mcp-scraper 0.3.19 → 0.3.21

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 (33) hide show
  1. package/dist/bin/api-server.cjs +219 -164
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +1 -1
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +2 -2
  13. package/dist/{chunk-5H22TOXQ.js → chunk-3KYRG7O7.js} +130 -7
  14. package/dist/chunk-3KYRG7O7.js.map +1 -0
  15. package/dist/{chunk-T337IGVE.js → chunk-7PQFU7TV.js} +13 -20
  16. package/dist/chunk-7PQFU7TV.js.map +1 -0
  17. package/dist/chunk-J4PSMJNZ.js +7 -0
  18. package/dist/chunk-J4PSMJNZ.js.map +1 -0
  19. package/dist/{chunk-A46NKAAM.js → chunk-UO252634.js} +2 -2
  20. package/dist/{db-P76GVIFN.js → db-EG5ETPTY.js} +12 -2
  21. package/dist/{server-EYNMXWUJ.js → server-K4UIE2R3.js} +88 -144
  22. package/dist/server-K4UIE2R3.js.map +1 -0
  23. package/dist/{worker-YZ2ZJE4F.js → worker-AITQTXHM.js} +3 -3
  24. package/docs/mcp-tool-manifest.generated.json +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunk-5H22TOXQ.js.map +0 -1
  27. package/dist/chunk-AUKOY2IV.js +0 -7
  28. package/dist/chunk-AUKOY2IV.js.map +0 -1
  29. package/dist/chunk-T337IGVE.js.map +0 -1
  30. package/dist/server-EYNMXWUJ.js.map +0 -1
  31. /package/dist/{chunk-A46NKAAM.js.map → chunk-UO252634.js.map} +0 -0
  32. /package/dist/{db-P76GVIFN.js.map → db-EG5ETPTY.js.map} +0 -0
  33. /package/dist/{worker-YZ2ZJE4F.js.map → worker-AITQTXHM.js.map} +0 -0
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CREDIT_LOT_TTL,
2
3
  SiteAuditJobRowSchema,
3
4
  SiteAuditPhaseLogRowSchema,
4
5
  advanceWorkflowRunStep,
@@ -25,6 +26,7 @@ import {
25
26
  deactivateUser,
26
27
  debitMc,
27
28
  deleteWorkflowSchedule,
29
+ expireOldLots,
28
30
  failJob,
29
31
  failKpoJob,
30
32
  failWorkflowRunRecord,
@@ -62,6 +64,7 @@ import {
62
64
  markWorkflowRunRunning,
63
65
  markWorkflowScheduleRan,
64
66
  migrate,
67
+ migrateExistingBalancesToLots,
65
68
  patchWorkflowSchedule,
66
69
  reconcileBalanceMc,
67
70
  recordStripeEvent,
@@ -70,15 +73,18 @@ import {
70
73
  revokeApiKey,
71
74
  rotateApiKey,
72
75
  setConcurrencySubId,
76
+ setDbForTesting,
73
77
  setExtraConcurrencySlots,
74
78
  setPassword,
75
79
  setStripeCustomerId,
80
+ setSubscriptionTier,
76
81
  startHarvestAttempt,
77
82
  stripeEventAlreadyProcessed,
78
83
  updateKpoJobState,
79
84
  verifyPassword
80
- } from "./chunk-5H22TOXQ.js";
85
+ } from "./chunk-3KYRG7O7.js";
81
86
  export {
87
+ CREDIT_LOT_TTL,
82
88
  SiteAuditJobRowSchema,
83
89
  SiteAuditPhaseLogRowSchema,
84
90
  advanceWorkflowRunStep,
@@ -105,6 +111,7 @@ export {
105
111
  deactivateUser,
106
112
  debitMc,
107
113
  deleteWorkflowSchedule,
114
+ expireOldLots,
108
115
  failJob,
109
116
  failKpoJob,
110
117
  failWorkflowRunRecord,
@@ -142,6 +149,7 @@ export {
142
149
  markWorkflowRunRunning,
143
150
  markWorkflowScheduleRan,
144
151
  migrate,
152
+ migrateExistingBalancesToLots,
145
153
  patchWorkflowSchedule,
146
154
  reconcileBalanceMc,
147
155
  recordStripeEvent,
@@ -150,12 +158,14 @@ export {
150
158
  revokeApiKey,
151
159
  rotateApiKey,
152
160
  setConcurrencySubId,
161
+ setDbForTesting,
153
162
  setExtraConcurrencySlots,
154
163
  setPassword,
155
164
  setStripeCustomerId,
165
+ setSubscriptionTier,
156
166
  startHarvestAttempt,
157
167
  stripeEventAlreadyProcessed,
158
168
  updateKpoJobState,
159
169
  verifyPassword
160
170
  };
161
- //# sourceMappingURL=db-P76GVIFN.js.map
171
+ //# sourceMappingURL=db-EG5ETPTY.js.map
@@ -21,7 +21,7 @@ import {
21
21
  renderLinkReport,
22
22
  sanitizeAttempts,
23
23
  sanitizeHarvestResult
24
- } from "./chunk-A46NKAAM.js";
24
+ } from "./chunk-UO252634.js";
25
25
  import {
26
26
  csvRecords,
27
27
  listWorkflowDefinitions,
@@ -33,18 +33,16 @@ import {
33
33
  workflowStepCount,
34
34
  workflowSupportsSteps
35
35
  } from "./chunk-H2R232HK.js";
36
- import "./chunk-AUKOY2IV.js";
36
+ import "./chunk-J4PSMJNZ.js";
37
37
  import {
38
- BALANCE_PACK_LABELS,
39
- BALANCE_PRICE_IDS,
40
38
  BROWSER_OPEN_MIN_BALANCE_MC,
41
39
  CONCURRENCY_PRICE_ID,
42
40
  CREDIT_COST_CATALOG,
43
- FREE_MONTHLY_REFRESH_MC,
44
- FREE_SIGNUP_MC,
45
41
  LedgerOperation,
46
42
  MC_COSTS,
47
43
  MC_PER_CREDIT,
44
+ SUBSCRIPTION_TIERS,
45
+ SUBSCRIPTION_TIER_BY_KEY,
48
46
  browserActiveCostMc,
49
47
  classifyHarvestProblem,
50
48
  concurrencySlotBillingInfo,
@@ -52,7 +50,7 @@ import {
52
50
  harvestProblemResponse,
53
51
  insufficientBalanceResponse,
54
52
  serializeHarvestProblem
55
- } from "./chunk-T337IGVE.js";
53
+ } from "./chunk-7PQFU7TV.js";
56
54
  import {
57
55
  BrowserDriver,
58
56
  MapsSelectors,
@@ -86,7 +84,6 @@ import {
86
84
  appendWorkflowArtifacts,
87
85
  cancelJob,
88
86
  checkRateLimit,
89
- claimMonthlyFreeRefresh,
90
87
  completeJob,
91
88
  completeWorkflowRunRecord,
92
89
  consumePasswordResetToken,
@@ -101,6 +98,7 @@ import {
101
98
  deactivateUser,
102
99
  debitMc,
103
100
  deleteWorkflowSchedule,
101
+ expireOldLots,
104
102
  failJob,
105
103
  failWorkflowRunRecord,
106
104
  getDb,
@@ -139,8 +137,9 @@ import {
139
137
  setExtraConcurrencySlots,
140
138
  setPassword,
141
139
  setStripeCustomerId,
140
+ setSubscriptionTier,
142
141
  verifyPassword
143
- } from "./chunk-5H22TOXQ.js";
142
+ } from "./chunk-3KYRG7O7.js";
144
143
 
145
144
  // src/blog/registry.ts
146
145
  var posts = [
@@ -9044,7 +9043,8 @@ var DEFAULT_RETRY_AFTER_SECONDS = 30;
9044
9043
  var MAX_LOCK_TTL_SECONDS = 24 * 60 * 60;
9045
9044
  var schemaReady = null;
9046
9045
  function concurrencyLimitForUser(user) {
9047
- return 1 + Math.max(0, Number(user.extra_concurrency_slots ?? 0));
9046
+ const tier = Math.max(0, Number(user.subscription_concurrency ?? 0));
9047
+ return Math.max(1, tier) + Math.max(0, Number(user.extra_concurrency_slots ?? 0));
9048
9048
  }
9049
9049
  function concurrencyLimitExceededResponse(gate) {
9050
9050
  const upgrade = concurrencySlotBillingInfo();
@@ -16457,49 +16457,58 @@ stripeApp.post("/webhooks", async (c) => {
16457
16457
  }
16458
16458
  const isNew = await recordStripeEvent(event.id);
16459
16459
  if (!isNew) return c.json({ received: true });
16460
- if (event.type === "checkout.session.completed") {
16461
- const session = event.data.object;
16462
- if (session.payment_status !== "paid") return c.json({ received: true });
16463
- const customerId = session.customer;
16464
- const lineItems = await stripe.checkout.sessions.listLineItems(session.id);
16465
- const priceId = lineItems.data[0]?.price?.id;
16466
- if (!priceId) return c.json({ received: true });
16467
- if (priceId in BALANCE_PRICE_IDS) {
16468
- const mc = BALANCE_PRICE_IDS[priceId];
16469
- const label = BALANCE_PACK_LABELS[priceId] ?? "unknown";
16470
- let user = await getUserByStripeCustomerId(customerId);
16471
- if (!user) {
16472
- const email = session.customer_details?.email;
16473
- if (!email) return c.json({ received: true });
16474
- user = await getUserByEmail(email);
16475
- if (!user) return c.json({ received: true });
16476
- await setStripeCustomerId(user.id, customerId);
16460
+ if (event.type === "invoice.paid" || event.type === "invoice.payment_succeeded") {
16461
+ const invoice = event.data.object;
16462
+ const lineTierId = invoice.lines.data.map(linePriceId).find((id) => id && id in SUBSCRIPTION_TIERS);
16463
+ if (lineTierId) {
16464
+ const tier = SUBSCRIPTION_TIERS[lineTierId];
16465
+ const user = await resolveUser(invoice.customer, invoice.customer_email ?? void 0);
16466
+ if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
16467
+ await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id);
16468
+ await setSubscriptionTier(user.id, tier.tier, tier.concurrency, invoice.subscription ?? user.subscription_id);
16477
16469
  }
16478
- const pi = session.payment_intent;
16479
- if (await ledgerExistsForStripePI(pi)) return c.json({ received: true });
16480
- await creditMc(user.id, mc, LedgerOperation.TOPUP, `${label} balance top-up`, pi);
16481
16470
  }
16482
16471
  }
16483
- if (event.type === "customer.subscription.created") {
16472
+ if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated") {
16484
16473
  const sub = event.data.object;
16485
16474
  const priceId = sub.items.data[0]?.price?.id;
16486
- if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
16487
16475
  const user = await getUserByStripeCustomerId(sub.customer);
16488
16476
  if (!user) return c.json({ received: true });
16489
- await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
16490
- await setConcurrencySubId(user.id, sub.id);
16477
+ if (priceId && priceId in SUBSCRIPTION_TIERS) {
16478
+ const tier = SUBSCRIPTION_TIERS[priceId];
16479
+ const live = sub.status === "active" || sub.status === "trialing";
16480
+ await setSubscriptionTier(user.id, live ? tier.tier : null, live ? tier.concurrency : 0, live ? sub.id : null);
16481
+ } else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
16482
+ await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
16483
+ await setConcurrencySubId(user.id, sub.id);
16484
+ }
16491
16485
  }
16492
16486
  if (event.type === "customer.subscription.deleted") {
16493
16487
  const sub = event.data.object;
16494
16488
  const priceId = sub.items.data[0]?.price?.id;
16495
- if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
16496
16489
  const user = await getUserByStripeCustomerId(sub.customer);
16497
16490
  if (!user) return c.json({ received: true });
16498
- await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
16499
- await setConcurrencySubId(user.id, null);
16491
+ if (priceId && priceId in SUBSCRIPTION_TIERS) {
16492
+ await setSubscriptionTier(user.id, null, 0, null);
16493
+ } else if (priceId === CONCURRENCY_PRICE_ID) {
16494
+ await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
16495
+ await setConcurrencySubId(user.id, null);
16496
+ }
16500
16497
  }
16501
16498
  return c.json({ received: true });
16502
16499
  });
16500
+ function linePriceId(line) {
16501
+ const l = line;
16502
+ return l?.price?.id ?? l?.pricing?.price_details?.price ?? l?.plan?.id;
16503
+ }
16504
+ async function resolveUser(customerId, emailFallback) {
16505
+ let user = await getUserByStripeCustomerId(customerId);
16506
+ if (user) return user;
16507
+ if (!emailFallback) return void 0;
16508
+ user = await getUserByEmail(emailFallback);
16509
+ if (user) await setStripeCustomerId(user.id, customerId);
16510
+ return user;
16511
+ }
16503
16512
 
16504
16513
  // src/api/site-audit-worker.ts
16505
16514
  var MAX_CONCURRENT_SITE_AUDIT = 1;
@@ -16539,96 +16548,15 @@ import { createMiddleware as createMiddleware3 } from "hono/factory";
16539
16548
  import { getCookie, setCookie, deleteCookie } from "hono/cookie";
16540
16549
  import Stripe2 from "stripe";
16541
16550
 
16542
- // src/api/billing-schemas.ts
16543
- import { z as z19 } from "zod";
16544
- var BillingCheckoutBodySchema = z19.object({
16545
- priceId: z19.string().min(1)
16546
- });
16547
- var FreeCreditBreakdownSchema = z19.object({
16548
- signup_grant_mc: z19.number().int().nonnegative(),
16549
- monthly_refresh_mc: z19.number().int().nonnegative(),
16550
- total_free_mc: z19.number().int().nonnegative(),
16551
- signup_grant_credits: z19.number().nonnegative(),
16552
- monthly_refresh_credits: z19.number().nonnegative(),
16553
- total_free_credits: z19.number().nonnegative()
16554
- });
16555
- var BillingBalanceResponseSchema = z19.object({
16556
- balance_mc: z19.number().int().nonnegative(),
16557
- balance_credits: z19.number().nonnegative(),
16558
- free_credits: FreeCreditBreakdownSchema,
16559
- ledger: z19.array(z19.any())
16560
- });
16561
- var MonthlyRefreshSweepResultSchema = z19.object({
16562
- usersRefreshed: z19.number().int().nonnegative(),
16563
- totalMcGranted: z19.number().int().nonnegative()
16564
- });
16565
-
16566
16551
  // src/api/credit-operations.ts
16567
- function monthKey(value = /* @__PURE__ */ new Date()) {
16568
- return value.toISOString().slice(0, 7);
16569
- }
16570
- function userCreatedBeforeThisMonth(user) {
16571
- return user.created_at.slice(0, 7) < monthKey();
16572
- }
16573
- async function grantSignupCredit(userId) {
16574
- if (await ledgerExistsForOperation(userId, LedgerOperation.SIGNUP_GRANT)) return;
16575
- await creditMc(userId, FREE_SIGNUP_MC, LedgerOperation.SIGNUP_GRANT, "Welcome to MCP Scraper");
16552
+ async function grantSignupCredit(_userId) {
16553
+ return;
16576
16554
  }
16577
16555
  async function applyMonthlyFreeRefresh(user) {
16578
- if (!userCreatedBeforeThisMonth(user)) return user;
16579
- const claimed = await claimMonthlyFreeRefresh(user.id, monthKey());
16580
- if (!claimed) return user;
16581
- const balance = await creditMc(
16582
- user.id,
16583
- FREE_MONTHLY_REFRESH_MC,
16584
- LedgerOperation.MONTHLY_REFRESH,
16585
- "Monthly free credits"
16586
- );
16587
- return { ...user, balance_mc: balance };
16556
+ return user;
16588
16557
  }
16589
16558
  async function runMonthlyRefreshSweep() {
16590
- const db = getDb();
16591
- const monthStart = /* @__PURE__ */ new Date();
16592
- monthStart.setUTCDate(1);
16593
- monthStart.setUTCHours(0, 0, 0, 0);
16594
- const monthStartIso = monthStart.toISOString();
16595
- const currentMonth = monthKey();
16596
- const res = await db.execute({
16597
- sql: `
16598
- SELECT * FROM users
16599
- WHERE created_at < ?
16600
- AND active = 1
16601
- AND id NOT IN (
16602
- SELECT user_id FROM free_credit_refreshes WHERE month = ?
16603
- )
16604
- `,
16605
- args: [monthStartIso, currentMonth]
16606
- });
16607
- const users = res.rows.map((r) => ({
16608
- id: Number(r.id),
16609
- email: String(r.email),
16610
- name: r.name != null ? String(r.name) : null,
16611
- api_key: String(r.api_key ?? ""),
16612
- key_active: Number(r.key_active ?? 1),
16613
- password_hash: r.password_hash != null ? String(r.password_hash) : null,
16614
- created_at: String(r.created_at),
16615
- active: Number(r.active),
16616
- stripe_customer_id: r.stripe_customer_id != null ? String(r.stripe_customer_id) : null,
16617
- balance_mc: Number(r.balance_mc ?? 0),
16618
- extra_concurrency_slots: Number(r.extra_concurrency_slots ?? 0),
16619
- concurrency_stripe_sub_id: r.concurrency_stripe_sub_id != null ? String(r.concurrency_stripe_sub_id) : null
16620
- }));
16621
- let usersRefreshed = 0;
16622
- let totalMcGranted = 0;
16623
- for (const user of users) {
16624
- const before = user.balance_mc;
16625
- const after = await applyMonthlyFreeRefresh(user);
16626
- if (after.balance_mc !== before) {
16627
- usersRefreshed++;
16628
- totalMcGranted += FREE_MONTHLY_REFRESH_MC;
16629
- }
16630
- }
16631
- return { usersRefreshed, totalMcGranted };
16559
+ return { usersRefreshed: 0, totalMcGranted: 0 };
16632
16560
  }
16633
16561
  async function getFreeCreditBreakdown(userId) {
16634
16562
  const res = await getDb().execute({
@@ -16932,8 +16860,10 @@ app.get("/me", async (c) => {
16932
16860
  balance_mc: user.balance_mc,
16933
16861
  balance_credits: user.balance_mc / 1e3,
16934
16862
  extra_concurrency_slots: user.extra_concurrency_slots,
16935
- concurrency_limit: 1 + user.extra_concurrency_slots,
16863
+ concurrency_limit: concurrencyLimitForUser(user),
16936
16864
  has_concurrency_sub: !!user.concurrency_stripe_sub_id,
16865
+ subscription_tier: user.subscription_tier,
16866
+ subscription_concurrency: user.subscription_concurrency,
16937
16867
  ...stats
16938
16868
  });
16939
16869
  });
@@ -17398,13 +17328,12 @@ app.get("/extract-site/status/:id", auth2, async (c) => {
17398
17328
  });
17399
17329
  });
17400
17330
  app.post("/billing/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
17331
+ return c.json({ error: "One-time credit packs are no longer available. Subscribe to a plan instead \u2014 use /billing/subscribe.", error_code: "packs_removed" }, 410);
17332
+ });
17333
+ app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
17401
17334
  try {
17402
17335
  const user = c.get("sessionUser");
17403
- const raw = await c.req.json();
17404
- const parsed = BillingCheckoutBodySchema.safeParse(raw);
17405
- if (!parsed.success) return c.json({ error: "Invalid request" }, 400);
17406
- const { priceId } = parsed.data;
17407
- if (!(priceId in BALANCE_PRICE_IDS)) return c.json({ error: "Invalid price" }, 400);
17336
+ if (user.concurrency_stripe_sub_id) return c.json({ error: "Already subscribed \u2014 cancel existing slot first to change." }, 409);
17408
17337
  const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
17409
17338
  if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
17410
17339
  const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
@@ -17416,26 +17345,28 @@ app.post("/billing/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
17416
17345
  }
17417
17346
  const session = await stripeClient.checkout.sessions.create({
17418
17347
  customer: customerId,
17419
- mode: "payment",
17420
- line_items: [{ price: priceId, quantity: 1 }],
17348
+ mode: "subscription",
17349
+ line_items: [{ price: CONCURRENCY_PRICE_ID, quantity: 1 }],
17421
17350
  ui_mode: "embedded",
17422
17351
  automatic_tax: { enabled: true },
17423
17352
  billing_address_collection: "required",
17424
17353
  customer_update: { address: "auto", name: "auto" },
17425
17354
  tax_id_collection: { enabled: true },
17426
- return_url: `${appOrigin()}/billing?session_id={CHECKOUT_SESSION_ID}`
17355
+ return_url: `${appOrigin()}/billing?slot_added=1`
17427
17356
  });
17428
17357
  return c.json({ clientSecret: session.client_secret });
17429
17358
  } catch (err) {
17430
17359
  const message = err instanceof Error ? err.message : "Unable to start checkout.";
17431
- console.error("[billing/checkout]", message);
17360
+ console.error("[billing/concurrency/checkout]", message);
17432
17361
  return c.json({ error: message }, 500);
17433
17362
  }
17434
17363
  });
17435
- app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
17364
+ app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
17436
17365
  try {
17437
17366
  const user = c.get("sessionUser");
17438
- if (user.concurrency_stripe_sub_id) return c.json({ error: "Already subscribed \u2014 cancel existing slot first to change." }, 409);
17367
+ const raw = await c.req.json().catch(() => ({}));
17368
+ const tier = SUBSCRIPTION_TIER_BY_KEY[String(raw.tier ?? "")];
17369
+ if (!tier) return c.json({ error: "Invalid tier. Choose starter, growth, or scale." }, 400);
17439
17370
  const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
17440
17371
  if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
17441
17372
  const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
@@ -17445,21 +17376,33 @@ app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, asy
17445
17376
  customerId = customer.id;
17446
17377
  await setStripeCustomerId(user.id, customerId);
17447
17378
  }
17379
+ if (user.subscription_id) {
17380
+ const sub = await stripeClient.subscriptions.retrieve(user.subscription_id);
17381
+ const itemId = sub.items.data[0]?.id;
17382
+ if (itemId) {
17383
+ await stripeClient.subscriptions.update(user.subscription_id, {
17384
+ items: [{ id: itemId, price: tier.price_id }],
17385
+ proration_behavior: "create_prorations"
17386
+ });
17387
+ return c.json({ updated: true, tier: tier.tier });
17388
+ }
17389
+ }
17448
17390
  const session = await stripeClient.checkout.sessions.create({
17449
17391
  customer: customerId,
17450
17392
  mode: "subscription",
17451
- line_items: [{ price: CONCURRENCY_PRICE_ID, quantity: 1 }],
17393
+ line_items: [{ price: tier.price_id, quantity: 1 }],
17394
+ ...tier.intro_coupon ? { discounts: [{ coupon: tier.intro_coupon }] } : {},
17452
17395
  ui_mode: "embedded",
17453
17396
  automatic_tax: { enabled: true },
17454
17397
  billing_address_collection: "required",
17455
17398
  customer_update: { address: "auto", name: "auto" },
17456
17399
  tax_id_collection: { enabled: true },
17457
- return_url: `${appOrigin()}/billing?slot_added=1`
17400
+ return_url: `${appOrigin()}/billing?subscribed=${tier.tier}`
17458
17401
  });
17459
17402
  return c.json({ clientSecret: session.client_secret });
17460
17403
  } catch (err) {
17461
- const message = err instanceof Error ? err.message : "Unable to start checkout.";
17462
- console.error("[billing/concurrency/checkout]", message);
17404
+ const message = err instanceof Error ? err.message : "Unable to start subscription.";
17405
+ console.error("[billing/subscribe]", message);
17463
17406
  return c.json({ error: message }, 500);
17464
17407
  }
17465
17408
  });
@@ -17507,7 +17450,7 @@ app.post("/billing/concurrency/terminal-checkout", auth2, async (c) => {
17507
17450
  current_extra_slots: user.extra_concurrency_slots,
17508
17451
  current_limit: concurrencyLimitForUser(user),
17509
17452
  after_checkout_extra_slots: user.extra_concurrency_slots + 1,
17510
- after_checkout_limit: concurrencyLimitForUser({ extra_concurrency_slots: user.extra_concurrency_slots + 1 })
17453
+ after_checkout_limit: concurrencyLimitForUser({ extra_concurrency_slots: user.extra_concurrency_slots + 1, subscription_concurrency: user.subscription_concurrency })
17511
17454
  },
17512
17455
  next_step: "Open checkout_url in a browser, complete checkout, then restart or retry the MCP request."
17513
17456
  });
@@ -17576,15 +17519,16 @@ app.get("/cron/tick", async (c) => {
17576
17519
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
17577
17520
  return c.json({ error: "Unauthorized" }, 401);
17578
17521
  }
17579
- const { drainQueue } = await import("./worker-YZ2ZJE4F.js");
17522
+ const { drainQueue } = await import("./worker-AITQTXHM.js");
17580
17523
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
17581
17524
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
17582
- const [results, sweepResult, reapResult] = await Promise.all([
17525
+ const [results, sweepResult, reapResult, expiredResult] = await Promise.all([
17583
17526
  drainQueue(budget),
17584
17527
  runMonthlyRefreshSweep(),
17585
- reapIdleBrowserSessions(120).catch(() => ({ reaped: 0 }))
17528
+ reapIdleBrowserSessions(120).catch(() => ({ reaped: 0 })),
17529
+ expireOldLots().catch(() => ({ expired_lots: 0, expired_mc: 0 }))
17586
17530
  ]);
17587
- return c.json({ drained: results.length, results, sweepResult, reaped: reapResult, workflowDispatch: workflowDispatchResult });
17531
+ return c.json({ drained: results.length, results, sweepResult, reaped: reapResult, expired: expiredResult, workflowDispatch: workflowDispatchResult });
17588
17532
  });
17589
17533
  app.on(["GET", "POST", "PUT"], "/api/inngest", serveInngest({ client: inngest, functions: [siteAuditFn, siteExtractFn] }));
17590
17534
  app.route("/api/internal/site-architecture-auditor", siteAuditApp);
@@ -17720,4 +17664,4 @@ app.get("/blog/:slug/", (c) => {
17720
17664
  export {
17721
17665
  app
17722
17666
  };
17723
- //# sourceMappingURL=server-EYNMXWUJ.js.map
17667
+ //# sourceMappingURL=server-K4UIE2R3.js.map