mcp-scraper 0.36.0 → 0.37.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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/api-server.cjs +766 -168
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  6. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  7. package/dist/bin/mcp-scraper-cli.js +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-install.js +1 -1
  11. package/dist/bin/mcp-stdio-server.cjs +52 -8
  12. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  13. package/dist/bin/mcp-stdio-server.js +5 -5
  14. package/dist/bin/paa-harvest.cjs.map +1 -1
  15. package/dist/bin/paa-harvest.js +3 -3
  16. package/dist/chunk-3LWYPAU5.js +7 -0
  17. package/dist/chunk-3LWYPAU5.js.map +1 -0
  18. package/dist/{chunk-IFYER7O4.js → chunk-CSCD2HNS.js} +132 -2
  19. package/dist/chunk-CSCD2HNS.js.map +1 -0
  20. package/dist/{chunk-D7LM5QZN.js → chunk-FSAXLDB3.js} +2 -2
  21. package/dist/{chunk-5RULXBJ7.js → chunk-JK2FRDAP.js} +55 -11
  22. package/dist/{chunk-5RULXBJ7.js.map → chunk-JK2FRDAP.js.map} +1 -1
  23. package/dist/{chunk-ANCGXUQJ.js → chunk-LOPKN3YL.js} +2 -2
  24. package/dist/{chunk-E5UEELA7.js → chunk-MA5JBAUZ.js} +2 -2
  25. package/dist/{chunk-AN3VQARU.js → chunk-PUJFYJXB.js} +2 -2
  26. package/dist/{chunk-4ZIJ3BKZ.js → chunk-PWPUKR5U.js} +4 -2
  27. package/dist/{chunk-4ZIJ3BKZ.js.map → chunk-PWPUKR5U.js.map} +1 -1
  28. package/dist/{chunk-O2MCWFXQ.js → chunk-Q35WZJJK.js} +3 -3
  29. package/dist/{db-N6MPVMEF.js → db-N3YECFWR.js} +6 -2
  30. package/dist/{extract-bundle-M4SDJG3V.js → extract-bundle-346R6MXD.js} +4 -4
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.js +3 -3
  33. package/dist/{location-data-repository-Z4NQOU5Y.js → location-data-repository-O2VII3ON.js} +3 -3
  34. package/dist/{server-ZIGAFKOL.js → server-QKDDEBVQ.js} +500 -99
  35. package/dist/server-QKDDEBVQ.js.map +1 -0
  36. package/dist/{site-extract-repository-PGQZNW6V.js → site-extract-repository-I3VM6WXN.js} +4 -4
  37. package/dist/{worker-EBB6CTGW.js → worker-TTFXPPDK.js} +6 -6
  38. package/docs/mcp-tool-manifest.generated.json +182 -6
  39. package/package.json +2 -2
  40. package/dist/chunk-4HO66323.js +0 -7
  41. package/dist/chunk-4HO66323.js.map +0 -1
  42. package/dist/chunk-IFYER7O4.js.map +0 -1
  43. package/dist/server-ZIGAFKOL.js.map +0 -1
  44. /package/dist/{chunk-D7LM5QZN.js.map → chunk-FSAXLDB3.js.map} +0 -0
  45. /package/dist/{chunk-ANCGXUQJ.js.map → chunk-LOPKN3YL.js.map} +0 -0
  46. /package/dist/{chunk-E5UEELA7.js.map → chunk-MA5JBAUZ.js.map} +0 -0
  47. /package/dist/{chunk-AN3VQARU.js.map → chunk-PUJFYJXB.js.map} +0 -0
  48. /package/dist/{chunk-O2MCWFXQ.js.map → chunk-Q35WZJJK.js.map} +0 -0
  49. /package/dist/{db-N6MPVMEF.js.map → db-N3YECFWR.js.map} +0 -0
  50. /package/dist/{extract-bundle-M4SDJG3V.js.map → extract-bundle-346R6MXD.js.map} +0 -0
  51. /package/dist/{location-data-repository-Z4NQOU5Y.js.map → location-data-repository-O2VII3ON.js.map} +0 -0
  52. /package/dist/{site-extract-repository-PGQZNW6V.js.map → site-extract-repository-I3VM6WXN.js.map} +0 -0
  53. /package/dist/{worker-EBB6CTGW.js.map → worker-TTFXPPDK.js.map} +0 -0
@@ -3601,6 +3601,7 @@ var init_url_utils = __esm({
3601
3601
  // src/api/db.ts
3602
3602
  var db_exports = {};
3603
3603
  __export(db_exports, {
3604
+ CORE_SCHEMA_VERSION: () => CORE_SCHEMA_VERSION,
3604
3605
  CREDIT_LOT_TTL: () => CREDIT_LOT_TTL,
3605
3606
  SiteAuditJobRowSchema: () => SiteAuditJobRowSchema,
3606
3607
  SiteAuditPhaseLogRowSchema: () => SiteAuditPhaseLogRowSchema,
@@ -3626,6 +3627,7 @@ __export(db_exports, {
3626
3627
  createWorkflowRun: () => createWorkflowRun,
3627
3628
  createWorkflowSchedule: () => createWorkflowSchedule,
3628
3629
  creditMc: () => creditMc,
3630
+ creditMcIdempotent: () => creditMcIdempotent,
3629
3631
  deactivateUser: () => deactivateUser,
3630
3632
  debitMc: () => debitMc,
3631
3633
  debitMcIdempotent: () => debitMcIdempotent,
@@ -4081,6 +4083,50 @@ async function migrate() {
4081
4083
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
4082
4084
  )
4083
4085
  `);
4086
+ await db.execute(`
4087
+ CREATE TABLE IF NOT EXISTS billing_credits (
4088
+ idempotency_key TEXT PRIMARY KEY,
4089
+ user_id INTEGER NOT NULL REFERENCES users(id),
4090
+ amount_mc INTEGER NOT NULL,
4091
+ operation TEXT NOT NULL,
4092
+ description TEXT,
4093
+ status TEXT NOT NULL DEFAULT 'claimed',
4094
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
4095
+ applied_at TEXT
4096
+ )
4097
+ `);
4098
+ await db.execute(`CREATE INDEX IF NOT EXISTS billing_credits_user_status ON billing_credits(user_id, status)`);
4099
+ await db.execute(`
4100
+ CREATE TABLE IF NOT EXISTS admin_credit_adjustments (
4101
+ reference TEXT PRIMARY KEY,
4102
+ user_id INTEGER NOT NULL REFERENCES users(id),
4103
+ direction TEXT NOT NULL,
4104
+ amount_mc INTEGER NOT NULL,
4105
+ reason TEXT NOT NULL,
4106
+ actor TEXT NOT NULL,
4107
+ status TEXT NOT NULL DEFAULT 'applied',
4108
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
4109
+ updated_at TEXT
4110
+ )
4111
+ `);
4112
+ await db.execute(`CREATE INDEX IF NOT EXISTS admin_credit_adjustments_user_created ON admin_credit_adjustments(user_id, created_at)`);
4113
+ await db.execute(`
4114
+ CREATE TABLE IF NOT EXISTS admin_credit_adjustment_events (
4115
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4116
+ reference TEXT NOT NULL REFERENCES admin_credit_adjustments(reference),
4117
+ seq INTEGER NOT NULL,
4118
+ kind TEXT NOT NULL,
4119
+ from_amount_mc INTEGER,
4120
+ to_amount_mc INTEGER,
4121
+ from_reason TEXT,
4122
+ to_reason TEXT,
4123
+ moved_mc INTEGER NOT NULL DEFAULT 0,
4124
+ actor TEXT NOT NULL,
4125
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
4126
+ UNIQUE (reference, seq)
4127
+ )
4128
+ `);
4129
+ await db.execute(`CREATE INDEX IF NOT EXISTS admin_credit_adjustment_events_reference ON admin_credit_adjustment_events(reference)`);
4084
4130
  await db.execute(`
4085
4131
  CREATE TABLE IF NOT EXISTS site_audit_jobs (
4086
4132
  id TEXT PRIMARY KEY,
@@ -5592,6 +5638,84 @@ async function creditMc(userId, mc, operation, description, stripePaymentIntent,
5592
5638
  const res = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
5593
5639
  return Number(res.rows[0]?.balance_mc ?? 0);
5594
5640
  }
5641
+ async function creditMcIdempotent(userId, mc, operation, description, idempotencyKey2, options) {
5642
+ if (!Number.isSafeInteger(mc) || mc <= 0) throw new Error("idempotent credit amount must be a positive integer");
5643
+ if (!idempotencyKey2 || idempotencyKey2.length > 500) throw new Error("idempotent credit key is required and must be bounded");
5644
+ const db = getDb();
5645
+ await ensureMigrated(userId);
5646
+ let neverExpires = options?.neverExpires;
5647
+ if (neverExpires === void 0) {
5648
+ const tier = await db.execute({ sql: "SELECT subscription_tier FROM users WHERE id = ? LIMIT 1", args: [userId] });
5649
+ neverExpires = String(tier.rows[0]?.subscription_tier ?? "").startsWith("sonic");
5650
+ }
5651
+ const expiry = neverExpires ? "9999-12-31 23:59:59" : null;
5652
+ const readBalance = async () => {
5653
+ const res = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
5654
+ return Number(res.rows[0]?.balance_mc ?? 0);
5655
+ };
5656
+ let results;
5657
+ try {
5658
+ results = await db.batch([
5659
+ {
5660
+ sql: "INSERT OR IGNORE INTO billing_credits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')",
5661
+ args: [idempotencyKey2, userId, mc, operation, description]
5662
+ },
5663
+ {
5664
+ sql: "UPDATE billing_credits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed'",
5665
+ args: [idempotencyKey2, userId, mc, operation, description]
5666
+ },
5667
+ {
5668
+ sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)
5669
+ SELECT user_id, amount_mc, amount_mc, operation, NULL, COALESCE(?, datetime('now', ?)), ?
5670
+ FROM billing_credits
5671
+ WHERE idempotency_key = ? AND status = 'applying'`,
5672
+ args: [expiry, CREDIT_LOT_TTL, neverExpires ? 1 : 0, idempotencyKey2]
5673
+ },
5674
+ {
5675
+ sql: `UPDATE users
5676
+ SET balance_mc = balance_mc + COALESCE((
5677
+ SELECT amount_mc FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'
5678
+ ), 0)
5679
+ WHERE id = ?`,
5680
+ args: [idempotencyKey2, userId]
5681
+ },
5682
+ {
5683
+ sql: "INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'",
5684
+ args: [idempotencyKey2]
5685
+ },
5686
+ {
5687
+ sql: "UPDATE billing_credits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'",
5688
+ args: [idempotencyKey2]
5689
+ }
5690
+ ], "write");
5691
+ } catch (error) {
5692
+ try {
5693
+ const recovered = await db.execute({
5694
+ sql: "SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1",
5695
+ args: [idempotencyKey2]
5696
+ });
5697
+ const row2 = recovered.rows[0];
5698
+ if (row2 && Number(row2.user_id) === userId && Number(row2.amount_mc) === mc && String(row2.operation) === operation && String(row2.description) === description) {
5699
+ return { ok: true, balance_mc: await readBalance(), duplicate: true };
5700
+ }
5701
+ } catch {
5702
+ }
5703
+ throw error;
5704
+ }
5705
+ const claim = await db.execute({
5706
+ sql: "SELECT user_id, amount_mc, operation, description, status FROM billing_credits WHERE idempotency_key = ? LIMIT 1",
5707
+ args: [idempotencyKey2]
5708
+ });
5709
+ const row = claim.rows[0];
5710
+ if (!row || Number(row.user_id) !== userId || Number(row.amount_mc) !== mc || String(row.operation) !== operation || String(row.description) !== description) {
5711
+ throw new Error("idempotent credit key conflicts with another credit");
5712
+ }
5713
+ return {
5714
+ ok: String(row.status) === "applied",
5715
+ balance_mc: await readBalance(),
5716
+ duplicate: results[1]?.rowsAffected === 0 && String(row.status) === "applied"
5717
+ };
5718
+ }
5595
5719
  async function ledgerExistsForOperation(userId, operation) {
5596
5720
  const res = await getDb().execute({
5597
5721
  sql: "SELECT 1 FROM ledger WHERE user_id = ? AND operation = ? LIMIT 1",
@@ -6138,9 +6262,12 @@ var init_db = __esm({
6138
6262
  DB_TOKEN = process.env.TURSO_AUTH_TOKEN;
6139
6263
  _db = null;
6140
6264
  _rateLimitSchemaReady = false;
6141
- CORE_SCHEMA_VERSION = "2026-07-24.2";
6265
+ CORE_SCHEMA_VERSION = "2026-07-28.1";
6142
6266
  CORE_SCHEMA_TABLES = [
6267
+ "admin_credit_adjustment_events",
6268
+ "admin_credit_adjustments",
6143
6269
  "billing_authorizations",
6270
+ "billing_credits",
6144
6271
  "billing_debits",
6145
6272
  "billing_debit_allocations",
6146
6273
  "billing_events",
@@ -6174,7 +6301,10 @@ var init_db = __esm({
6174
6301
  "workflow_webhook_deliveries"
6175
6302
  ];
6176
6303
  CORE_SCHEMA_INDEXES = [
6304
+ "admin_credit_adjustment_events_reference",
6305
+ "admin_credit_adjustments_user_created",
6177
6306
  "billing_authorizations_user_status",
6307
+ "billing_credits_user_status",
6178
6308
  "billing_debits_user_status",
6179
6309
  "billing_events_authorization",
6180
6310
  "billing_events_user_created_at",
@@ -11196,7 +11326,9 @@ var init_rates = __esm({
11196
11326
  SITE_AUDIT_LLM: "site_audit_llm",
11197
11327
  SCHEDULED_RUN: "scheduled_run",
11198
11328
  CONNECTED_USAGE: "connected_usage",
11199
- CONNECTED_ACCOUNT_FEE: "connected_account_fee"
11329
+ CONNECTED_ACCOUNT_FEE: "connected_account_fee",
11330
+ ADMIN_GRANT: "admin_grant",
11331
+ ADMIN_REVOKE: "admin_revoke"
11200
11332
  };
11201
11333
  MEMORY_AI_MARGIN_MULTIPLE = 3;
11202
11334
  SITE_AUDIT_LLM_MARGIN_MULTIPLE = 1.5;
@@ -28017,6 +28149,427 @@ var init_google_ads_routes = __esm({
28017
28149
  }
28018
28150
  });
28019
28151
 
28152
+ // src/api/admin-auth.ts
28153
+ var import_factory5, adminAuth;
28154
+ var init_admin_auth = __esm({
28155
+ "src/api/admin-auth.ts"() {
28156
+ "use strict";
28157
+ import_factory5 = require("hono/factory");
28158
+ adminAuth = (0, import_factory5.createMiddleware)(async (c, next) => {
28159
+ const configured = process.env.ADMIN_KEY?.trim();
28160
+ const key = c.req.header("x-admin-key")?.trim();
28161
+ if (!configured) return c.json({ error: "Admin access disabled" }, 503);
28162
+ if (!key || key !== configured) return c.json({ error: "Unauthorized" }, 401);
28163
+ return next();
28164
+ });
28165
+ }
28166
+ });
28167
+
28168
+ // src/api/admin-credits.ts
28169
+ function toCredits(mc) {
28170
+ return mc / MC_PER_CREDIT;
28171
+ }
28172
+ function creditsToMc(credits) {
28173
+ return Math.round(credits * MC_PER_CREDIT);
28174
+ }
28175
+ function validateCredits(credits, confirmLarge) {
28176
+ if (typeof credits !== "number" || !Number.isFinite(credits) || !Number.isInteger(credits)) {
28177
+ throw new AdminCreditError(400, "credits must be a whole number");
28178
+ }
28179
+ if (credits < MIN_ADJUSTMENT_CREDITS) {
28180
+ throw new AdminCreditError(400, `credits must be at least ${MIN_ADJUSTMENT_CREDITS}`);
28181
+ }
28182
+ if (credits > MAX_ADJUSTMENT_CREDITS) {
28183
+ throw new AdminCreditError(400, `credits exceeds the hard ceiling of ${MAX_ADJUSTMENT_CREDITS.toLocaleString()}`);
28184
+ }
28185
+ if (credits > LARGE_ADJUSTMENT_CREDITS && !confirmLarge) {
28186
+ throw new AdminCreditError(422, `adjustments over ${LARGE_ADJUSTMENT_CREDITS.toLocaleString()} credits require confirmLarge: true`, {
28187
+ credits,
28188
+ threshold: LARGE_ADJUSTMENT_CREDITS
28189
+ });
28190
+ }
28191
+ return credits;
28192
+ }
28193
+ function validateReason(reason) {
28194
+ const trimmed = typeof reason === "string" ? reason.trim() : "";
28195
+ if (trimmed.length < MIN_REASON_LENGTH) {
28196
+ throw new AdminCreditError(400, `reason must be at least ${MIN_REASON_LENGTH} characters`);
28197
+ }
28198
+ if (trimmed.length > MAX_REASON_LENGTH) {
28199
+ throw new AdminCreditError(400, `reason must be at most ${MAX_REASON_LENGTH} characters`);
28200
+ }
28201
+ return trimmed;
28202
+ }
28203
+ function validateActor(actor) {
28204
+ const trimmed = typeof actor === "string" ? actor.trim() : "";
28205
+ if (!trimmed) throw new AdminCreditError(400, "actor is required so the adjustment records who made it");
28206
+ if (trimmed.length > MAX_ACTOR_LENGTH) {
28207
+ throw new AdminCreditError(400, `actor must be at most ${MAX_ACTOR_LENGTH} characters`);
28208
+ }
28209
+ return trimmed;
28210
+ }
28211
+ function validateDirection(direction) {
28212
+ if (direction !== "grant" && direction !== "revoke") {
28213
+ throw new AdminCreditError(400, "direction must be either 'grant' or 'revoke'");
28214
+ }
28215
+ return direction;
28216
+ }
28217
+ async function resolveUser2(target) {
28218
+ const email = target.email?.trim().toLowerCase();
28219
+ if (!email && target.userId == null) {
28220
+ throw new AdminCreditError(400, "Provide either email or userId");
28221
+ }
28222
+ const user = email ? await getUserByEmail(email) : await getUserById(target.userId);
28223
+ if (user) return user;
28224
+ const probe = await getDb().execute(
28225
+ email ? { sql: "SELECT active FROM users WHERE email = ? LIMIT 1", args: [email] } : { sql: "SELECT active FROM users WHERE id = ? LIMIT 1", args: [target.userId] }
28226
+ );
28227
+ if (probe.rows.length > 0) {
28228
+ throw new AdminCreditError(409, "Account is deactivated; reactivate it before adjusting credits");
28229
+ }
28230
+ throw new AdminCreditError(404, "No account found for that email or id");
28231
+ }
28232
+ async function moveCredits(userId, direction, mc, reason, key) {
28233
+ if (direction === "grant") {
28234
+ const res2 = await creditMcIdempotent(userId, mc, LedgerOperation.ADMIN_GRANT, reason, key);
28235
+ if (!res2.ok) throw new AdminCreditError(409, "Grant could not be applied");
28236
+ return;
28237
+ }
28238
+ const res = await debitMcIdempotent(userId, mc, LedgerOperation.ADMIN_REVOKE, reason, key);
28239
+ if (!res.ok) {
28240
+ const balance = await reconcileBalanceMc(userId);
28241
+ throw new AdminCreditError(409, "Account does not have enough credits for this revoke", {
28242
+ requested_credits: toCredits(mc),
28243
+ available_credits: toCredits(balance)
28244
+ });
28245
+ }
28246
+ }
28247
+ function flip(direction) {
28248
+ return direction === "grant" ? "revoke" : "grant";
28249
+ }
28250
+ async function loadAdjustment(reference) {
28251
+ const db = getDb();
28252
+ const res = await db.execute({
28253
+ sql: `SELECT a.reference, a.user_id, a.direction, a.amount_mc, a.reason, a.actor, a.status, a.created_at, a.updated_at, u.email
28254
+ FROM admin_credit_adjustments a JOIN users u ON u.id = a.user_id
28255
+ WHERE a.reference = ? LIMIT 1`,
28256
+ args: [reference]
28257
+ });
28258
+ const row = res.rows[0];
28259
+ if (!row) throw new AdminCreditError(404, `No adjustment found with reference ${reference}`);
28260
+ const events = await db.execute({
28261
+ sql: `SELECT seq, kind, from_amount_mc, to_amount_mc, from_reason, to_reason, moved_mc, actor, created_at
28262
+ FROM admin_credit_adjustment_events WHERE reference = ? ORDER BY seq ASC`,
28263
+ args: [reference]
28264
+ });
28265
+ return {
28266
+ reference: String(row.reference),
28267
+ user_id: Number(row.user_id),
28268
+ user_email: String(row.email),
28269
+ direction: String(row.direction),
28270
+ credits: toCredits(Number(row.amount_mc)),
28271
+ amount_mc: Number(row.amount_mc),
28272
+ reason: String(row.reason),
28273
+ actor: String(row.actor),
28274
+ status: String(row.status),
28275
+ created_at: String(row.created_at),
28276
+ updated_at: row.updated_at == null ? null : String(row.updated_at),
28277
+ history: events.rows.map((e) => ({
28278
+ seq: Number(e.seq),
28279
+ kind: String(e.kind),
28280
+ from_credits: e.from_amount_mc == null ? null : toCredits(Number(e.from_amount_mc)),
28281
+ to_credits: e.to_amount_mc == null ? null : toCredits(Number(e.to_amount_mc)),
28282
+ from_reason: e.from_reason == null ? null : String(e.from_reason),
28283
+ to_reason: e.to_reason == null ? null : String(e.to_reason),
28284
+ moved_credits: toCredits(Number(e.moved_mc ?? 0)),
28285
+ actor: String(e.actor),
28286
+ created_at: String(e.created_at)
28287
+ }))
28288
+ };
28289
+ }
28290
+ async function nextSeq(reference) {
28291
+ const res = await getDb().execute({
28292
+ sql: "SELECT COALESCE(MAX(seq), 0) AS max_seq FROM admin_credit_adjustment_events WHERE reference = ?",
28293
+ args: [reference]
28294
+ });
28295
+ return Number(res.rows[0]?.max_seq ?? 0) + 1;
28296
+ }
28297
+ async function applyAdjustment(input) {
28298
+ const direction = validateDirection(input.direction);
28299
+ const credits = validateCredits(input.credits, input.confirmLarge === true);
28300
+ const reason = validateReason(input.reason);
28301
+ const actor = validateActor(input.actor);
28302
+ const reference = input.reference?.trim() || `adj_${(0, import_node_crypto15.randomUUID)()}`;
28303
+ if (reference.length > 120) throw new AdminCreditError(400, "reference must be at most 120 characters");
28304
+ const db = getDb();
28305
+ const existing = await db.execute({
28306
+ sql: "SELECT reference, user_id, direction, amount_mc FROM admin_credit_adjustments WHERE reference = ? LIMIT 1",
28307
+ args: [reference]
28308
+ });
28309
+ if (existing.rows.length > 0) {
28310
+ const prior = existing.rows[0];
28311
+ if (String(prior.direction) !== direction || Number(prior.amount_mc) !== creditsToMc(credits)) {
28312
+ throw new AdminCreditError(409, "reference already exists with different values; use the edit endpoint to change it");
28313
+ }
28314
+ return loadAdjustment(reference);
28315
+ }
28316
+ const user = await resolveUser2({ email: input.email, userId: input.userId });
28317
+ const mc = creditsToMc(credits);
28318
+ await moveCredits(user.id, direction, mc, reason, `${reference}:1`);
28319
+ await db.batch([
28320
+ {
28321
+ sql: `INSERT OR IGNORE INTO admin_credit_adjustments (reference, user_id, direction, amount_mc, reason, actor, status)
28322
+ VALUES (?, ?, ?, ?, ?, ?, 'applied')`,
28323
+ args: [reference, user.id, direction, mc, reason, actor]
28324
+ },
28325
+ {
28326
+ sql: `INSERT OR IGNORE INTO admin_credit_adjustment_events (reference, seq, kind, from_amount_mc, to_amount_mc, from_reason, to_reason, moved_mc, actor)
28327
+ VALUES (?, 1, 'created', NULL, ?, NULL, ?, ?, ?)`,
28328
+ args: [reference, mc, reason, mc, actor]
28329
+ }
28330
+ ], "write");
28331
+ return loadAdjustment(reference);
28332
+ }
28333
+ async function editAdjustment(input) {
28334
+ const actor = validateActor(input.actor);
28335
+ const current = await loadAdjustment(input.reference);
28336
+ if (current.status === "reversed") {
28337
+ throw new AdminCreditError(409, "This adjustment was reversed and can no longer be edited");
28338
+ }
28339
+ if (input.credits === void 0 && input.reason === void 0) {
28340
+ throw new AdminCreditError(400, "Provide credits, reason, or both");
28341
+ }
28342
+ const db = getDb();
28343
+ if (input.reason !== void 0) {
28344
+ const reason = validateReason(input.reason);
28345
+ if (reason !== current.reason) {
28346
+ const seq = await nextSeq(current.reference);
28347
+ await db.batch([
28348
+ {
28349
+ sql: "UPDATE admin_credit_adjustments SET reason = ?, updated_at = datetime('now') WHERE reference = ?",
28350
+ args: [reason, current.reference]
28351
+ },
28352
+ {
28353
+ sql: `INSERT INTO admin_credit_adjustment_events (reference, seq, kind, from_reason, to_reason, moved_mc, actor)
28354
+ VALUES (?, ?, 'reason_edited', ?, ?, 0, ?)`,
28355
+ args: [current.reference, seq, current.reason, reason, actor]
28356
+ }
28357
+ ], "write");
28358
+ }
28359
+ }
28360
+ if (input.credits !== void 0) {
28361
+ const credits = validateCredits(input.credits, input.confirmLarge === true);
28362
+ const targetMc = creditsToMc(credits);
28363
+ if (targetMc !== current.amount_mc) {
28364
+ const deltaMc = targetMc - current.amount_mc;
28365
+ const moveDirection = deltaMc > 0 ? current.direction : flip(current.direction);
28366
+ const moveMc = Math.abs(deltaMc);
28367
+ const seq = await nextSeq(current.reference);
28368
+ const reason = input.reason !== void 0 ? validateReason(input.reason) : current.reason;
28369
+ await moveCredits(current.user_id, moveDirection, moveMc, `edit ${current.reference}: ${reason}`, `${current.reference}:${seq}`);
28370
+ await db.batch([
28371
+ {
28372
+ sql: "UPDATE admin_credit_adjustments SET amount_mc = ?, updated_at = datetime('now') WHERE reference = ?",
28373
+ args: [targetMc, current.reference]
28374
+ },
28375
+ {
28376
+ sql: `INSERT INTO admin_credit_adjustment_events (reference, seq, kind, from_amount_mc, to_amount_mc, moved_mc, actor)
28377
+ VALUES (?, ?, 'amount_edited', ?, ?, ?, ?)`,
28378
+ args: [current.reference, seq, current.amount_mc, targetMc, moveMc, actor]
28379
+ }
28380
+ ], "write");
28381
+ }
28382
+ }
28383
+ return loadAdjustment(input.reference);
28384
+ }
28385
+ async function reverseAdjustment(reference, actor) {
28386
+ const validActor = validateActor(actor);
28387
+ const current = await loadAdjustment(reference);
28388
+ if (current.status === "reversed") return current;
28389
+ const seq = await nextSeq(reference);
28390
+ if (current.amount_mc > 0) {
28391
+ await moveCredits(current.user_id, flip(current.direction), current.amount_mc, `reverse ${reference}: ${current.reason}`, `${reference}:${seq}`);
28392
+ }
28393
+ await getDb().batch([
28394
+ {
28395
+ sql: "UPDATE admin_credit_adjustments SET status = 'reversed', updated_at = datetime('now') WHERE reference = ?",
28396
+ args: [reference]
28397
+ },
28398
+ {
28399
+ sql: `INSERT INTO admin_credit_adjustment_events (reference, seq, kind, from_amount_mc, to_amount_mc, moved_mc, actor)
28400
+ VALUES (?, ?, 'reversed', ?, 0, ?, ?)`,
28401
+ args: [reference, seq, current.amount_mc, current.amount_mc, validActor]
28402
+ }
28403
+ ], "write");
28404
+ return loadAdjustment(reference);
28405
+ }
28406
+ async function lookupAccount(target, limit = 20) {
28407
+ const user = await resolveUser2(target);
28408
+ const db = getDb();
28409
+ const balanceMc = await reconcileBalanceMc(user.id, user.balance_mc);
28410
+ const lots = await db.execute({
28411
+ sql: `SELECT COUNT(*) AS n, COALESCE(SUM(remaining_mc), 0) AS mc, MIN(expires_at) AS next_expiry
28412
+ FROM credit_lots
28413
+ WHERE user_id = ? AND remaining_mc > 0 AND (never_expires = 1 OR expires_at > datetime('now'))`,
28414
+ args: [user.id]
28415
+ });
28416
+ const refs = await db.execute({
28417
+ sql: "SELECT reference FROM admin_credit_adjustments WHERE user_id = ? ORDER BY created_at DESC, rowid DESC LIMIT ?",
28418
+ args: [user.id, limit]
28419
+ });
28420
+ const adjustments = await Promise.all(refs.rows.map((r) => loadAdjustment(String(r.reference))));
28421
+ const ledger = await db.execute({
28422
+ sql: "SELECT created_at, operation, amount_mc, description FROM ledger WHERE user_id = ? ORDER BY id DESC LIMIT ?",
28423
+ args: [user.id, limit]
28424
+ });
28425
+ return {
28426
+ user: {
28427
+ id: user.id,
28428
+ email: user.email,
28429
+ active: Boolean(user.active),
28430
+ balance_credits: toCredits(balanceMc),
28431
+ balance_mc: balanceMc
28432
+ },
28433
+ lots: {
28434
+ active_lots: Number(lots.rows[0]?.n ?? 0),
28435
+ active_credits: toCredits(Number(lots.rows[0]?.mc ?? 0)),
28436
+ next_expiry: lots.rows[0]?.next_expiry == null ? null : String(lots.rows[0].next_expiry)
28437
+ },
28438
+ adjustments,
28439
+ recent_ledger: ledger.rows.map((r) => ({
28440
+ created_at: String(r.created_at),
28441
+ operation: String(r.operation),
28442
+ credits: toCredits(Number(r.amount_mc)),
28443
+ description: r.description == null ? null : String(r.description)
28444
+ }))
28445
+ };
28446
+ }
28447
+ var import_node_crypto15, MIN_ADJUSTMENT_CREDITS, LARGE_ADJUSTMENT_CREDITS, MAX_ADJUSTMENT_CREDITS, MIN_REASON_LENGTH, MAX_REASON_LENGTH, MAX_ACTOR_LENGTH, AdminCreditError;
28448
+ var init_admin_credits = __esm({
28449
+ "src/api/admin-credits.ts"() {
28450
+ "use strict";
28451
+ import_node_crypto15 = require("crypto");
28452
+ init_db();
28453
+ init_rates();
28454
+ MIN_ADJUSTMENT_CREDITS = 1;
28455
+ LARGE_ADJUSTMENT_CREDITS = 1e5;
28456
+ MAX_ADJUSTMENT_CREDITS = 1e7;
28457
+ MIN_REASON_LENGTH = 3;
28458
+ MAX_REASON_LENGTH = 500;
28459
+ MAX_ACTOR_LENGTH = 120;
28460
+ AdminCreditError = class extends Error {
28461
+ constructor(status, message, detail) {
28462
+ super(message);
28463
+ this.status = status;
28464
+ this.detail = detail;
28465
+ this.name = "AdminCreditError";
28466
+ }
28467
+ status;
28468
+ detail;
28469
+ };
28470
+ }
28471
+ });
28472
+
28473
+ // src/api/admin-credit-routes.ts
28474
+ function fail(c, err) {
28475
+ if (err instanceof AdminCreditError) {
28476
+ return c.json({ error: err.message, ...err.detail ?? {} }, err.status);
28477
+ }
28478
+ const message = err instanceof Error ? err.message : String(err);
28479
+ if (message.includes("conflicts with another")) {
28480
+ return c.json({ error: "A concurrent adjustment used the same key; re-read the account and retry" }, 409);
28481
+ }
28482
+ console.error("[admin/credits] unexpected failure:", message);
28483
+ return c.json({ error: "Adjustment failed" }, 500);
28484
+ }
28485
+ async function readBody(c) {
28486
+ try {
28487
+ const parsed = await c.req.json();
28488
+ return parsed && typeof parsed === "object" ? parsed : {};
28489
+ } catch {
28490
+ return {};
28491
+ }
28492
+ }
28493
+ function targetFrom(query) {
28494
+ const email = query.email?.trim();
28495
+ const idRaw = query.userId?.trim();
28496
+ const userId = idRaw ? Number(idRaw) : void 0;
28497
+ if (userId !== void 0 && !Number.isInteger(userId)) {
28498
+ throw new AdminCreditError(400, "userId must be an integer");
28499
+ }
28500
+ return { email: email || void 0, userId };
28501
+ }
28502
+ var import_hono9, adminCreditsApp;
28503
+ var init_admin_credit_routes = __esm({
28504
+ "src/api/admin-credit-routes.ts"() {
28505
+ "use strict";
28506
+ import_hono9 = require("hono");
28507
+ init_admin_auth();
28508
+ init_admin_credits();
28509
+ adminCreditsApp = new import_hono9.Hono();
28510
+ adminCreditsApp.use("*", adminAuth);
28511
+ adminCreditsApp.post("/adjust", async (c) => {
28512
+ try {
28513
+ const body = await readBody(c);
28514
+ const record = await applyAdjustment({
28515
+ email: typeof body.email === "string" ? body.email : void 0,
28516
+ userId: typeof body.userId === "number" ? body.userId : void 0,
28517
+ direction: body.direction,
28518
+ credits: body.credits,
28519
+ reason: body.reason,
28520
+ actor: body.actor,
28521
+ reference: typeof body.reference === "string" ? body.reference : void 0,
28522
+ confirmLarge: body.confirmLarge === true
28523
+ });
28524
+ return c.json({ ok: true, adjustment: record });
28525
+ } catch (err) {
28526
+ return fail(c, err);
28527
+ }
28528
+ });
28529
+ adminCreditsApp.post("/adjustments/:reference/edit", async (c) => {
28530
+ try {
28531
+ const body = await readBody(c);
28532
+ const record = await editAdjustment({
28533
+ reference: c.req.param("reference"),
28534
+ credits: typeof body.credits === "number" ? body.credits : void 0,
28535
+ reason: typeof body.reason === "string" ? body.reason : void 0,
28536
+ actor: body.actor,
28537
+ confirmLarge: body.confirmLarge === true
28538
+ });
28539
+ return c.json({ ok: true, adjustment: record });
28540
+ } catch (err) {
28541
+ return fail(c, err);
28542
+ }
28543
+ });
28544
+ adminCreditsApp.post("/adjustments/:reference/reverse", async (c) => {
28545
+ try {
28546
+ const body = await readBody(c);
28547
+ const record = await reverseAdjustment(c.req.param("reference"), body.actor);
28548
+ return c.json({ ok: true, adjustment: record });
28549
+ } catch (err) {
28550
+ return fail(c, err);
28551
+ }
28552
+ });
28553
+ adminCreditsApp.get("/adjustments/:reference", async (c) => {
28554
+ try {
28555
+ return c.json({ ok: true, adjustment: await loadAdjustment(c.req.param("reference")) });
28556
+ } catch (err) {
28557
+ return fail(c, err);
28558
+ }
28559
+ });
28560
+ adminCreditsApp.get("/lookup", async (c) => {
28561
+ try {
28562
+ const target = targetFrom(c.req.query());
28563
+ const limitRaw = c.req.query("limit");
28564
+ const limit = limitRaw ? Math.min(Math.max(Number(limitRaw), 1), 100) : 20;
28565
+ return c.json({ ok: true, ...await lookupAccount(target, limit) });
28566
+ } catch (err) {
28567
+ return fail(c, err);
28568
+ }
28569
+ });
28570
+ }
28571
+ });
28572
+
28020
28573
  // src/extractor/InstagramContentExtractor.ts
28021
28574
  function htmlDecode2(value) {
28022
28575
  return value.replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">");
@@ -28470,11 +29023,11 @@ function trackFilename(track, index, shortcode) {
28470
29023
  const bitrate = track.bitrate ? `-${track.bitrate}` : "";
28471
29024
  return `${label}-${type}-${index}${bitrate}.mp4`;
28472
29025
  }
28473
- var import_hono9, import_zod23, import_node_fs7, import_node_os8, import_node_path11, import_node_child_process2, InstagramProfileContentBodySchema, InstagramMediaTypeSchema, InstagramMediaDownloadBodySchema, instagramApp;
29026
+ var import_hono10, import_zod23, import_node_fs7, import_node_os8, import_node_path11, import_node_child_process2, InstagramProfileContentBodySchema, InstagramMediaTypeSchema, InstagramMediaDownloadBodySchema, instagramApp;
28474
29027
  var init_instagram_routes = __esm({
28475
29028
  "src/api/instagram-routes.ts"() {
28476
29029
  "use strict";
28477
- import_hono9 = require("hono");
29030
+ import_hono10 = require("hono");
28478
29031
  import_zod23 = require("zod");
28479
29032
  import_node_fs7 = require("fs");
28480
29033
  import_node_os8 = require("os");
@@ -28515,7 +29068,7 @@ var init_instagram_routes = __esm({
28515
29068
  includeTranscript: import_zod23.z.boolean().default(false),
28516
29069
  mux: import_zod23.z.boolean().default(true)
28517
29070
  });
28518
- instagramApp = new import_hono9.Hono();
29071
+ instagramApp = new import_hono10.Hono();
28519
29072
  instagramApp.post("/profile-content", createApiKeyAuth(), async (c) => {
28520
29073
  const raw = await c.req.json().catch(() => ({}));
28521
29074
  const parsed = InstagramProfileContentBodySchema.safeParse(raw);
@@ -30971,11 +31524,11 @@ async function scrapeOldRedditWithRetries(url, script, accept) {
30971
31524
  }
30972
31525
  return null;
30973
31526
  }
30974
- var import_hono10, import_zod24, RedditThreadBodySchema, RedditTrendingBodySchema, PARSE_REDDIT, redditApp, TRENDING_SOFT_DEADLINE_MS, TRENDING_SCRAPE_CONCURRENCY;
31527
+ var import_hono11, import_zod24, RedditThreadBodySchema, RedditTrendingBodySchema, PARSE_REDDIT, redditApp, TRENDING_SOFT_DEADLINE_MS, TRENDING_SCRAPE_CONCURRENCY;
30975
31528
  var init_reddit_routes = __esm({
30976
31529
  "src/api/reddit-routes.ts"() {
30977
31530
  "use strict";
30978
- import_hono10 = require("hono");
31531
+ import_hono11 = require("hono");
30979
31532
  import_zod24 = require("zod");
30980
31533
  init_backup_proxy();
30981
31534
  init_BrowserDriver();
@@ -31024,7 +31577,7 @@ var init_reddit_routes = __esm({
31024
31577
  });
31025
31578
  return { title: title, author: author, score: score, postBody: postBody, blocked: blocked, numComments: comments.length, comments: comments };
31026
31579
  })()`;
31027
- redditApp = new import_hono10.Hono();
31580
+ redditApp = new import_hono11.Hono();
31028
31581
  redditApp.post("/thread", createApiKeyAuth(), async (c) => {
31029
31582
  const raw = await c.req.json().catch(() => ({}));
31030
31583
  const parsed = RedditThreadBodySchema.safeParse(raw);
@@ -31233,11 +31786,11 @@ function invalidRequest5(message) {
31233
31786
  function tiersFor(frames) {
31234
31787
  return Math.ceil(Math.min(Math.max(frames, 1), 480) / 120);
31235
31788
  }
31236
- var import_hono11, import_zod25, videoApp, AnalyzeBodySchema;
31789
+ var import_hono12, import_zod25, videoApp, AnalyzeBodySchema;
31237
31790
  var init_video_routes = __esm({
31238
31791
  "src/api/video-routes.ts"() {
31239
31792
  "use strict";
31240
- import_hono11 = require("hono");
31793
+ import_hono12 = require("hono");
31241
31794
  import_zod25 = require("zod");
31242
31795
  init_db();
31243
31796
  init_rates();
@@ -31245,7 +31798,7 @@ var init_video_routes = __esm({
31245
31798
  init_memory();
31246
31799
  init_cost_telemetry();
31247
31800
  init_cost_context();
31248
- videoApp = new import_hono11.Hono();
31801
+ videoApp = new import_hono12.Hono();
31249
31802
  AnalyzeBodySchema = import_zod25.z.object({
31250
31803
  sourceUrl: import_zod25.z.string().trim().url("sourceUrl must be a direct video file URL"),
31251
31804
  intervalS: import_zod25.z.number().min(1).max(30).optional(),
@@ -32002,11 +32555,11 @@ function mapsErrorResponse(c, err, errorCode) {
32002
32555
  retryable
32003
32556
  }, retryable ? 503 : 500);
32004
32557
  }
32005
- var import_hono12, mapsApp;
32558
+ var import_hono13, mapsApp;
32006
32559
  var init_maps_routes = __esm({
32007
32560
  "src/api/maps-routes.ts"() {
32008
32561
  "use strict";
32009
- import_hono12 = require("hono");
32562
+ import_hono13 = require("hono");
32010
32563
  init_db();
32011
32564
  init_rates();
32012
32565
  init_MapsExtractor();
@@ -32017,7 +32570,7 @@ var init_maps_routes = __esm({
32017
32570
  init_browser_service_env();
32018
32571
  init_maps_search_rotation();
32019
32572
  init_concurrency_gates();
32020
- mapsApp = new import_hono12.Hono();
32573
+ mapsApp = new import_hono13.Hono();
32021
32574
  mapsApp.post("/search", createApiKeyAuth(), async (c) => {
32022
32575
  const user = c.get("user");
32023
32576
  const body = await c.req.json().catch(() => ({}));
@@ -32221,11 +32774,11 @@ async function extractReviewCards(page) {
32221
32774
  });
32222
32775
  });
32223
32776
  }
32224
- var import_hono13, import_zod27, TrustpilotReviewsBodySchema, trustpilotApp;
32777
+ var import_hono14, import_zod27, TrustpilotReviewsBodySchema, trustpilotApp;
32225
32778
  var init_trustpilot_routes = __esm({
32226
32779
  "src/api/trustpilot-routes.ts"() {
32227
32780
  "use strict";
32228
- import_hono13 = require("hono");
32781
+ import_hono14 = require("hono");
32229
32782
  import_zod27 = require("zod");
32230
32783
  init_BrowserDriver();
32231
32784
  init_browser_service_env();
@@ -32237,7 +32790,7 @@ var init_trustpilot_routes = __esm({
32237
32790
  domain: import_zod27.z.string().trim().min(1, "domain is required"),
32238
32791
  maxPages: import_zod27.z.number().int().min(1).max(50).default(5)
32239
32792
  });
32240
- trustpilotApp = new import_hono13.Hono();
32793
+ trustpilotApp = new import_hono14.Hono();
32241
32794
  trustpilotApp.post("/reviews", createApiKeyAuth(), async (c) => {
32242
32795
  const raw = await c.req.json().catch(() => ({}));
32243
32796
  const parsed = TrustpilotReviewsBodySchema.safeParse(raw);
@@ -32423,11 +32976,11 @@ async function extractG2ReviewCards(page) {
32423
32976
  });
32424
32977
  });
32425
32978
  }
32426
- var import_hono14, import_zod28, G2ReviewsBodySchema, g2App;
32979
+ var import_hono15, import_zod28, G2ReviewsBodySchema, g2App;
32427
32980
  var init_g2_routes = __esm({
32428
32981
  "src/api/g2-routes.ts"() {
32429
32982
  "use strict";
32430
- import_hono14 = require("hono");
32983
+ import_hono15 = require("hono");
32431
32984
  import_zod28 = require("zod");
32432
32985
  init_backup_proxy();
32433
32986
  init_BrowserDriver();
@@ -32441,7 +32994,7 @@ var init_g2_routes = __esm({
32441
32994
  product: import_zod28.z.string().trim().min(1, "product is required"),
32442
32995
  maxPages: import_zod28.z.number().int().min(1).max(50).default(5)
32443
32996
  });
32444
- g2App = new import_hono14.Hono();
32997
+ g2App = new import_hono15.Hono();
32445
32998
  g2App.post("/reviews", createApiKeyAuth(), async (c) => {
32446
32999
  const raw = await c.req.json().catch(() => ({}));
32447
33000
  const parsed = G2ReviewsBodySchema.safeParse(raw);
@@ -32549,7 +33102,7 @@ function retryDelaySeconds(attempts) {
32549
33102
  }
32550
33103
  async function dispatchPendingDirectoryWorkflows(limit = 25) {
32551
33104
  const rows = await claimDirectoryWorkflowOutbox({
32552
- workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`,
33105
+ workerId: `directory-dispatch-${process.pid}-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`,
32553
33106
  limit
32554
33107
  });
32555
33108
  const result = { claimed: rows.length, dispatched: 0, failed: 0 };
@@ -32571,11 +33124,11 @@ async function dispatchPendingDirectoryWorkflows(limit = 25) {
32571
33124
  }
32572
33125
  return result;
32573
33126
  }
32574
- var import_node_crypto15;
33127
+ var import_node_crypto16;
32575
33128
  var init_directory_workflow_dispatch = __esm({
32576
33129
  "src/api/directory-workflow-dispatch.ts"() {
32577
33130
  "use strict";
32578
- import_node_crypto15 = require("crypto");
33131
+ import_node_crypto16 = require("crypto");
32579
33132
  init_client();
32580
33133
  init_directory_workflow_repository();
32581
33134
  }
@@ -32587,7 +33140,7 @@ function safeOptions(options) {
32587
33140
  return safe2;
32588
33141
  }
32589
33142
  function requestFingerprint(options) {
32590
- return (0, import_node_crypto16.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
33143
+ return (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify(safeOptions(options))).digest("hex");
32591
33144
  }
32592
33145
  function idempotencyKey(raw) {
32593
33146
  if (raw !== void 0) {
@@ -32596,14 +33149,14 @@ function idempotencyKey(raw) {
32596
33149
  if (trimmed.length > 500) return { ok: false, message: "Idempotency-Key must be 500 characters or fewer." };
32597
33150
  return { ok: true, key: trimmed };
32598
33151
  }
32599
- return { ok: true, key: `directory-${(0, import_node_crypto16.randomUUID)()}` };
33152
+ return { ok: true, key: `directory-${(0, import_node_crypto17.randomUUID)()}` };
32600
33153
  }
32601
33154
  function debitKeyFor(userId, responseKey) {
32602
- const digest = (0, import_node_crypto16.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
33155
+ const digest = (0, import_node_crypto17.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
32603
33156
  return `directory-workflow:${userId}:${digest}`;
32604
33157
  }
32605
33158
  function jobId() {
32606
- return `dir_${(0, import_node_crypto16.randomUUID)().replace(/-/g, "")}`;
33159
+ return `dir_${(0, import_node_crypto17.randomUUID)().replace(/-/g, "")}`;
32607
33160
  }
32608
33161
  function publicStatus(job, result) {
32609
33162
  if (job.status === "failed") return "failed";
@@ -32712,7 +33265,7 @@ async function runSynchronously(c, user, options, plan, responseKey) {
32712
33265
  const csv = renderDirectoryWorkflowCsv(result);
32713
33266
  const artifact = await createDirectoryCsvArtifact({
32714
33267
  ownerId: String(user.id),
32715
- jobId: (0, import_node_crypto16.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
33268
+ jobId: (0, import_node_crypto17.createHash)("sha256").update(debitKey2).digest("hex").slice(0, 32),
32716
33269
  createdAt: result.extractedAt,
32717
33270
  filename: `${options.state}-${options.query}-directory.csv`,
32718
33271
  csv,
@@ -32768,12 +33321,12 @@ async function runSynchronously(c, user, options, plan, responseKey) {
32768
33321
  await releaseConcurrencyGate(gate.lockId);
32769
33322
  }
32770
33323
  }
32771
- var import_node_crypto16, import_hono15, directoryApp;
33324
+ var import_node_crypto17, import_hono16, directoryApp;
32772
33325
  var init_directory_routes = __esm({
32773
33326
  "src/api/directory-routes.ts"() {
32774
33327
  "use strict";
32775
- import_node_crypto16 = require("crypto");
32776
- import_hono15 = require("hono");
33328
+ import_node_crypto17 = require("crypto");
33329
+ import_hono16 = require("hono");
32777
33330
  init_api_auth();
32778
33331
  init_db();
32779
33332
  init_rates();
@@ -32785,7 +33338,7 @@ var init_directory_routes = __esm({
32785
33338
  init_directory_workflow_repository();
32786
33339
  init_directory_artifacts();
32787
33340
  init_directory_workflow_dispatch();
32788
- directoryApp = new import_hono15.Hono();
33341
+ directoryApp = new import_hono16.Hono();
32789
33342
  directoryApp.post("/run", createApiKeyAuth(), async (c) => {
32790
33343
  const user = c.get("user");
32791
33344
  const body = await c.req.json().catch(() => ({}));
@@ -32993,11 +33546,11 @@ async function readHostedLocationCsv(sourceUrl) {
32993
33546
  const bytes = await readResponseBodyWithLimit(response, LOCATION_IMPORT_MAX_BYTES);
32994
33547
  return bytes.toString("utf8");
32995
33548
  }
32996
- var import_hono16, import_zod29, LOCATION_IMPORT_MAX_BYTES, LOCATION_IMPORT_TIMEOUT_MS, marketQuerySchema, importSchema, censusSyncSchema, locationDataApp;
33549
+ var import_hono17, import_zod29, LOCATION_IMPORT_MAX_BYTES, LOCATION_IMPORT_TIMEOUT_MS, marketQuerySchema, importSchema, censusSyncSchema, locationDataApp;
32997
33550
  var init_location_data_routes = __esm({
32998
33551
  "src/api/location-data-routes.ts"() {
32999
33552
  "use strict";
33000
- import_hono16 = require("hono");
33553
+ import_hono17 = require("hono");
33001
33554
  import_zod29 = require("zod");
33002
33555
  init_api_auth();
33003
33556
  init_url_utils();
@@ -33022,7 +33575,7 @@ var init_location_data_routes = __esm({
33022
33575
  sourceUrl: import_zod29.z.string().url().max(2e3).optional()
33023
33576
  });
33024
33577
  censusSyncSchema = import_zod29.z.object({ state: import_zod29.z.string().trim().min(2).max(40) });
33025
- locationDataApp = new import_hono16.Hono();
33578
+ locationDataApp = new import_hono17.Hono();
33026
33579
  locationDataApp.get("/markets", createApiKeyAuth(), async (c) => {
33027
33580
  const parsed = marketQuerySchema.safeParse(c.req.query());
33028
33581
  if (!parsed.success) {
@@ -35193,7 +35746,7 @@ async function readManifestFromSummary(summary) {
35193
35746
  function webhookSignature(body, timestamp2) {
35194
35747
  const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
35195
35748
  if (!secret2) return null;
35196
- return (0, import_node_crypto17.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
35749
+ return (0, import_node_crypto18.createHmac)("sha256", secret2).update(`${timestamp2}.${body}`).digest("hex");
35197
35750
  }
35198
35751
  async function deliverWorkflowWebhook(input) {
35199
35752
  if (!input.webhookUrl) return;
@@ -35400,13 +35953,13 @@ async function dispatchDueWorkflowSchedules(apiUrl, limit = 3) {
35400
35953
  }
35401
35954
  return { dispatched: results.length, results };
35402
35955
  }
35403
- var import_node_crypto17, import_promises12, import_hono17, import_zod36, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
35956
+ var import_node_crypto18, import_promises12, import_hono18, import_zod36, workflowApp, WorkflowInputSchema, WorkflowIdSchema, CadenceSchema, ScheduleStatusSchema, RunBodySchema, ScheduleCreateSchema, SchedulePatchSchema, TERMINAL_RUN_STATUSES;
35404
35957
  var init_workflow_routes = __esm({
35405
35958
  "src/api/workflow-routes.ts"() {
35406
35959
  "use strict";
35407
- import_node_crypto17 = require("crypto");
35960
+ import_node_crypto18 = require("crypto");
35408
35961
  import_promises12 = require("fs/promises");
35409
- import_hono17 = require("hono");
35962
+ import_hono18 = require("hono");
35410
35963
  import_zod36 = require("zod");
35411
35964
  init_artifact_writer();
35412
35965
  init_registry2();
@@ -35414,7 +35967,7 @@ var init_workflow_routes = __esm({
35414
35967
  init_db();
35415
35968
  init_url_utils();
35416
35969
  init_concurrency_gates();
35417
- workflowApp = new import_hono17.Hono();
35970
+ workflowApp = new import_hono18.Hono();
35418
35971
  WorkflowInputSchema = import_zod36.z.record(import_zod36.z.unknown()).default({});
35419
35972
  WorkflowIdSchema = import_zod36.z.string().min(1);
35420
35973
  CadenceSchema = import_zod36.z.enum(["daily", "weekly", "monthly"]);
@@ -35685,7 +36238,7 @@ var init_workflow_routes = __esm({
35685
36238
  // src/serp-intelligence/page-snapshot-extractor.ts
35686
36239
  function sha256(value) {
35687
36240
  if (!value) return null;
35688
- return (0, import_node_crypto18.createHash)("sha256").update(value).digest("hex");
36241
+ return (0, import_node_crypto19.createHash)("sha256").update(value).digest("hex");
35689
36242
  }
35690
36243
  function countWords(markdown) {
35691
36244
  const matches = markdown.trim().match(/\b[\p{L}\p{N}][\p{L}\p{N}'-]*\b/gu);
@@ -35985,11 +36538,11 @@ async function capturePageSnapshots(targets, options = {}) {
35985
36538
  }
35986
36539
  };
35987
36540
  }
35988
- var import_node_crypto18, import_p_limit5, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
36541
+ var import_node_crypto19, import_p_limit5, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_CONTENT_CHARS;
35989
36542
  var init_page_snapshot_extractor = __esm({
35990
36543
  "src/serp-intelligence/page-snapshot-extractor.ts"() {
35991
36544
  "use strict";
35992
- import_node_crypto18 = require("crypto");
36545
+ import_node_crypto19 = require("crypto");
35993
36546
  import_p_limit5 = __toESM(require("p-limit"), 1);
35994
36547
  init_kpo_extractor();
35995
36548
  init_url_utils();
@@ -36596,21 +37149,21 @@ async function logRequestEventBestEffort(input) {
36596
37149
  }
36597
37150
  }
36598
37151
  function captureBillingKeys(userId, suppliedKey, body) {
36599
- const responseKey = suppliedKey?.trim() || (0, import_node_crypto19.randomUUID)();
36600
- const requestFingerprint2 = (0, import_node_crypto19.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
36601
- const keyDigest = (0, import_node_crypto19.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
37152
+ const responseKey = suppliedKey?.trim() || (0, import_node_crypto20.randomUUID)();
37153
+ const requestFingerprint2 = (0, import_node_crypto20.createHash)("sha256").update(JSON.stringify(body)).digest("hex");
37154
+ const keyDigest = (0, import_node_crypto20.createHash)("sha256").update(String(userId)).update("\0").update(responseKey).digest("hex");
36602
37155
  return {
36603
37156
  responseKey,
36604
37157
  debitKey: `serp-capture:${userId}:${keyDigest}`,
36605
37158
  debitDescription: `${body.query} [request:${requestFingerprint2}]`
36606
37159
  };
36607
37160
  }
36608
- var import_hono18, import_node_crypto19, SERP_INTELLIGENCE_RATE_LIMIT, SERP_INTELLIGENCE_RATE_WINDOW_SECONDS, POST_CAPTURE_ROUTE_LABEL, POST_PAGE_SNAPSHOTS_ROUTE_LABEL, SERP_CAPTURE_BILLING_SOURCE, serpIntelligenceApp;
37161
+ var import_hono19, import_node_crypto20, SERP_INTELLIGENCE_RATE_LIMIT, SERP_INTELLIGENCE_RATE_WINDOW_SECONDS, POST_CAPTURE_ROUTE_LABEL, POST_PAGE_SNAPSHOTS_ROUTE_LABEL, SERP_CAPTURE_BILLING_SOURCE, serpIntelligenceApp;
36609
37162
  var init_serp_intelligence_routes = __esm({
36610
37163
  "src/api/serp-intelligence-routes.ts"() {
36611
37164
  "use strict";
36612
- import_hono18 = require("hono");
36613
- import_node_crypto19 = require("crypto");
37165
+ import_hono19 = require("hono");
37166
+ import_node_crypto20 = require("crypto");
36614
37167
  init_browser_service_env();
36615
37168
  init_page_snapshot_extractor();
36616
37169
  init_serp_capture_service();
@@ -36625,7 +37178,7 @@ var init_serp_intelligence_routes = __esm({
36625
37178
  POST_CAPTURE_ROUTE_LABEL = "POST /capture";
36626
37179
  POST_PAGE_SNAPSHOTS_ROUTE_LABEL = "POST /page-snapshots";
36627
37180
  SERP_CAPTURE_BILLING_SOURCE = "serp_intelligence_capture";
36628
- serpIntelligenceApp = new import_hono18.Hono();
37181
+ serpIntelligenceApp = new import_hono19.Hono();
36629
37182
  serpIntelligenceApp.use("*", createApiKeyAuth());
36630
37183
  serpIntelligenceApp.post("/capture", async (c) => {
36631
37184
  void POST_CAPTURE_ROUTE_LABEL;
@@ -36847,7 +37400,7 @@ var PACKAGE_VERSION;
36847
37400
  var init_version = __esm({
36848
37401
  "src/version.ts"() {
36849
37402
  "use strict";
36850
- PACKAGE_VERSION = "0.36.0";
37403
+ PACKAGE_VERSION = "0.37.0";
36851
37404
  }
36852
37405
  });
36853
37406
 
@@ -39372,7 +39925,7 @@ var init_rank_tracker_blueprint = __esm({
39372
39925
 
39373
39926
  // src/mcp/paa-mcp-server.ts
39374
39927
  function hashOwnerId(callerKey) {
39375
- return (0, import_node_crypto20.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
39928
+ return (0, import_node_crypto21.createHash)("sha256").update(callerKey).digest("hex").slice(0, 24);
39376
39929
  }
39377
39930
  function liveWebToolAnnotations(title) {
39378
39931
  return {
@@ -39865,14 +40418,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
39865
40418
  annotations: { title: "Set Scheduled Action Connections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
39866
40419
  }, async (input) => executor.setScheduledActionConnections(input));
39867
40420
  }
39868
- var import_mcp, import_node_fs10, import_node_path14, import_node_crypto20;
40421
+ var import_mcp, import_node_fs10, import_node_path14, import_node_crypto21;
39869
40422
  var init_paa_mcp_server = __esm({
39870
40423
  "src/mcp/paa-mcp-server.ts"() {
39871
40424
  "use strict";
39872
40425
  import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
39873
40426
  import_node_fs10 = require("fs");
39874
40427
  import_node_path14 = require("path");
39875
- import_node_crypto20 = require("crypto");
40428
+ import_node_crypto21 = require("crypto");
39876
40429
  init_version();
39877
40430
  init_mcp_response_formatter();
39878
40431
  init_server_instructions();
@@ -39942,11 +40495,11 @@ function withDefaultHostedProfile(input) {
39942
40495
  ...typeof saveProfileChanges === "boolean" ? { saveProfileChanges } : {}
39943
40496
  };
39944
40497
  }
39945
- var import_node_crypto21, HttpMcpToolExecutor;
40498
+ var import_node_crypto22, HttpMcpToolExecutor;
39946
40499
  var init_http_mcp_tool_executor = __esm({
39947
40500
  "src/mcp/http-mcp-tool-executor.ts"() {
39948
40501
  "use strict";
39949
- import_node_crypto21 = require("crypto");
40502
+ import_node_crypto22 = require("crypto");
39950
40503
  init_harvest_timeout();
39951
40504
  init_browser_service_env();
39952
40505
  HttpMcpToolExecutor = class {
@@ -40006,13 +40559,13 @@ var init_http_mcp_tool_executor = __esm({
40006
40559
  }
40007
40560
  }
40008
40561
  async callConnectedMutation(path6, body, timeoutMs = this.timeoutMs) {
40009
- const fingerprint = (0, import_node_crypto21.createHash)("sha256").update("POST").update("\0").update(path6).update("\0").update(JSON.stringify(body)).digest("hex");
40562
+ const fingerprint = (0, import_node_crypto22.createHash)("sha256").update("POST").update("\0").update(path6).update("\0").update(JSON.stringify(body)).digest("hex");
40010
40563
  const now = Date.now();
40011
40564
  for (const [pendingFingerprint, pendingEntry] of this.pendingConnectedMutationKeys) {
40012
40565
  if (pendingEntry.expiresAt <= now) this.pendingConnectedMutationKeys.delete(pendingFingerprint);
40013
40566
  }
40014
40567
  const pending = this.pendingConnectedMutationKeys.get(fingerprint);
40015
- const idempotencyKey2 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto21.randomUUID)();
40568
+ const idempotencyKey2 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto22.randomUUID)();
40016
40569
  this.pendingConnectedMutationKeys.set(fingerprint, {
40017
40570
  key: idempotencyKey2,
40018
40571
  expiresAt: now + 15 * 6e4
@@ -40026,13 +40579,13 @@ var init_http_mcp_tool_executor = __esm({
40026
40579
  return result;
40027
40580
  }
40028
40581
  async callDirectoryWorkflowStart(body, explicitIdempotencyKey) {
40029
- const idempotencyKey2 = `mcp-directory-${(0, import_node_crypto21.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
40582
+ const idempotencyKey2 = `mcp-directory-${(0, import_node_crypto22.createHash)("sha256").update(explicitIdempotencyKey).digest("hex")}`;
40030
40583
  return this.call("/directory/run", body, this.timeoutMs, "POST", {
40031
40584
  "Idempotency-Key": idempotencyKey2
40032
40585
  });
40033
40586
  }
40034
40587
  async callSiteExtractStart(toolName, body, explicitIdempotencyKey) {
40035
- const idempotencyKey2 = `mcp-site-${(0, import_node_crypto21.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
40588
+ const idempotencyKey2 = `mcp-site-${(0, import_node_crypto22.createHash)("sha256").update(toolName).update("\0").update(explicitIdempotencyKey).digest("hex")}`;
40036
40589
  return this.call("/extract-site", body, this.timeoutMs, "POST", {
40037
40590
  "Idempotency-Key": idempotencyKey2
40038
40591
  });
@@ -40293,13 +40846,13 @@ var init_http_mcp_tool_executor = __esm({
40293
40846
  }, void 0, "PUT");
40294
40847
  }
40295
40848
  async captureSerpSnapshot(input) {
40296
- const fingerprint = (0, import_node_crypto21.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
40849
+ const fingerprint = (0, import_node_crypto22.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
40297
40850
  const now = Date.now();
40298
40851
  for (const [pendingFingerprint, pendingEntry] of this.pendingSerpCaptureBillingKeys) {
40299
40852
  if (pendingEntry.expiresAt <= now) this.pendingSerpCaptureBillingKeys.delete(pendingFingerprint);
40300
40853
  }
40301
40854
  const pending = this.pendingSerpCaptureBillingKeys.get(fingerprint);
40302
- const idempotencyKey2 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto21.randomUUID)();
40855
+ const idempotencyKey2 = pending && pending.expiresAt > now ? pending.key : (0, import_node_crypto22.randomUUID)();
40303
40856
  this.pendingSerpCaptureBillingKeys.set(fingerprint, {
40304
40857
  key: idempotencyKey2,
40305
40858
  expiresAt: now + 15 * 6e4
@@ -41930,7 +42483,7 @@ var init_browser_agent_mcp_server = __esm({
41930
42483
  });
41931
42484
 
41932
42485
  // src/mcp/memory-tool-schemas.ts
41933
- var import_zod40, AcceptShareSchema, ApproveSenderSchema, DeclineShareSchema, GetChatLinkSchema, GetVaultAppLinkSchema, InboxSettingsSchema, InviteAccountSchema, issueKeyTool_scopeShape, IssueKeySchema, ListApprovedSendersSchema, ListKeysSchema, NoteInboxSchema, RemoveApprovedSenderSchema, RevokeChatLinkSchema, RevokeKeySchema, RevokeShareSchema, RevokeVaultAppLinkSchema, SetAgentIdentitySchema, SetScopeSchema, ShareNoteSchema, ShareVaultSchema, SwapVaultSchema, SwitchAccountSchema, UnlinkShareSchema, memoryCaptureTool_notePropsSchema, MemoryCaptureSchema, MemoryQuestionsSchema, PrepareMemoryWriteSchema, validateMemoryWriteTool_notePropsSchema, ValidateMemoryWriteSchema, CreateChannelSchema, GetMessageNoteSchema, ListChannelMembersSchema, ListChannelMessagesSchema, MyMentionsSchema, PollChannelSchema, PostMessageSchema, ReactMessageSchema, RemoveChannelMemberSchema, ReplyMessageSchema, factHistoryTool_entryShape, FactHistorySchema, recordFactTool_factShape, RecordFactSchema, NoteBacklinksSchema, GraphPathSchema, GraphUniverseSchema, LibraryIngestSchema, bulkDeleteNotesTool_FilterSchema, BulkDeleteNotesSchema, DeleteNoteSchema, ExportSchema, getTool_notePropsSchema, GetSchema, ListSchema, putTool_notePropsSchema, PutSchema, searchTool_primitiveValue, SearchSchema, SuggestSchema, UploadSchema, TemporalRecallSchema, CreateScheduledActionSchema, DeleteScheduledActionSchema, GetScheduleLinkSchema, GetScheduleStatusSchema, ListScheduledActionsSchema, PauseScheduledActionSchema, ProposeScheduledActionSchema, ResumeScheduledActionSchema, RevokeScheduleLinkSchema, SetScheduleDefaultsSchema, SetScheduleEntitlementSchema, CostUsageSchema, StorageUsageSchema, CreateTableSchema, deleteTableRowsTool_FilterSchema, DeleteTableRowsSchema, DescribeTableSchema, DropTableSchema, InsertTableRowsSchema, ListTablesSchema, queryTableTool_FilterSchema, QueryTableSchema, ListTagsSchema, ResolveTagsSchema, UpsertTagSchema, AddVaultSchema, CreateSecureVaultSchema, DeleteVaultSchema, GetVaultContractSchema, ListSharedWithMeSchema, ListVaultsSchema, ProvisionDefaultsSchema, RouteMemorySchema, VideoAnalyzeStartSchema, VideoAnalyzeStatusSchema, CreateWebhookSchema, ListWebhooksSchema, RevokeWebhookSchema, MEMORY_TOOL_SCHEMAS;
42486
+ var import_zod40, AcceptShareSchema, ApproveSenderSchema, DeclineShareSchema, GetChatLinkSchema, GetVaultAppLinkSchema, InboxSettingsSchema, InviteAccountSchema, issueKeyTool_scopeShape, IssueKeySchema, ListApprovedSendersSchema, ListKeysSchema, NoteInboxSchema, RemoveApprovedSenderSchema, RevokeChatLinkSchema, RevokeKeySchema, RevokeShareSchema, RevokeVaultAppLinkSchema, SetAgentIdentitySchema, SetScopeSchema, ShareNoteSchema, ShareVaultSchema, SwapVaultSchema, SwitchAccountSchema, UnlinkShareSchema, memoryCaptureTool_notePropsSchema, MemoryCaptureSchema, MemoryQuestionsSchema, PrepareMemoryWriteSchema, validateMemoryWriteTool_notePropsSchema, ValidateMemoryWriteSchema, CreateChannelSchema, GetMessageNoteSchema, ListChannelMembersSchema, ListChannelMessagesSchema, MyMentionsSchema, PollChannelSchema, PostMessageSchema, ReactMessageSchema, RemoveChannelMemberSchema, ReplyMessageSchema, factHistoryTool_entryShape, FactHistorySchema, recordFactTool_factShape, RecordFactSchema, NoteBacklinksSchema, GraphPathSchema, GraphUniverseSchema, LibraryIngestSchema, bulkDeleteNotesTool_FilterSchema, BulkDeleteNotesSchema, DeleteNoteSchema, ExportSchema, getTool_notePropsSchema, GetSchema, ListSchema, putTool_notePropsSchema, PutSchema, searchTool_primitiveValue, SearchSchema, SuggestSchema, UploadSchema, TemporalRecallSchema, CreateScheduledActionSchema, DeleteScheduledActionSchema, GetScheduleLinkSchema, GetScheduleStatusSchema, ListScheduledActionsSchema, PauseScheduledActionSchema, ProposeScheduledActionSchema, ResumeScheduledActionSchema, RevokeScheduleLinkSchema, SetScheduleDefaultsSchema, SetScheduleEntitlementSchema, CostUsageSchema, StorageUsageSchema, CreateTableSchema, deleteTableRowsTool_FilterSchema, DeleteTableRowsSchema, DescribeTableSchema, DropTableSchema, InsertTableRowsSchema, ListTablesSchema, queryTableTool_FilterSchema, QueryTableSchema, ListTagsSchema, MergeTagsSchema, ResolveTagsSchema, UpsertTagSchema, AddVaultSchema, CreateSecureVaultSchema, DeleteVaultSchema, GetVaultContractSchema, ListSharedWithMeSchema, ListVaultsSchema, ProvisionDefaultsSchema, RouteMemorySchema, VideoAnalyzeStartSchema, VideoAnalyzeStatusSchema, CreateWebhookSchema, ListWebhooksSchema, RevokeWebhookSchema, MEMORY_TOOL_SCHEMAS;
41934
42487
  var init_memory_tool_schemas = __esm({
41935
42488
  "src/mcp/memory-tool-schemas.ts"() {
41936
42489
  "use strict";
@@ -42508,14 +43061,20 @@ var init_memory_tool_schemas = __esm({
42508
43061
  content: import_zod40.z.string().min(1),
42509
43062
  props: memoryCaptureTool_notePropsSchema,
42510
43063
  baseRevision: import_zod40.z.number().optional(),
42511
- tagDecisions: import_zod40.z.array(import_zod40.z.object({ tag: import_zod40.z.string().min(1), central: import_zod40.z.boolean(), reusable: import_zod40.z.boolean(), description: import_zod40.z.string().optional() })).max(8).optional().describe("Required justification for any tag that does not already exist. Existing exact/alias/near tags are canonicalized automatically; a new tag is accepted only when its matching decision has central=true and reusable=true.")
43064
+ tagDecisions: import_zod40.z.array(import_zod40.z.object({ tag: import_zod40.z.string().min(1), central: import_zod40.z.boolean(), reusable: import_zod40.z.boolean(), description: import_zod40.z.string().optional(), acceptCanonical: import_zod40.z.string().optional().describe("Reuse this existing tag instead of the proposed one, confirming a candidate returned by an earlier review. The proposed spelling is recorded as its alias.") })).max(8).optional().describe("Required justification for any tag that does not already exist. Tags resolve against the account's existing vocabulary; new tags require a one-line description.")
42512
43065
  },
42513
43066
  output: {
42514
43067
  ok: import_zod40.z.boolean(),
42515
43068
  valid: import_zod40.z.boolean().optional(),
42516
43069
  errors: import_zod40.z.array(import_zod40.z.string()).optional(),
42517
43070
  warnings: import_zod40.z.array(import_zod40.z.string()).optional(),
42518
- tagResolutions: import_zod40.z.array(import_zod40.z.object({ candidate: import_zod40.z.string(), action: import_zod40.z.enum(["reuse", "create", "omit"]), tag: import_zod40.z.string().optional(), reason: import_zod40.z.string() })).optional(),
43071
+ tagResolutions: import_zod40.z.array(import_zod40.z.object({
43072
+ candidate: import_zod40.z.string(),
43073
+ action: import_zod40.z.enum(["reuse", "review", "create", "omit"]),
43074
+ tag: import_zod40.z.string().optional(),
43075
+ candidates: import_zod40.z.array(import_zod40.z.object({ tag: import_zod40.z.string(), matchedVia: import_zod40.z.string(), score: import_zod40.z.number(), description: import_zod40.z.string().nullable() })).optional(),
43076
+ reason: import_zod40.z.string()
43077
+ })).optional(),
42519
43078
  note: import_zod40.z.object({ path: import_zod40.z.string(), title: import_zod40.z.string(), updatedAt: import_zod40.z.string(), revision: import_zod40.z.number() }).optional(),
42520
43079
  indexed: import_zod40.z.number().optional(),
42521
43080
  verified: import_zod40.z.object({ contentBytes: import_zod40.z.number(), propsPersisted: import_zod40.z.boolean(), revision: import_zod40.z.number() }).optional(),
@@ -43027,7 +43586,8 @@ var init_memory_tool_schemas = __esm({
43027
43586
  source: import_zod40.z.string().min(1).describe("Provenance of the content, e.g. a URL or tool name. Must be non-empty."),
43028
43587
  capturedAt: import_zod40.z.string().optional().describe("ISO-8601 capture timestamp. Optional; defaults to now. Also seeds the deterministic storage path."),
43029
43588
  summary: import_zod40.z.string().optional().describe("Retrieval-ready source summary. Optional; a provenance summary is generated when omitted."),
43030
- tags: import_zod40.z.array(import_zod40.z.string()).max(8).optional().describe("Reviewed canonical tags. Existing tags should be resolved first; when omitted, deterministic source/topic tags are generated."),
43589
+ tags: import_zod40.z.array(import_zod40.z.string()).max(8).optional().describe("Reviewed canonical tags. Tags resolve against the account's existing vocabulary; new tags require a one-line description. When omitted, only deterministic source-provenance tags are recorded."),
43590
+ tagDescriptions: import_zod40.z.record(import_zod40.z.string()).optional().describe("One-line meaning for any supplied tag that is new to the account, keyed by tag."),
43031
43591
  related: import_zod40.z.array(import_zod40.z.string()).optional().describe("Reviewed same-vault Library note paths."),
43032
43592
  relatedVaultNotes: import_zod40.z.array(import_zod40.z.string()).optional().describe("Reviewed cross-vault references in Vault::path.md form."),
43033
43593
  localVaultPath: import_zod40.z.string().optional().describe("Filesystem root to also mirror the item to. Optional; falls back to MEMORY_LOCAL_VAULT_ROOT env when set.")
@@ -43274,7 +43834,8 @@ var init_memory_tool_schemas = __esm({
43274
43834
  title: import_zod40.z.string().optional().describe("Optional human-readable title; defaults are derived from the path when omitted."),
43275
43835
  content: import_zod40.z.string().min(1).describe("The full note body to store and index for semantic search. Must be non-empty."),
43276
43836
  props: putTool_notePropsSchema.optional().describe("Obsidian note primitives plus vault-specific template fields. On edits, supplied fields patch the stored props instead of replacing the whole object; pass an empty array to deliberately clear a link list. Type/domain/folder also steer routing when no vault is given."),
43277
- baseRevision: import_zod40.z.number().optional().describe("Revision the edit is based on (from a prior get/put). When provided, the write only applies if the note is still at this revision; otherwise it is rejected as a conflict instead of silently overwriting a concurrent edit. Omit for last-write-wins (fine for solo notes).")
43837
+ baseRevision: import_zod40.z.number().optional().describe("Revision the edit is based on (from a prior get/put). When provided, the write only applies if the note is still at this revision; otherwise it is rejected as a conflict instead of silently overwriting a concurrent edit. Omit for last-write-wins (fine for solo notes)."),
43838
+ tagDescriptions: import_zod40.z.record(import_zod40.z.string()).optional().describe("One-line meaning for any tag in props.tags that is new to the account, keyed by tag. Tags resolve against the account's existing vocabulary; new tags require a one-line description.")
43278
43839
  },
43279
43840
  output: {
43280
43841
  ok: import_zod40.z.boolean().describe("True when the note was stored; false on auth/scope error, empty content, or a revision conflict."),
@@ -43953,16 +44514,51 @@ var init_memory_tool_schemas = __esm({
43953
44514
  },
43954
44515
  annotations: { title: "List Memory Tags", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
43955
44516
  };
44517
+ MergeTagsSchema = {
44518
+ id: "merge-memory-tags",
44519
+ upstreamName: "mergeTagsTool",
44520
+ description: 'Collapse a duplicate tag into the canonical one across the whole account: every note using "from" is retagged to "into", "from" is recorded as an alias of "into", and the duplicate is removed from the vocabulary. Use when list-memory-tags shows two spellings of one concept. Irreversible; requires write scope.',
44521
+ input: {
44522
+ from: import_zod40.z.string().min(1).describe("The duplicate tag to retire."),
44523
+ into: import_zod40.z.string().min(1).describe('The canonical tag to keep. Every note using "from" is retagged to this.')
44524
+ },
44525
+ output: {
44526
+ ok: import_zod40.z.boolean(),
44527
+ from: import_zod40.z.string().optional(),
44528
+ into: import_zod40.z.string().optional(),
44529
+ notesRetagged: import_zod40.z.number().optional(),
44530
+ aliases: import_zod40.z.array(import_zod40.z.string()).optional(),
44531
+ descriptionCopied: import_zod40.z.boolean().optional(),
44532
+ error: import_zod40.z.string().optional()
44533
+ },
44534
+ annotations: { title: "Merge Memory Tags", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
44535
+ };
43956
44536
  ResolveTagsSchema = {
43957
44537
  id: "resolve-memory-tags",
43958
44538
  upstreamName: "resolveTagsTool",
43959
- description: "Resolve proposed concepts against the live tag vocabulary. Always inspect the complete vocabulary with list-memory-tags first. Returns reuse, create, or omit; a new tag is appropriate only when no equivalent exists and the concept is central and reusable.",
44539
+ description: "Resolve proposed concepts against the live tag vocabulary. Always inspect the complete vocabulary with list-memory-tags first. Returns reuse, review, create, or omit: spelling and singular/plural variants resolve to the canonical tag silently, while close and semantically related tags come back as ranked candidates for you to choose from. A new tag is appropriate only when no equivalent exists and the concept is central and reusable.",
43960
44540
  input: {
43961
- candidates: import_zod40.z.array(import_zod40.z.object({ tag: import_zod40.z.string().min(1), central: import_zod40.z.boolean().optional(), reusable: import_zod40.z.boolean().optional(), description: import_zod40.z.string().optional() })).min(1).max(20)
44541
+ candidates: import_zod40.z.array(import_zod40.z.object({ tag: import_zod40.z.string().min(1), central: import_zod40.z.boolean().optional(), reusable: import_zod40.z.boolean().optional(), description: import_zod40.z.string().optional() })).min(1).max(20),
44542
+ accept: import_zod40.z.record(import_zod40.z.string()).optional().describe("Confirm a candidate returned by an earlier review, as {proposedTag: canonicalTag}. The proposed spelling is recorded as an alias of the canonical tag so the same judgement is never re-litigated.")
43962
44543
  },
43963
44544
  output: {
43964
44545
  ok: import_zod40.z.boolean(),
43965
- resolutions: import_zod40.z.array(import_zod40.z.object({ candidate: import_zod40.z.string(), normalized: import_zod40.z.string(), action: import_zod40.z.enum(["reuse", "create", "omit"]), tag: import_zod40.z.string().optional(), matchedBy: import_zod40.z.enum(["exact", "alias", "near"]).optional(), score: import_zod40.z.number().optional(), reason: import_zod40.z.string() })).optional(),
44546
+ resolutions: import_zod40.z.array(import_zod40.z.object({
44547
+ candidate: import_zod40.z.string(),
44548
+ normalized: import_zod40.z.string(),
44549
+ action: import_zod40.z.enum(["reuse", "review", "create", "omit"]),
44550
+ tag: import_zod40.z.string().optional(),
44551
+ matchedBy: import_zod40.z.enum(["exact", "alias", "near"]).optional(),
44552
+ matchedVia: import_zod40.z.enum(["key", "alias", "stem", "trigram", "embedding"]).optional(),
44553
+ score: import_zod40.z.number().optional(),
44554
+ candidates: import_zod40.z.array(import_zod40.z.object({
44555
+ tag: import_zod40.z.string(),
44556
+ matchedVia: import_zod40.z.enum(["key", "alias", "stem", "trigram", "embedding"]),
44557
+ score: import_zod40.z.number(),
44558
+ description: import_zod40.z.string().nullable()
44559
+ })).optional().describe("Ranked existing tags to choose from when action is review. Nothing is merged automatically."),
44560
+ reason: import_zod40.z.string()
44561
+ })).optional(),
43966
44562
  error: import_zod40.z.string().optional()
43967
44563
  },
43968
44564
  annotations: { title: "Resolve Memory Tags", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
@@ -44345,6 +44941,7 @@ var init_memory_tool_schemas = __esm({
44345
44941
  ListTablesSchema,
44346
44942
  QueryTableSchema,
44347
44943
  ListTagsSchema,
44944
+ MergeTagsSchema,
44348
44945
  ResolveTagsSchema,
44349
44946
  UpsertTagSchema,
44350
44947
  AddVaultSchema,
@@ -44811,8 +45408,12 @@ function registerWriteCutovers() {
44811
45408
  const result = await import_upsert_tag.upsertTagTool.execute({ ...input, apiKey }, {});
44812
45409
  return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
44813
45410
  });
45411
+ registerCutover("mergeTagsTool", async (input, apiKey) => {
45412
+ const result = await import_merge_tags.mergeTagsTool.execute({ ...input, apiKey }, {});
45413
+ return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
45414
+ });
44814
45415
  }
44815
- var import_accept_share, import_approve_sender, import_decline_share, import_inbox_settings, import_remove_approved_sender, import_revoke_share, import_set_agent_identity, import_share_note, import_share_vault, import_swap_vault, import_switch_account, import_unlink_share, import_memory_questions, import_create_channel, import_list_channel_messages, import_poll_channel, import_post_message, import_react_message, import_remove_channel_member, import_reply_message, import_record_fact, import_library_ingest, import_delete_note, import_put, import_upload, import_delete_scheduled_action, import_pause_scheduled_action, import_resume_scheduled_action, import_table_create, import_table_delete_rows, import_table_drop, import_table_insert_rows, import_add_vault, import_delete_vault, import_memory_capture, import_upsert_tag, registered2;
45416
+ var import_accept_share, import_approve_sender, import_decline_share, import_inbox_settings, import_remove_approved_sender, import_revoke_share, import_set_agent_identity, import_share_note, import_share_vault, import_swap_vault, import_switch_account, import_unlink_share, import_memory_questions, import_create_channel, import_list_channel_messages, import_poll_channel, import_post_message, import_react_message, import_remove_channel_member, import_reply_message, import_record_fact, import_library_ingest, import_delete_note, import_put, import_upload, import_delete_scheduled_action, import_pause_scheduled_action, import_resume_scheduled_action, import_table_create, import_table_delete_rows, import_table_drop, import_table_insert_rows, import_add_vault, import_delete_vault, import_memory_capture, import_upsert_tag, import_merge_tags, registered2;
44816
45417
  var init_writes = __esm({
44817
45418
  "src/mcp/memory-cutover/writes.ts"() {
44818
45419
  "use strict";
@@ -44853,6 +45454,7 @@ var init_writes = __esm({
44853
45454
  import_delete_vault = require("mcpscraper-memory-tools/tools/vaults/delete-vault");
44854
45455
  import_memory_capture = require("mcpscraper-memory-tools/tools/capture/memory-capture");
44855
45456
  import_upsert_tag = require("mcpscraper-memory-tools/tools/tags/upsert-tag");
45457
+ import_merge_tags = require("mcpscraper-memory-tools/tools/tags/merge-tags");
44856
45458
  registered2 = false;
44857
45459
  }
44858
45460
  });
@@ -45085,11 +45687,11 @@ async function requireMcpCallerKey(c) {
45085
45687
  if (!user) return mcpAuthError();
45086
45688
  return { key: callerKey, user };
45087
45689
  }
45088
- var import_hono19, import_webStandardStreamableHttp, mcpApp;
45690
+ var import_hono20, import_webStandardStreamableHttp, mcpApp;
45089
45691
  var init_mcp_routes = __esm({
45090
45692
  "src/mcp/mcp-routes.ts"() {
45091
45693
  "use strict";
45092
- import_hono19 = require("hono");
45694
+ import_hono20 = require("hono");
45093
45695
  import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
45094
45696
  init_paa_mcp_server();
45095
45697
  init_http_mcp_tool_executor();
@@ -45111,7 +45713,7 @@ var init_mcp_routes = __esm({
45111
45713
  registerCrossServiceCutovers();
45112
45714
  registerVideoCutovers();
45113
45715
  configureReportSaving(false);
45114
- mcpApp = new import_hono19.Hono();
45716
+ mcpApp = new import_hono20.Hono();
45115
45717
  mcpApp.all("/", async (c) => {
45116
45718
  try {
45117
45719
  const keyOrError = await requireMcpCallerKey(c);
@@ -45267,7 +45869,7 @@ async function runBrowserAgentMigration() {
45267
45869
  }
45268
45870
  async function createExtensionRow(input) {
45269
45871
  const db = getDb();
45270
- const id = `bext_${(0, import_node_crypto22.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45872
+ const id = `bext_${(0, import_node_crypto23.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45271
45873
  await db.execute({
45272
45874
  sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
45273
45875
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -45302,7 +45904,7 @@ async function deleteExtensionRow(userId, name) {
45302
45904
  }
45303
45905
  async function createAuthConnectionRow(input) {
45304
45906
  const db = getDb();
45305
- const connectionId = `authc_${(0, import_node_crypto22.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45907
+ const connectionId = `authc_${(0, import_node_crypto23.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45306
45908
  await db.execute({
45307
45909
  sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
45308
45910
  VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
@@ -45387,7 +45989,7 @@ async function deleteProfileLabel(userId, profile) {
45387
45989
  }
45388
45990
  async function createSessionRow(input) {
45389
45991
  const db = getDb();
45390
- const id = `bas_${(0, import_node_crypto22.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45992
+ const id = `bas_${(0, import_node_crypto23.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
45391
45993
  await db.execute({
45392
45994
  sql: `INSERT INTO browser_agent_sessions (id, runtime_session_id, live_view_url, cdp_ws_url, status, label, user_id, concurrency_lock_id, last_action_at)
45393
45995
  VALUES (?, ?, ?, ?, 'open', ?, ?, ?, datetime('now'))`,
@@ -45456,7 +46058,7 @@ async function recordAction(input) {
45456
46058
  sql: `INSERT INTO browser_agent_actions (id, session_id, type, params_json, ok, error)
45457
46059
  VALUES (?, ?, ?, ?, ?, ?)`,
45458
46060
  args: [
45459
- `baa_${(0, import_node_crypto22.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
46061
+ `baa_${(0, import_node_crypto23.randomUUID)().replace(/-/g, "").slice(0, 20)}`,
45460
46062
  input.sessionId,
45461
46063
  input.type,
45462
46064
  input.params == null ? null : JSON.stringify(input.params),
@@ -45496,11 +46098,11 @@ async function listReplayRows(sessionId) {
45496
46098
  });
45497
46099
  return res.rows;
45498
46100
  }
45499
- var import_node_crypto22, _ready2, _migrationPromise2;
46101
+ var import_node_crypto23, _ready2, _migrationPromise2;
45500
46102
  var init_browser_agent_db = __esm({
45501
46103
  "src/api/browser-agent-db.ts"() {
45502
46104
  "use strict";
45503
- import_node_crypto22 = require("crypto");
46105
+ import_node_crypto23 = require("crypto");
45504
46106
  init_db();
45505
46107
  _ready2 = false;
45506
46108
  _migrationPromise2 = null;
@@ -46755,7 +47357,7 @@ async function loadOpenSession(id, userId) {
46755
47357
  return row;
46756
47358
  }
46757
47359
  function buildBrowserAgentRoutes() {
46758
- const app2 = new import_hono20.Hono();
47360
+ const app2 = new import_hono21.Hono();
46759
47361
  app2.use("*", async (c, next) => {
46760
47362
  await migrateBrowserAgent();
46761
47363
  return next();
@@ -47256,7 +47858,7 @@ function buildBrowserAgentRoutes() {
47256
47858
  }
47257
47859
  const existing = await getExtensionRow(user.id, name);
47258
47860
  if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
47259
- const backendName = `u${user.id}_${(0, import_node_crypto23.randomUUID)().replace(/-/g, "")}`;
47861
+ const backendName = `u${user.id}_${(0, import_node_crypto24.randomUUID)().replace(/-/g, "")}`;
47260
47862
  try {
47261
47863
  const imported = await importExtensionFromStore(storeUrl, backendName);
47262
47864
  const row = await createExtensionRow({
@@ -47315,12 +47917,12 @@ function buildBrowserAgentRoutes() {
47315
47917
  });
47316
47918
  return app2;
47317
47919
  }
47318
- var import_node_crypto23, import_hono20, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE;
47920
+ var import_node_crypto24, import_hono21, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE;
47319
47921
  var init_browser_agent_routes = __esm({
47320
47922
  "src/api/browser-agent-routes.ts"() {
47321
47923
  "use strict";
47322
- import_node_crypto23 = require("crypto");
47323
- import_hono20 = require("hono");
47924
+ import_node_crypto24 = require("crypto");
47925
+ import_hono21 = require("hono");
47324
47926
  init_api_auth();
47325
47927
  init_errors();
47326
47928
  init_db();
@@ -47628,7 +48230,7 @@ function linePriceId(line) {
47628
48230
  const l = line;
47629
48231
  return l?.price?.id ?? l?.pricing?.price_details?.price ?? l?.plan?.id;
47630
48232
  }
47631
- async function resolveUser2(customerId, emailFallback) {
48233
+ async function resolveUser3(customerId, emailFallback) {
47632
48234
  let user = await getUserByStripeCustomerId(customerId);
47633
48235
  if (user) return user;
47634
48236
  if (!emailFallback) return void 0;
@@ -47636,18 +48238,18 @@ async function resolveUser2(customerId, emailFallback) {
47636
48238
  if (user) await setStripeCustomerId(user.id, customerId);
47637
48239
  return user;
47638
48240
  }
47639
- var import_stripe, import_hono21, stripe, stripeApp;
48241
+ var import_stripe, import_hono22, stripe, stripeApp;
47640
48242
  var init_stripe_routes = __esm({
47641
48243
  "src/api/stripe-routes.ts"() {
47642
48244
  "use strict";
47643
48245
  import_stripe = __toESM(require("stripe"), 1);
47644
- import_hono21 = require("hono");
48246
+ import_hono22 = require("hono");
47645
48247
  init_db();
47646
48248
  init_rates();
47647
48249
  init_memory();
47648
48250
  init_connected_account_billing();
47649
48251
  stripe = new import_stripe.default(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-02-25.clover" });
47650
- stripeApp = new import_hono21.Hono();
48252
+ stripeApp = new import_hono22.Hono();
47651
48253
  stripeApp.post("/webhooks", async (c) => {
47652
48254
  const sig = c.req.header("stripe-signature");
47653
48255
  const body = await c.req.text();
@@ -47673,7 +48275,7 @@ var init_stripe_routes = __esm({
47673
48275
  const tierPriceId = liveBasePlanPriceId ?? invoice.lines.data.map(linePriceId).find((id) => id && id in SUBSCRIPTION_TIERS);
47674
48276
  const tier = tierPriceId ? SUBSCRIPTION_TIERS[tierPriceId] : void 0;
47675
48277
  if (tier) {
47676
- const user = await resolveUser2(invoice.customer, invoice.customer_email ?? void 0);
48278
+ const user = await resolveUser3(invoice.customer, invoice.customer_email ?? void 0);
47677
48279
  if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
47678
48280
  await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id, { neverExpires: tier.credits_never_expire });
47679
48281
  await setSubscriptionTier(user.id, tier.tier, tier.concurrency, subId ?? user.subscription_id);
@@ -47693,7 +48295,7 @@ var init_stripe_routes = __esm({
47693
48295
  const priceId = findBasePlanItem(sub)?.price?.id ?? sub.items.data[0]?.price?.id;
47694
48296
  if (priceId && priceId in MEMORY_PLANS) {
47695
48297
  const mem = MEMORY_PLANS[priceId];
47696
- const user2 = await resolveUser2(sub.customer, sub.customer_email);
48298
+ const user2 = await resolveUser3(sub.customer, sub.customer_email);
47697
48299
  if (user2) {
47698
48300
  const live = sub.status === "active" || sub.status === "trialing";
47699
48301
  const newMemoryPlan = live ? mem.plan : "free";
@@ -47775,7 +48377,7 @@ async function getKeys() {
47775
48377
  const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
47776
48378
  const full = await (0, import_jose2.exportJWK)(privateKey);
47777
48379
  const publicJwk = { kty: full.kty, n: full.n, e: full.e };
47778
- const kid = (0, import_node_crypto24.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
48380
+ const kid = (0, import_node_crypto25.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
47779
48381
  publicJwk.kid = kid;
47780
48382
  publicJwk.alg = "RS256";
47781
48383
  publicJwk.use = "sig";
@@ -47954,23 +48556,23 @@ async function validateAuthRequest(p) {
47954
48556
  }
47955
48557
  function pkceMatches(verifier, challenge) {
47956
48558
  if (!verifier) return false;
47957
- const computed = (0, import_node_crypto24.createHash)("sha256").update(verifier).digest("base64url");
48559
+ const computed = (0, import_node_crypto25.createHash)("sha256").update(verifier).digest("base64url");
47958
48560
  return computed === challenge;
47959
48561
  }
47960
48562
  async function mintAccessToken(identity, scope, plan, audience) {
47961
48563
  const { privateKey, kid } = await getKeys();
47962
- return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto24.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
48564
+ return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto25.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
47963
48565
  }
47964
48566
  function tokenErrorResponse(c, error, description, status) {
47965
48567
  return c.json({ error, error_description: description }, status);
47966
48568
  }
47967
- var import_hono22, import_cookie, import_node_crypto24, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
48569
+ var import_hono23, import_cookie, import_node_crypto25, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
47968
48570
  var init_oauth_routes = __esm({
47969
48571
  "src/api/oauth-routes.ts"() {
47970
48572
  "use strict";
47971
- import_hono22 = require("hono");
48573
+ import_hono23 = require("hono");
47972
48574
  import_cookie = require("hono/cookie");
47973
- import_node_crypto24 = require("crypto");
48575
+ import_node_crypto25 = require("crypto");
47974
48576
  import_jose2 = require("jose");
47975
48577
  init_session();
47976
48578
  init_db();
@@ -47995,7 +48597,7 @@ var init_oauth_routes = __esm({
47995
48597
  sameSite: "Lax"
47996
48598
  };
47997
48599
  cachedKeys = null;
47998
- oauthApp = new import_hono22.Hono();
48600
+ oauthApp = new import_hono23.Hono();
47999
48601
  oauthApp.use("*", async (c, next) => {
48000
48602
  c.header("Access-Control-Allow-Origin", "*");
48001
48603
  c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
@@ -48056,7 +48658,7 @@ var init_oauth_routes = __esm({
48056
48658
  }
48057
48659
  }
48058
48660
  const clientName = typeof body.client_name === "string" ? body.client_name : null;
48059
- const clientId = `client_${(0, import_node_crypto24.randomBytes)(16).toString("hex")}`;
48661
+ const clientId = `client_${(0, import_node_crypto25.randomBytes)(16).toString("hex")}`;
48060
48662
  await registerClient(clientId, redirectUris, clientName);
48061
48663
  console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
48062
48664
  return c.json({
@@ -48109,7 +48711,7 @@ var init_oauth_routes = __esm({
48109
48711
  if (action === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
48110
48712
  if (action !== "approve") return c.text("unsupported action", 400);
48111
48713
  const scope = negotiateScope(p.scope, user, p.resource);
48112
- const code = `code_${(0, import_node_crypto24.randomBytes)(32).toString("base64url")}`;
48714
+ const code = `code_${(0, import_node_crypto25.randomBytes)(32).toString("base64url")}`;
48113
48715
  const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
48114
48716
  await putCode({
48115
48717
  code,
@@ -48148,7 +48750,7 @@ var init_oauth_routes = __esm({
48148
48750
  const plan = user ? resolvePlan(user) : "free";
48149
48751
  const audience = record.resource ?? RESOURCE();
48150
48752
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
48151
- const refreshToken = `rt_${(0, import_node_crypto24.randomBytes)(40).toString("base64url")}`;
48753
+ const refreshToken = `rt_${(0, import_node_crypto25.randomBytes)(40).toString("base64url")}`;
48152
48754
  await putRefresh({
48153
48755
  refresh_token: refreshToken,
48154
48756
  client_id: clientId,
@@ -48178,7 +48780,7 @@ var init_oauth_routes = __esm({
48178
48780
  const plan = user ? resolvePlan(user) : "free";
48179
48781
  const audience = record.resource ?? RESOURCE();
48180
48782
  const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
48181
- const nextRefresh = `rt_${(0, import_node_crypto24.randomBytes)(40).toString("base64url")}`;
48783
+ const nextRefresh = `rt_${(0, import_node_crypto25.randomBytes)(40).toString("base64url")}`;
48182
48784
  await rotateRefresh(refreshToken, {
48183
48785
  refresh_token: nextRefresh,
48184
48786
  client_id: record.client_id,
@@ -48740,13 +49342,13 @@ loadChannels();
48740
49342
  </body>
48741
49343
  </html>`;
48742
49344
  }
48743
- var import_hono23, chatApp;
49345
+ var import_hono24, chatApp;
48744
49346
  var init_chat_routes = __esm({
48745
49347
  "src/api/chat-routes.ts"() {
48746
49348
  "use strict";
48747
- import_hono23 = require("hono");
49349
+ import_hono24 = require("hono");
48748
49350
  init_memory();
48749
- chatApp = new import_hono23.Hono();
49351
+ chatApp = new import_hono24.Hono();
48750
49352
  chatApp.get("/api/channels", async (c) => {
48751
49353
  const token = c.req.query("token");
48752
49354
  if (!tokenOk(token)) return jsonError("missing or malformed token", 401);
@@ -49030,11 +49632,11 @@ async function readVault(vault, token) {
49030
49632
  if (failed && !failed.ok) throw new Error(failed.error ?? `unable to read ${vault} record`);
49031
49633
  return notes.filter((result) => result.ok && !!result.note).map((result) => workspaceRecord(vault, result.note));
49032
49634
  }
49033
- var import_hono24, VAULTS, PROJECT_TYPES, PROJECT_KIND_BY_TYPE, PROJECT_TYPE_BY_KIND, MAX_VAULT_RECORDS, MAX_MEMORY_READ_CONCURRENCY, MAX_MEMORY_READ_ATTEMPTS, vaultApp;
49635
+ var import_hono25, VAULTS, PROJECT_TYPES, PROJECT_KIND_BY_TYPE, PROJECT_TYPE_BY_KIND, MAX_VAULT_RECORDS, MAX_MEMORY_READ_CONCURRENCY, MAX_MEMORY_READ_ATTEMPTS, vaultApp;
49034
49636
  var init_vault_routes = __esm({
49035
49637
  "src/api/vault-routes.ts"() {
49036
49638
  "use strict";
49037
- import_hono24 = require("hono");
49639
+ import_hono25 = require("hono");
49038
49640
  init_memory();
49039
49641
  VAULTS = ["People", "Organizations", "Deals", "Projects", "Tasks", "Communications", "Calendar"];
49040
49642
  PROJECT_TYPES = ["codebase", "personal", "work", "client"];
@@ -49054,7 +49656,7 @@ var init_vault_routes = __esm({
49054
49656
  MAX_VAULT_RECORDS = 200;
49055
49657
  MAX_MEMORY_READ_CONCURRENCY = 4;
49056
49658
  MAX_MEMORY_READ_ATTEMPTS = 3;
49057
- vaultApp = new import_hono24.Hono();
49659
+ vaultApp = new import_hono25.Hono();
49058
49660
  vaultApp.use("/api/*", async (c, next) => {
49059
49661
  cors(c);
49060
49662
  if (c.req.method === "OPTIONS") return c.body(null, 204);
@@ -49518,13 +50120,13 @@ loadActions();
49518
50120
  </body>
49519
50121
  </html>`;
49520
50122
  }
49521
- var import_hono25, scheduleApp;
50123
+ var import_hono26, scheduleApp;
49522
50124
  var init_schedule_routes = __esm({
49523
50125
  "src/api/schedule-routes.ts"() {
49524
50126
  "use strict";
49525
- import_hono25 = require("hono");
50127
+ import_hono26 = require("hono");
49526
50128
  init_memory();
49527
- scheduleApp = new import_hono25.Hono();
50129
+ scheduleApp = new import_hono26.Hono();
49528
50130
  scheduleApp.get("/api/status", async (c) => {
49529
50131
  const token = c.req.query("token");
49530
50132
  if (!tokenOk2(token)) return jsonError2("missing or malformed token", 401);
@@ -49598,7 +50200,7 @@ function quoteUntrusted(value) {
49598
50200
  return clean2.split("\n").map((line) => `> ${line}`).join("\n");
49599
50201
  }
49600
50202
  function shortHash(value, length = 24) {
49601
- return (0, import_node_crypto25.createHash)("sha256").update(value).digest("hex").slice(0, length);
50203
+ return (0, import_node_crypto26.createHash)("sha256").update(value).digest("hex").slice(0, length);
49602
50204
  }
49603
50205
  function safePathPart(value) {
49604
50206
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "contact";
@@ -50086,11 +50688,11 @@ async function captureSupportMessage(memoryKey, email, options) {
50086
50688
  direction: options.direction
50087
50689
  };
50088
50690
  }
50089
- var import_node_crypto25, SUPPORT_TAGS, ISSUE_TAGS;
50691
+ var import_node_crypto26, SUPPORT_TAGS, ISSUE_TAGS;
50090
50692
  var init_resend_support_thread = __esm({
50091
50693
  "src/api/resend-support-thread.ts"() {
50092
50694
  "use strict";
50093
- import_node_crypto25 = require("crypto");
50695
+ import_node_crypto26 = require("crypto");
50094
50696
  init_memory();
50095
50697
  SUPPORT_TAGS = [
50096
50698
  {
@@ -50219,17 +50821,17 @@ async function relayOperatorReply(resend, email, customerEmail, threadAddress, n
50219
50821
  if (sent.error) throw new Error(`support reply relay failed: ${sent.error.message}`);
50220
50822
  return sent.data.id;
50221
50823
  }
50222
- var import_hono26, import_resend, SUPPORT_ADDRESS, DMARC_ADDRESS, FORWARD_FROM, resendInboundApp;
50824
+ var import_hono27, import_resend, SUPPORT_ADDRESS, DMARC_ADDRESS, FORWARD_FROM, resendInboundApp;
50223
50825
  var init_resend_inbound_routes = __esm({
50224
50826
  "src/api/resend-inbound-routes.ts"() {
50225
50827
  "use strict";
50226
- import_hono26 = require("hono");
50828
+ import_hono27 = require("hono");
50227
50829
  import_resend = require("resend");
50228
50830
  init_resend_support_thread();
50229
50831
  SUPPORT_ADDRESS = "support@mcpscraper.dev";
50230
50832
  DMARC_ADDRESS = "dmarc@mcpscraper.dev";
50231
50833
  FORWARD_FROM = "MCP Scraper Support <support@updates.mcpscraper.dev>";
50232
- resendInboundApp = new import_hono26.Hono();
50834
+ resendInboundApp = new import_hono27.Hono();
50233
50835
  resendInboundApp.post("/webhooks", async (c) => {
50234
50836
  const apiKey = process.env.RESEND_API_KEY?.trim();
50235
50837
  const webhookSecret = process.env.RESEND_WEBHOOK_SECRET?.trim();
@@ -50486,7 +51088,7 @@ var init_site_extract_reconciliation = __esm({
50486
51088
 
50487
51089
  // src/api/page-diff.ts
50488
51090
  function sha256Hex(value) {
50489
- return (0, import_node_crypto26.createHash)("sha256").update(value).digest("hex");
51091
+ return (0, import_node_crypto27.createHash)("sha256").update(value).digest("hex");
50490
51092
  }
50491
51093
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
50492
51094
  if (value.length <= maxChars) return { value, truncated: false };
@@ -50543,11 +51145,11 @@ function diffPageContent(oldContent, newContent) {
50543
51145
  totalChangedLineCount
50544
51146
  };
50545
51147
  }
50546
- var import_node_crypto26, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
51148
+ var import_node_crypto27, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
50547
51149
  var init_page_diff = __esm({
50548
51150
  "src/api/page-diff.ts"() {
50549
51151
  "use strict";
50550
- import_node_crypto26 = require("crypto");
51152
+ import_node_crypto27 = require("crypto");
50551
51153
  import_diff = require("diff");
50552
51154
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
50553
51155
  MAX_DIFF_HUNKS = 200;
@@ -50624,7 +51226,7 @@ function canonicalJson(value) {
50624
51226
  return JSON.stringify(value);
50625
51227
  }
50626
51228
  function sha2562(value) {
50627
- return (0, import_node_crypto27.createHash)("sha256").update(value).digest("hex");
51229
+ return (0, import_node_crypto28.createHash)("sha256").update(value).digest("hex");
50628
51230
  }
50629
51231
  function sensitiveKey(key) {
50630
51232
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -50839,11 +51441,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
50839
51441
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
50840
51442
  };
50841
51443
  }
50842
- var import_node_crypto27, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
51444
+ var import_node_crypto28, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
50843
51445
  var init_connection_memory_import = __esm({
50844
51446
  "src/api/connection-memory-import.ts"() {
50845
51447
  "use strict";
50846
- import_node_crypto27 = require("crypto");
51448
+ import_node_crypto28 = require("crypto");
50847
51449
  init_slugify();
50848
51450
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
50849
51451
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -51017,7 +51619,7 @@ function finitePositive(value, fallback) {
51017
51619
  return Number.isFinite(value) && value > 0 ? value : fallback;
51018
51620
  }
51019
51621
  async function collectConnectedDataExport(args) {
51020
- const exportId = (0, import_node_crypto28.randomUUID)();
51622
+ const exportId = (0, import_node_crypto29.randomUUID)();
51021
51623
  const now = args.now ?? Date.now;
51022
51624
  const startedAt = now();
51023
51625
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -51127,11 +51729,11 @@ ${lines.length ? `${lines.join("\n")}
51127
51729
  untrustedContent: true
51128
51730
  };
51129
51731
  }
51130
- var import_node_crypto28, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
51732
+ var import_node_crypto29, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
51131
51733
  var init_connected_data_export = __esm({
51132
51734
  "src/api/connected-data-export.ts"() {
51133
51735
  "use strict";
51134
- import_node_crypto28 = require("crypto");
51736
+ import_node_crypto29 = require("crypto");
51135
51737
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
51136
51738
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
51137
51739
  );
@@ -51244,7 +51846,7 @@ async function exportSearchConsoleTableData(args) {
51244
51846
  offset += rows.length;
51245
51847
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
51246
51848
  }
51247
- const exportId = (0, import_node_crypto29.randomUUID)();
51849
+ const exportId = (0, import_node_crypto30.randomUUID)();
51248
51850
  const artifact = await args.writeArtifact({
51249
51851
  ownerId: args.ownerId,
51250
51852
  exportId,
@@ -51266,11 +51868,11 @@ async function exportSearchConsoleTableData(args) {
51266
51868
  warnings
51267
51869
  };
51268
51870
  }
51269
- var import_node_crypto29, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
51871
+ var import_node_crypto30, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
51270
51872
  var init_search_console_table_export = __esm({
51271
51873
  "src/api/search-console-table-export.ts"() {
51272
51874
  "use strict";
51273
- import_node_crypto29 = require("crypto");
51875
+ import_node_crypto30 = require("crypto");
51274
51876
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
51275
51877
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
51276
51878
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -51698,7 +52300,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
51698
52300
  updated_at = excluded.updated_at
51699
52301
  `,
51700
52302
  args: [
51701
- (0, import_node_crypto30.randomUUID)(),
52303
+ (0, import_node_crypto31.randomUUID)(),
51702
52304
  userId,
51703
52305
  connection.providerConfigKey,
51704
52306
  connection.provider,
@@ -51769,7 +52371,7 @@ async function recordServiceConnectionHealth(args) {
51769
52371
  });
51770
52372
  await getDb().execute({
51771
52373
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
51772
- args: [(0, import_node_crypto30.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
52374
+ args: [(0, import_node_crypto31.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
51773
52375
  });
51774
52376
  }
51775
52377
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -51809,7 +52411,7 @@ async function claimServiceConnectionAction(args) {
51809
52411
  if (!connection) throw new Error("service_connection_not_found");
51810
52412
  const inserted = await getDb().execute({
51811
52413
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status) VALUES (?, ?, ?, ?, ?, 'started')`,
51812
- args: [(0, import_node_crypto30.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId]
52414
+ args: [(0, import_node_crypto31.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId]
51813
52415
  });
51814
52416
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
51815
52417
  const existing = await getDb().execute({
@@ -51822,11 +52424,11 @@ async function claimServiceConnectionAction(args) {
51822
52424
  errorCode: typeof existing.rows[0]?.error_code === "string" ? existing.rows[0].error_code : null
51823
52425
  };
51824
52426
  }
51825
- var import_node_crypto30, schemaReady2, schemaDb3;
52427
+ var import_node_crypto31, schemaReady2, schemaDb3;
51826
52428
  var init_service_connections = __esm({
51827
52429
  "src/api/service-connections.ts"() {
51828
52430
  "use strict";
51829
- import_node_crypto30 = require("crypto");
52431
+ import_node_crypto31 = require("crypto");
51830
52432
  init_db();
51831
52433
  schemaReady2 = null;
51832
52434
  schemaDb3 = null;
@@ -52143,7 +52745,7 @@ async function listNangoToolsDirect(identity, connectionId) {
52143
52745
  });
52144
52746
  const readTools = tools.filter((tool) => classifyTool(tool) === "read").map((tool) => tool.name);
52145
52747
  const actionTools = tools.filter((tool) => classifyTool(tool) === "action").map((tool) => tool.name);
52146
- const revision = (0, import_node_crypto31.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
52748
+ const revision = (0, import_node_crypto32.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
52147
52749
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
52148
52750
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
52149
52751
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -52163,8 +52765,8 @@ async function callNangoToolDirect(args) {
52163
52765
  identity: args.identity,
52164
52766
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
52165
52767
  });
52166
- const requestId = args.requestId?.trim() || (0, import_node_crypto31.randomUUID)();
52167
- const idempotencyKey2 = `main-nango:${(0, import_node_crypto31.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
52768
+ const requestId = args.requestId?.trim() || (0, import_node_crypto32.randomUUID)();
52769
+ const idempotencyKey2 = `main-nango:${(0, import_node_crypto32.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
52168
52770
  const startedAt = /* @__PURE__ */ new Date();
52169
52771
  const started = performance.now();
52170
52772
  let result;
@@ -52189,7 +52791,7 @@ async function callNangoToolDirect(args) {
52189
52791
  toolName: args.tool,
52190
52792
  operationKind: args.operationKind ?? args.classification,
52191
52793
  outcome: providerError ? "error" : "partial",
52192
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto31.createHash)("sha256").update(requestId).digest("hex"),
52794
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto32.createHash)("sha256").update(requestId).digest("hex"),
52193
52795
  startedAt: startedAt.toISOString(),
52194
52796
  completedAt: completedAt.toISOString()
52195
52797
  }
@@ -52226,15 +52828,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
52226
52828
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
52227
52829
  protocolVersion: null,
52228
52830
  schemaSource: "live_tools_list",
52229
- schemaHash: (0, import_node_crypto31.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
52831
+ schemaHash: (0, import_node_crypto32.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
52230
52832
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
52231
52833
  };
52232
52834
  }
52233
- var import_node_crypto31, import_client9, import_streamableHttp, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
52835
+ var import_node_crypto32, import_client9, import_streamableHttp, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
52234
52836
  var init_main_nango_transport = __esm({
52235
52837
  "src/api/main-nango-transport.ts"() {
52236
52838
  "use strict";
52237
- import_node_crypto31 = require("crypto");
52839
+ import_node_crypto32 = require("crypto");
52238
52840
  import_client9 = require("@modelcontextprotocol/sdk/client/index.js");
52239
52841
  import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
52240
52842
  init_service_connections();
@@ -52856,7 +53458,7 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
52856
53458
  return data.connection.actionsEnabled === true;
52857
53459
  }
52858
53460
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey2) {
52859
- const requestId = `main-connected-action:${(0, import_node_crypto32.createHash)("sha256").update(identity).update("\0").update(idempotencyKey2?.trim() || (0, import_node_crypto32.randomUUID)()).digest("hex")}`;
53461
+ const requestId = `main-connected-action:${(0, import_node_crypto33.createHash)("sha256").update(identity).update("\0").update(idempotencyKey2?.trim() || (0, import_node_crypto33.randomUUID)()).digest("hex")}`;
52860
53462
  if (mainOwnsIntegrations()) {
52861
53463
  const selectedTool = tool?.trim();
52862
53464
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -52998,7 +53600,7 @@ function canonicalJson2(value) {
52998
53600
  return JSON.stringify(value);
52999
53601
  }
53000
53602
  function projectedToolSchemaHash(tool) {
53001
- return (0, import_node_crypto32.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
53603
+ return (0, import_node_crypto33.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
53002
53604
  }
53003
53605
  async function describeNangoTool(identity, connectionId, tool, fresh) {
53004
53606
  if (mainOwnsIntegrations()) {
@@ -53259,11 +53861,11 @@ async function callMainOwnedExportPage(identity, input) {
53259
53861
  untrustedContent: true
53260
53862
  };
53261
53863
  }
53262
- var import_node_crypto32, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
53864
+ var import_node_crypto33, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
53263
53865
  var init_nango_control = __esm({
53264
53866
  "src/api/nango-control.ts"() {
53265
53867
  "use strict";
53266
- import_node_crypto32 = require("crypto");
53868
+ import_node_crypto33 = require("crypto");
53267
53869
  init_connected_data_export();
53268
53870
  init_main_nango_transport();
53269
53871
  init_service_connections();
@@ -53731,8 +54333,8 @@ function signingSecret() {
53731
54333
  return secret2;
53732
54334
  }
53733
54335
  function schedulerIntegrationSignature(args) {
53734
- const bodyHash = (0, import_node_crypto33.createHash)("sha256").update(args.body).digest("hex");
53735
- return (0, import_node_crypto33.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
54336
+ const bodyHash = (0, import_node_crypto34.createHash)("sha256").update(args.body).digest("hex");
54337
+ return (0, import_node_crypto34.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
53736
54338
  ${args.path}
53737
54339
  ${args.timestamp}
53738
54340
  ${args.nonce}
@@ -53779,17 +54381,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
53779
54381
  });
53780
54382
  const suppliedBytes = Buffer.from(signature, "hex");
53781
54383
  const expectedBytes = Buffer.from(expected, "hex");
53782
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto33.timingSafeEqual)(suppliedBytes, expectedBytes)) {
54384
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto34.timingSafeEqual)(suppliedBytes, expectedBytes)) {
53783
54385
  throw new SchedulerIntegrationAuthError("invalid_signature");
53784
54386
  }
53785
54387
  await claimNonce(nonce, timestampMs);
53786
54388
  return { requestId };
53787
54389
  }
53788
- var import_node_crypto33, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
54390
+ var import_node_crypto34, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
53789
54391
  var init_scheduler_integration_auth = __esm({
53790
54392
  "src/api/scheduler-integration-auth.ts"() {
53791
54393
  "use strict";
53792
- import_node_crypto33 = require("crypto");
54394
+ import_node_crypto34 = require("crypto");
53793
54395
  init_db();
53794
54396
  init_service_connections();
53795
54397
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -54284,7 +54886,7 @@ async function chargeTierChangeNow(stripeClient, subscriptionId, customerId) {
54284
54886
  return { ok: false, amountDue: 0, error: err instanceof Error ? err.message : "Unable to charge the plan change immediately." };
54285
54887
  }
54286
54888
  }
54287
- var import_resend2, import_hono27, import_hono28, import_factory6, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth2, adminAuth, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
54889
+ var import_resend2, import_hono28, import_hono29, import_factory7, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth2, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
54288
54890
  var init_server = __esm({
54289
54891
  "src/api/server.ts"() {
54290
54892
  "use strict";
@@ -54302,8 +54904,8 @@ var init_server = __esm({
54302
54904
  init_media_extractor();
54303
54905
  init_site_mapper();
54304
54906
  init_site_extractor();
54305
- import_hono27 = require("hono");
54306
- import_hono28 = require("inngest/hono");
54907
+ import_hono28 = require("hono");
54908
+ import_hono29 = require("inngest/hono");
54307
54909
  init_client();
54308
54910
  init_site_audit();
54309
54911
  init_site_extract();
@@ -54321,6 +54923,8 @@ var init_server = __esm({
54321
54923
  init_screenshot_routes();
54322
54924
  init_facebook_ad_routes();
54323
54925
  init_google_ads_routes();
54926
+ init_admin_auth();
54927
+ init_admin_credit_routes();
54324
54928
  init_instagram_routes();
54325
54929
  init_reddit_routes();
54326
54930
  init_video_routes();
@@ -54346,7 +54950,7 @@ var init_server = __esm({
54346
54950
  init_schedule_routes();
54347
54951
  init_resend_inbound_routes();
54348
54952
  init_site_audit_worker();
54349
- import_factory6 = require("hono/factory");
54953
+ import_factory7 = require("hono/factory");
54350
54954
  import_cookie2 = require("hono/cookie");
54351
54955
  init_db();
54352
54956
  import_stripe2 = __toESM(require("stripe"), 1);
@@ -54386,13 +54990,13 @@ var init_server = __esm({
54386
54990
  maxAge: 60 * 60 * 24 * 30,
54387
54991
  sameSite: "Strict"
54388
54992
  };
54389
- requireAllowedOrigin = (0, import_factory6.createMiddleware)(async (c, next) => {
54993
+ requireAllowedOrigin = (0, import_factory7.createMiddleware)(async (c, next) => {
54390
54994
  const origin = c.req.header("origin");
54391
54995
  if (!origin) return next();
54392
54996
  if (!configuredOrigins().has(origin)) return c.json({ error: "Origin not allowed" }, 403);
54393
54997
  return next();
54394
54998
  });
54395
- auth2 = (0, import_factory6.createMiddleware)(async (c, next) => {
54999
+ auth2 = (0, import_factory7.createMiddleware)(async (c, next) => {
54396
55000
  const key = c.req.header("x-api-key");
54397
55001
  if (!key) return c.json({ error: "Missing API key" }, 401);
54398
55002
  const user = await getUserByApiKey(key);
@@ -54402,14 +55006,7 @@ var init_server = __esm({
54402
55006
  c.set("user", { ...refreshed, balance_mc: balanceMc });
54403
55007
  return next();
54404
55008
  });
54405
- adminAuth = (0, import_factory6.createMiddleware)(async (c, next) => {
54406
- const configured = process.env.ADMIN_KEY?.trim();
54407
- const key = c.req.header("x-admin-key")?.trim();
54408
- if (!configured) return c.json({ error: "Admin access disabled" }, 503);
54409
- if (!key || key !== configured) return c.json({ error: "Unauthorized" }, 401);
54410
- return next();
54411
- });
54412
- sessionAuth = (0, import_factory6.createMiddleware)(async (c, next) => {
55009
+ sessionAuth = (0, import_factory7.createMiddleware)(async (c, next) => {
54413
55010
  const token = (0, import_cookie2.getCookie)(c, "session");
54414
55011
  if (!token) return c.json({ error: "Not authenticated" }, 401);
54415
55012
  const userId = verifySession(token);
@@ -54421,7 +55018,7 @@ var init_server = __esm({
54421
55018
  c.set("sessionUser", { ...refreshed, balance_mc: balanceMc });
54422
55019
  return next();
54423
55020
  });
54424
- requireIntegrationsTier = (0, import_factory6.createMiddleware)(async (c, next) => {
55021
+ requireIntegrationsTier = (0, import_factory7.createMiddleware)(async (c, next) => {
54425
55022
  const user = c.get("user");
54426
55023
  if (!user.subscription_tier) {
54427
55024
  return c.json({
@@ -54432,12 +55029,12 @@ var init_server = __esm({
54432
55029
  }
54433
55030
  return next();
54434
55031
  });
54435
- requirePaidSchedulingTier = (0, import_factory6.createMiddleware)(async (c, next) => {
55032
+ requirePaidSchedulingTier = (0, import_factory7.createMiddleware)(async (c, next) => {
54436
55033
  const user = c.get("user");
54437
55034
  if (!hasPaidSchedulingAccess(user)) return c.json(paidPlanRequiredPayload(), 403);
54438
55035
  return next();
54439
55036
  });
54440
- app = new import_hono27.Hono();
55037
+ app = new import_hono28.Hono();
54441
55038
  STRIPE_API_VERSION = "2026-02-25.clover";
54442
55039
  app.use("*", async (c, next) => {
54443
55040
  await next();
@@ -56864,7 +57461,8 @@ var init_server = __esm({
56864
57461
  const bundle = stored.find((a) => a.contentType === "application/zip" || a.filename?.endsWith(".zip"));
56865
57462
  return c.json({ ok: true, jobId: jobId2, artifacts: stored.length, settlement, bundleUrl: bundle?.url ?? null, bundleBytes: bundle?.bytes ?? null });
56866
57463
  });
56867
- app.on(["GET", "POST", "PUT"], "/api/inngest", (0, import_hono28.serve)({ client: inngest, functions: [siteAuditFn, siteExtractFn, directoryWorkflowFn] }));
57464
+ app.on(["GET", "POST", "PUT"], "/api/inngest", (0, import_hono29.serve)({ client: inngest, functions: [siteAuditFn, siteExtractFn, directoryWorkflowFn] }));
57465
+ app.route("/admin/credits", adminCreditsApp);
56868
57466
  app.route("/api/internal/site-architecture-auditor", siteAuditApp);
56869
57467
  app.route("/api/internal/memory", internalMemoryApp);
56870
57468
  app.route("/api/internal/billing", internalBillingApp);