mcp-scraper 0.3.20 → 0.3.22
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.
- package/dist/bin/api-server.cjs +163 -155
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +18 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +18 -1
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +1 -1
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/chunk-2IXMOXDH.js +7 -0
- package/dist/chunk-2IXMOXDH.js.map +1 -0
- package/dist/{chunk-IORA5BFI.js → chunk-3KYRG7O7.js} +24 -2
- package/dist/chunk-3KYRG7O7.js.map +1 -0
- package/dist/{chunk-YZAYOFJV.js → chunk-7PQFU7TV.js} +13 -20
- package/dist/chunk-7PQFU7TV.js.map +1 -0
- package/dist/{chunk-6N6USFMH.js → chunk-FEUCMGIJ.js} +2 -2
- package/dist/{db-ZWV4Y43C.js → db-EG5ETPTY.js} +4 -2
- package/dist/{server-NKZW2SEV.js → server-G7PVVOH3.js} +138 -141
- package/dist/server-G7PVVOH3.js.map +1 -0
- package/dist/{worker-PR7GODIV.js → worker-AITQTXHM.js} +3 -3
- package/docs/mcp-tool-manifest.generated.json +1 -1
- package/package.json +1 -1
- package/dist/chunk-IORA5BFI.js.map +0 -1
- package/dist/chunk-XLHBBA3U.js +0 -7
- package/dist/chunk-XLHBBA3U.js.map +0 -1
- package/dist/chunk-YZAYOFJV.js.map +0 -1
- package/dist/server-NKZW2SEV.js.map +0 -1
- /package/dist/{chunk-6N6USFMH.js.map → chunk-FEUCMGIJ.js.map} +0 -0
- /package/dist/{db-ZWV4Y43C.js.map → db-EG5ETPTY.js.map} +0 -0
- /package/dist/{worker-PR7GODIV.js.map → worker-AITQTXHM.js.map} +0 -0
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
renderLinkReport,
|
|
22
22
|
sanitizeAttempts,
|
|
23
23
|
sanitizeHarvestResult
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-FEUCMGIJ.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-
|
|
36
|
+
import "./chunk-2IXMOXDH.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-
|
|
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,
|
|
@@ -140,8 +137,9 @@ import {
|
|
|
140
137
|
setExtraConcurrencySlots,
|
|
141
138
|
setPassword,
|
|
142
139
|
setStripeCustomerId,
|
|
140
|
+
setSubscriptionTier,
|
|
143
141
|
verifyPassword
|
|
144
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-3KYRG7O7.js";
|
|
145
143
|
|
|
146
144
|
// src/blog/registry.ts
|
|
147
145
|
var posts = [
|
|
@@ -9045,7 +9043,8 @@ var DEFAULT_RETRY_AFTER_SECONDS = 30;
|
|
|
9045
9043
|
var MAX_LOCK_TTL_SECONDS = 24 * 60 * 60;
|
|
9046
9044
|
var schemaReady = null;
|
|
9047
9045
|
function concurrencyLimitForUser(user) {
|
|
9048
|
-
|
|
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));
|
|
9049
9048
|
}
|
|
9050
9049
|
function concurrencyLimitExceededResponse(gate) {
|
|
9051
9050
|
const upgrade = concurrencySlotBillingInfo();
|
|
@@ -16458,49 +16457,58 @@ stripeApp.post("/webhooks", async (c) => {
|
|
|
16458
16457
|
}
|
|
16459
16458
|
const isNew = await recordStripeEvent(event.id);
|
|
16460
16459
|
if (!isNew) return c.json({ received: true });
|
|
16461
|
-
if (event.type === "
|
|
16462
|
-
const
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
const label = BALANCE_PACK_LABELS[priceId] ?? "unknown";
|
|
16471
|
-
let user = await getUserByStripeCustomerId(customerId);
|
|
16472
|
-
if (!user) {
|
|
16473
|
-
const email = session.customer_details?.email;
|
|
16474
|
-
if (!email) return c.json({ received: true });
|
|
16475
|
-
user = await getUserByEmail(email);
|
|
16476
|
-
if (!user) return c.json({ received: true });
|
|
16477
|
-
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);
|
|
16478
16469
|
}
|
|
16479
|
-
const pi = session.payment_intent;
|
|
16480
|
-
if (await ledgerExistsForStripePI(pi)) return c.json({ received: true });
|
|
16481
|
-
await creditMc(user.id, mc, LedgerOperation.TOPUP, `${label} balance top-up`, pi);
|
|
16482
16470
|
}
|
|
16483
16471
|
}
|
|
16484
|
-
if (event.type === "customer.subscription.created") {
|
|
16472
|
+
if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated") {
|
|
16485
16473
|
const sub = event.data.object;
|
|
16486
16474
|
const priceId = sub.items.data[0]?.price?.id;
|
|
16487
|
-
if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
|
|
16488
16475
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
16489
16476
|
if (!user) return c.json({ received: true });
|
|
16490
|
-
|
|
16491
|
-
|
|
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
|
+
}
|
|
16492
16485
|
}
|
|
16493
16486
|
if (event.type === "customer.subscription.deleted") {
|
|
16494
16487
|
const sub = event.data.object;
|
|
16495
16488
|
const priceId = sub.items.data[0]?.price?.id;
|
|
16496
|
-
if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
|
|
16497
16489
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
16498
16490
|
if (!user) return c.json({ received: true });
|
|
16499
|
-
|
|
16500
|
-
|
|
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
|
+
}
|
|
16501
16497
|
}
|
|
16502
16498
|
return c.json({ received: true });
|
|
16503
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
|
+
}
|
|
16504
16512
|
|
|
16505
16513
|
// src/api/site-audit-worker.ts
|
|
16506
16514
|
var MAX_CONCURRENT_SITE_AUDIT = 1;
|
|
@@ -16540,96 +16548,15 @@ import { createMiddleware as createMiddleware3 } from "hono/factory";
|
|
|
16540
16548
|
import { getCookie, setCookie, deleteCookie } from "hono/cookie";
|
|
16541
16549
|
import Stripe2 from "stripe";
|
|
16542
16550
|
|
|
16543
|
-
// src/api/billing-schemas.ts
|
|
16544
|
-
import { z as z19 } from "zod";
|
|
16545
|
-
var BillingCheckoutBodySchema = z19.object({
|
|
16546
|
-
priceId: z19.string().min(1)
|
|
16547
|
-
});
|
|
16548
|
-
var FreeCreditBreakdownSchema = z19.object({
|
|
16549
|
-
signup_grant_mc: z19.number().int().nonnegative(),
|
|
16550
|
-
monthly_refresh_mc: z19.number().int().nonnegative(),
|
|
16551
|
-
total_free_mc: z19.number().int().nonnegative(),
|
|
16552
|
-
signup_grant_credits: z19.number().nonnegative(),
|
|
16553
|
-
monthly_refresh_credits: z19.number().nonnegative(),
|
|
16554
|
-
total_free_credits: z19.number().nonnegative()
|
|
16555
|
-
});
|
|
16556
|
-
var BillingBalanceResponseSchema = z19.object({
|
|
16557
|
-
balance_mc: z19.number().int().nonnegative(),
|
|
16558
|
-
balance_credits: z19.number().nonnegative(),
|
|
16559
|
-
free_credits: FreeCreditBreakdownSchema,
|
|
16560
|
-
ledger: z19.array(z19.any())
|
|
16561
|
-
});
|
|
16562
|
-
var MonthlyRefreshSweepResultSchema = z19.object({
|
|
16563
|
-
usersRefreshed: z19.number().int().nonnegative(),
|
|
16564
|
-
totalMcGranted: z19.number().int().nonnegative()
|
|
16565
|
-
});
|
|
16566
|
-
|
|
16567
16551
|
// src/api/credit-operations.ts
|
|
16568
|
-
function
|
|
16569
|
-
return
|
|
16570
|
-
}
|
|
16571
|
-
function userCreatedBeforeThisMonth(user) {
|
|
16572
|
-
return user.created_at.slice(0, 7) < monthKey();
|
|
16573
|
-
}
|
|
16574
|
-
async function grantSignupCredit(userId) {
|
|
16575
|
-
if (await ledgerExistsForOperation(userId, LedgerOperation.SIGNUP_GRANT)) return;
|
|
16576
|
-
await creditMc(userId, FREE_SIGNUP_MC, LedgerOperation.SIGNUP_GRANT, "Welcome to MCP Scraper");
|
|
16552
|
+
async function grantSignupCredit(_userId) {
|
|
16553
|
+
return;
|
|
16577
16554
|
}
|
|
16578
16555
|
async function applyMonthlyFreeRefresh(user) {
|
|
16579
|
-
|
|
16580
|
-
const claimed = await claimMonthlyFreeRefresh(user.id, monthKey());
|
|
16581
|
-
if (!claimed) return user;
|
|
16582
|
-
const balance = await creditMc(
|
|
16583
|
-
user.id,
|
|
16584
|
-
FREE_MONTHLY_REFRESH_MC,
|
|
16585
|
-
LedgerOperation.MONTHLY_REFRESH,
|
|
16586
|
-
"Monthly free credits"
|
|
16587
|
-
);
|
|
16588
|
-
return { ...user, balance_mc: balance };
|
|
16556
|
+
return user;
|
|
16589
16557
|
}
|
|
16590
16558
|
async function runMonthlyRefreshSweep() {
|
|
16591
|
-
|
|
16592
|
-
const monthStart = /* @__PURE__ */ new Date();
|
|
16593
|
-
monthStart.setUTCDate(1);
|
|
16594
|
-
monthStart.setUTCHours(0, 0, 0, 0);
|
|
16595
|
-
const monthStartIso = monthStart.toISOString();
|
|
16596
|
-
const currentMonth = monthKey();
|
|
16597
|
-
const res = await db.execute({
|
|
16598
|
-
sql: `
|
|
16599
|
-
SELECT * FROM users
|
|
16600
|
-
WHERE created_at < ?
|
|
16601
|
-
AND active = 1
|
|
16602
|
-
AND id NOT IN (
|
|
16603
|
-
SELECT user_id FROM free_credit_refreshes WHERE month = ?
|
|
16604
|
-
)
|
|
16605
|
-
`,
|
|
16606
|
-
args: [monthStartIso, currentMonth]
|
|
16607
|
-
});
|
|
16608
|
-
const users = res.rows.map((r) => ({
|
|
16609
|
-
id: Number(r.id),
|
|
16610
|
-
email: String(r.email),
|
|
16611
|
-
name: r.name != null ? String(r.name) : null,
|
|
16612
|
-
api_key: String(r.api_key ?? ""),
|
|
16613
|
-
key_active: Number(r.key_active ?? 1),
|
|
16614
|
-
password_hash: r.password_hash != null ? String(r.password_hash) : null,
|
|
16615
|
-
created_at: String(r.created_at),
|
|
16616
|
-
active: Number(r.active),
|
|
16617
|
-
stripe_customer_id: r.stripe_customer_id != null ? String(r.stripe_customer_id) : null,
|
|
16618
|
-
balance_mc: Number(r.balance_mc ?? 0),
|
|
16619
|
-
extra_concurrency_slots: Number(r.extra_concurrency_slots ?? 0),
|
|
16620
|
-
concurrency_stripe_sub_id: r.concurrency_stripe_sub_id != null ? String(r.concurrency_stripe_sub_id) : null
|
|
16621
|
-
}));
|
|
16622
|
-
let usersRefreshed = 0;
|
|
16623
|
-
let totalMcGranted = 0;
|
|
16624
|
-
for (const user of users) {
|
|
16625
|
-
const before = user.balance_mc;
|
|
16626
|
-
const after = await applyMonthlyFreeRefresh(user);
|
|
16627
|
-
if (after.balance_mc !== before) {
|
|
16628
|
-
usersRefreshed++;
|
|
16629
|
-
totalMcGranted += FREE_MONTHLY_REFRESH_MC;
|
|
16630
|
-
}
|
|
16631
|
-
}
|
|
16632
|
-
return { usersRefreshed, totalMcGranted };
|
|
16559
|
+
return { usersRefreshed: 0, totalMcGranted: 0 };
|
|
16633
16560
|
}
|
|
16634
16561
|
async function getFreeCreditBreakdown(userId) {
|
|
16635
16562
|
const res = await getDb().execute({
|
|
@@ -16933,8 +16860,10 @@ app.get("/me", async (c) => {
|
|
|
16933
16860
|
balance_mc: user.balance_mc,
|
|
16934
16861
|
balance_credits: user.balance_mc / 1e3,
|
|
16935
16862
|
extra_concurrency_slots: user.extra_concurrency_slots,
|
|
16936
|
-
concurrency_limit:
|
|
16863
|
+
concurrency_limit: concurrencyLimitForUser(user),
|
|
16937
16864
|
has_concurrency_sub: !!user.concurrency_stripe_sub_id,
|
|
16865
|
+
subscription_tier: user.subscription_tier,
|
|
16866
|
+
subscription_concurrency: user.subscription_concurrency,
|
|
16938
16867
|
...stats
|
|
16939
16868
|
});
|
|
16940
16869
|
});
|
|
@@ -17399,13 +17328,12 @@ app.get("/extract-site/status/:id", auth2, async (c) => {
|
|
|
17399
17328
|
});
|
|
17400
17329
|
});
|
|
17401
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) => {
|
|
17402
17334
|
try {
|
|
17403
17335
|
const user = c.get("sessionUser");
|
|
17404
|
-
|
|
17405
|
-
const parsed = BillingCheckoutBodySchema.safeParse(raw);
|
|
17406
|
-
if (!parsed.success) return c.json({ error: "Invalid request" }, 400);
|
|
17407
|
-
const { priceId } = parsed.data;
|
|
17408
|
-
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);
|
|
17409
17337
|
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
17410
17338
|
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
17411
17339
|
const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
@@ -17417,26 +17345,28 @@ app.post("/billing/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
|
17417
17345
|
}
|
|
17418
17346
|
const session = await stripeClient.checkout.sessions.create({
|
|
17419
17347
|
customer: customerId,
|
|
17420
|
-
mode: "
|
|
17421
|
-
line_items: [{ price:
|
|
17348
|
+
mode: "subscription",
|
|
17349
|
+
line_items: [{ price: CONCURRENCY_PRICE_ID, quantity: 1 }],
|
|
17422
17350
|
ui_mode: "embedded",
|
|
17423
17351
|
automatic_tax: { enabled: true },
|
|
17424
17352
|
billing_address_collection: "required",
|
|
17425
17353
|
customer_update: { address: "auto", name: "auto" },
|
|
17426
17354
|
tax_id_collection: { enabled: true },
|
|
17427
|
-
return_url: `${appOrigin()}/billing?
|
|
17355
|
+
return_url: `${appOrigin()}/billing?slot_added=1`
|
|
17428
17356
|
});
|
|
17429
17357
|
return c.json({ clientSecret: session.client_secret });
|
|
17430
17358
|
} catch (err) {
|
|
17431
17359
|
const message = err instanceof Error ? err.message : "Unable to start checkout.";
|
|
17432
|
-
console.error("[billing/checkout]", message);
|
|
17360
|
+
console.error("[billing/concurrency/checkout]", message);
|
|
17433
17361
|
return c.json({ error: message }, 500);
|
|
17434
17362
|
}
|
|
17435
17363
|
});
|
|
17436
|
-
app.post("/billing/
|
|
17364
|
+
app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
17437
17365
|
try {
|
|
17438
17366
|
const user = c.get("sessionUser");
|
|
17439
|
-
|
|
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);
|
|
17440
17370
|
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
17441
17371
|
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
17442
17372
|
const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
@@ -17446,21 +17376,33 @@ app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, asy
|
|
|
17446
17376
|
customerId = customer.id;
|
|
17447
17377
|
await setStripeCustomerId(user.id, customerId);
|
|
17448
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
|
+
}
|
|
17449
17390
|
const session = await stripeClient.checkout.sessions.create({
|
|
17450
17391
|
customer: customerId,
|
|
17451
17392
|
mode: "subscription",
|
|
17452
|
-
line_items: [{ price:
|
|
17393
|
+
line_items: [{ price: tier.price_id, quantity: 1 }],
|
|
17394
|
+
...tier.intro_coupon ? { discounts: [{ coupon: tier.intro_coupon }] } : {},
|
|
17453
17395
|
ui_mode: "embedded",
|
|
17454
17396
|
automatic_tax: { enabled: true },
|
|
17455
17397
|
billing_address_collection: "required",
|
|
17456
17398
|
customer_update: { address: "auto", name: "auto" },
|
|
17457
17399
|
tax_id_collection: { enabled: true },
|
|
17458
|
-
return_url: `${appOrigin()}/billing?
|
|
17400
|
+
return_url: `${appOrigin()}/billing?subscribed=${tier.tier}`
|
|
17459
17401
|
});
|
|
17460
17402
|
return c.json({ clientSecret: session.client_secret });
|
|
17461
17403
|
} catch (err) {
|
|
17462
|
-
const message = err instanceof Error ? err.message : "Unable to start
|
|
17463
|
-
console.error("[billing/
|
|
17404
|
+
const message = err instanceof Error ? err.message : "Unable to start subscription.";
|
|
17405
|
+
console.error("[billing/subscribe]", message);
|
|
17464
17406
|
return c.json({ error: message }, 500);
|
|
17465
17407
|
}
|
|
17466
17408
|
});
|
|
@@ -17508,7 +17450,7 @@ app.post("/billing/concurrency/terminal-checkout", auth2, async (c) => {
|
|
|
17508
17450
|
current_extra_slots: user.extra_concurrency_slots,
|
|
17509
17451
|
current_limit: concurrencyLimitForUser(user),
|
|
17510
17452
|
after_checkout_extra_slots: user.extra_concurrency_slots + 1,
|
|
17511
|
-
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 })
|
|
17512
17454
|
},
|
|
17513
17455
|
next_step: "Open checkout_url in a browser, complete checkout, then restart or retry the MCP request."
|
|
17514
17456
|
});
|
|
@@ -17518,6 +17460,61 @@ app.post("/billing/concurrency/terminal-checkout", auth2, async (c) => {
|
|
|
17518
17460
|
return c.json({ error: message }, 500);
|
|
17519
17461
|
}
|
|
17520
17462
|
});
|
|
17463
|
+
app.post("/billing/subscribe/terminal-checkout", auth2, async (c) => {
|
|
17464
|
+
try {
|
|
17465
|
+
const user = c.get("user");
|
|
17466
|
+
const raw = await c.req.json().catch(() => ({}));
|
|
17467
|
+
const tier = SUBSCRIPTION_TIER_BY_KEY[String(raw.tier ?? "")];
|
|
17468
|
+
if (!tier) return c.json({ error: "Invalid tier. Choose starter, growth, or scale.", tiers: Object.keys(SUBSCRIPTION_TIER_BY_KEY) }, 400);
|
|
17469
|
+
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
17470
|
+
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
17471
|
+
const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
17472
|
+
let customerId = user.stripe_customer_id;
|
|
17473
|
+
if (!customerId) {
|
|
17474
|
+
const customer = await stripeClient.customers.create({ email: user.email });
|
|
17475
|
+
customerId = customer.id;
|
|
17476
|
+
await setStripeCustomerId(user.id, customerId);
|
|
17477
|
+
}
|
|
17478
|
+
if (user.subscription_id) {
|
|
17479
|
+
const sub = await stripeClient.subscriptions.retrieve(user.subscription_id);
|
|
17480
|
+
const itemId = sub.items.data[0]?.id;
|
|
17481
|
+
if (itemId) {
|
|
17482
|
+
await stripeClient.subscriptions.update(user.subscription_id, {
|
|
17483
|
+
items: [{ id: itemId, price: tier.price_id }],
|
|
17484
|
+
proration_behavior: "create_prorations"
|
|
17485
|
+
});
|
|
17486
|
+
return c.json({ updated: true, tier: tier.tier, message: `Switched to ${tier.label} (prorated). Credits and concurrency update on the next invoice.` });
|
|
17487
|
+
}
|
|
17488
|
+
}
|
|
17489
|
+
const session = await stripeClient.checkout.sessions.create({
|
|
17490
|
+
customer: customerId,
|
|
17491
|
+
mode: "subscription",
|
|
17492
|
+
line_items: [{ price: tier.price_id, quantity: 1 }],
|
|
17493
|
+
...tier.intro_coupon ? { discounts: [{ coupon: tier.intro_coupon }] } : {},
|
|
17494
|
+
automatic_tax: { enabled: true },
|
|
17495
|
+
billing_address_collection: "required",
|
|
17496
|
+
customer_update: { address: "auto", name: "auto" },
|
|
17497
|
+
tax_id_collection: { enabled: true },
|
|
17498
|
+
success_url: `${appOrigin()}/billing?subscribed=${tier.tier}&source=terminal`,
|
|
17499
|
+
cancel_url: `${appOrigin()}/billing?subscribe_cancelled=1&source=terminal`
|
|
17500
|
+
});
|
|
17501
|
+
if (!session.url) return c.json({ error: "Stripe did not return a checkout URL." }, 502);
|
|
17502
|
+
return c.json({
|
|
17503
|
+
checkout_url: session.url,
|
|
17504
|
+
tier: tier.tier,
|
|
17505
|
+
label: tier.label,
|
|
17506
|
+
monthly_usd: tier.monthly_usd,
|
|
17507
|
+
credits_per_month: tier.credits_mc / 1e3,
|
|
17508
|
+
concurrency: tier.concurrency,
|
|
17509
|
+
intro: tier.intro_coupon ? "$1 first month" : null,
|
|
17510
|
+
next_step: "Open checkout_url in a browser to complete payment."
|
|
17511
|
+
});
|
|
17512
|
+
} catch (err) {
|
|
17513
|
+
const message = err instanceof Error ? err.message : "Unable to start subscription checkout.";
|
|
17514
|
+
console.error("[billing/subscribe/terminal-checkout]", message);
|
|
17515
|
+
return c.json({ error: message }, 500);
|
|
17516
|
+
}
|
|
17517
|
+
});
|
|
17521
17518
|
app.post("/billing/concurrency/cancel", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
17522
17519
|
const user = c.get("sessionUser");
|
|
17523
17520
|
if (!user.concurrency_stripe_sub_id) return c.json({ error: "No active concurrency subscription." }, 404);
|
|
@@ -17577,7 +17574,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
17577
17574
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
17578
17575
|
return c.json({ error: "Unauthorized" }, 401);
|
|
17579
17576
|
}
|
|
17580
|
-
const { drainQueue } = await import("./worker-
|
|
17577
|
+
const { drainQueue } = await import("./worker-AITQTXHM.js");
|
|
17581
17578
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
17582
17579
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
17583
17580
|
const [results, sweepResult, reapResult, expiredResult] = await Promise.all([
|
|
@@ -17722,4 +17719,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
17722
17719
|
export {
|
|
17723
17720
|
app
|
|
17724
17721
|
};
|
|
17725
|
-
//# sourceMappingURL=server-
|
|
17722
|
+
//# sourceMappingURL=server-G7PVVOH3.js.map
|