mcp-scraper 0.3.20 → 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.
- package/dist/bin/api-server.cjs +108 -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 +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +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-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-J4PSMJNZ.js +7 -0
- package/dist/chunk-J4PSMJNZ.js.map +1 -0
- package/dist/{chunk-6N6USFMH.js → chunk-UO252634.js} +2 -2
- package/dist/{db-ZWV4Y43C.js → db-EG5ETPTY.js} +4 -2
- package/dist/{server-NKZW2SEV.js → server-K4UIE2R3.js} +83 -141
- package/dist/server-K4UIE2R3.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-UO252634.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
package/dist/bin/api-server.cjs
CHANGED
|
@@ -7400,6 +7400,7 @@ __export(db_exports, {
|
|
|
7400
7400
|
setExtraConcurrencySlots: () => setExtraConcurrencySlots,
|
|
7401
7401
|
setPassword: () => setPassword,
|
|
7402
7402
|
setStripeCustomerId: () => setStripeCustomerId,
|
|
7403
|
+
setSubscriptionTier: () => setSubscriptionTier,
|
|
7403
7404
|
startHarvestAttempt: () => startHarvestAttempt,
|
|
7404
7405
|
stripeEventAlreadyProcessed: () => stripeEventAlreadyProcessed,
|
|
7405
7406
|
updateKpoJobState: () => updateKpoJobState,
|
|
@@ -7527,6 +7528,18 @@ async function migrate() {
|
|
|
7527
7528
|
await db.execute(`ALTER TABLE users ADD COLUMN concurrency_stripe_sub_id TEXT`);
|
|
7528
7529
|
} catch {
|
|
7529
7530
|
}
|
|
7531
|
+
try {
|
|
7532
|
+
await db.execute(`ALTER TABLE users ADD COLUMN subscription_concurrency INTEGER NOT NULL DEFAULT 0`);
|
|
7533
|
+
} catch {
|
|
7534
|
+
}
|
|
7535
|
+
try {
|
|
7536
|
+
await db.execute(`ALTER TABLE users ADD COLUMN subscription_tier TEXT`);
|
|
7537
|
+
} catch {
|
|
7538
|
+
}
|
|
7539
|
+
try {
|
|
7540
|
+
await db.execute(`ALTER TABLE users ADD COLUMN subscription_id TEXT`);
|
|
7541
|
+
} catch {
|
|
7542
|
+
}
|
|
7530
7543
|
await db.execute(`
|
|
7531
7544
|
CREATE TABLE IF NOT EXISTS concurrency_locks (
|
|
7532
7545
|
id TEXT PRIMARY KEY,
|
|
@@ -7809,7 +7822,10 @@ function rowToUser(row) {
|
|
|
7809
7822
|
stripe_customer_id: row.stripe_customer_id != null ? String(row.stripe_customer_id) : null,
|
|
7810
7823
|
balance_mc: Number(row.balance_mc ?? 0),
|
|
7811
7824
|
extra_concurrency_slots: Number(row.extra_concurrency_slots ?? 0),
|
|
7812
|
-
concurrency_stripe_sub_id: row.concurrency_stripe_sub_id != null ? String(row.concurrency_stripe_sub_id) : null
|
|
7825
|
+
concurrency_stripe_sub_id: row.concurrency_stripe_sub_id != null ? String(row.concurrency_stripe_sub_id) : null,
|
|
7826
|
+
subscription_concurrency: Number(row.subscription_concurrency ?? 0),
|
|
7827
|
+
subscription_tier: row.subscription_tier != null ? String(row.subscription_tier) : null,
|
|
7828
|
+
subscription_id: row.subscription_id != null ? String(row.subscription_id) : null
|
|
7813
7829
|
};
|
|
7814
7830
|
}
|
|
7815
7831
|
async function getUserByEmail(email) {
|
|
@@ -8594,6 +8610,12 @@ async function setConcurrencySubId(userId, subId) {
|
|
|
8594
8610
|
args: [subId, userId]
|
|
8595
8611
|
});
|
|
8596
8612
|
}
|
|
8613
|
+
async function setSubscriptionTier(userId, tier, concurrency, subId) {
|
|
8614
|
+
await getDb().execute({
|
|
8615
|
+
sql: "UPDATE users SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ? WHERE id = ?",
|
|
8616
|
+
args: [tier, Math.max(0, Math.round(concurrency)), subId, userId]
|
|
8617
|
+
});
|
|
8618
|
+
}
|
|
8597
8619
|
async function ensureMigrated(userId) {
|
|
8598
8620
|
const db = getDb();
|
|
8599
8621
|
const hasLots = await db.execute({ sql: "SELECT 1 FROM credit_lots WHERE user_id = ? LIMIT 1", args: [userId] });
|
|
@@ -9613,7 +9635,7 @@ function insufficientBalanceResponse(balanceMc, requiredMc) {
|
|
|
9613
9635
|
topup_url: topupUrl
|
|
9614
9636
|
};
|
|
9615
9637
|
}
|
|
9616
|
-
var MC_COSTS, MC_PER_BROWSER_MS, BROWSER_OPEN_MIN_BALANCE_MC, MC_PER_CREDIT, CREDIT_COST_CATALOG, CONCURRENCY_PRICE_ID, CONCURRENCY_SLOT_PRICE_USD, CONCURRENCY_SLOT_PRICE_CURRENCY, CONCURRENCY_SLOT_PRICE_INTERVAL, CONCURRENCY_SLOT_PRICE_LABEL, CONCURRENCY_SLOT_TERMINAL_COMMAND,
|
|
9638
|
+
var MC_COSTS, MC_PER_BROWSER_MS, BROWSER_OPEN_MIN_BALANCE_MC, MC_PER_CREDIT, CREDIT_COST_CATALOG, CONCURRENCY_PRICE_ID, SUBSCRIPTION_TIERS, SUBSCRIPTION_TIER_BY_KEY, CONCURRENCY_SLOT_PRICE_USD, CONCURRENCY_SLOT_PRICE_CURRENCY, CONCURRENCY_SLOT_PRICE_INTERVAL, CONCURRENCY_SLOT_PRICE_LABEL, CONCURRENCY_SLOT_TERMINAL_COMMAND, LedgerOperation;
|
|
9617
9639
|
var init_rates = __esm({
|
|
9618
9640
|
"src/api/rates.ts"() {
|
|
9619
9641
|
"use strict";
|
|
@@ -9767,27 +9789,22 @@ var init_rates = __esm({
|
|
|
9767
9789
|
}
|
|
9768
9790
|
];
|
|
9769
9791
|
CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
9792
|
+
SUBSCRIPTION_TIERS = {
|
|
9793
|
+
"price_1TmiHRS8aAcsk3TGwmSNfNIa": { tier: "starter", label: "Starter", price_id: "price_1TmiHRS8aAcsk3TGwmSNfNIa", monthly_usd: 12, credits_mc: 10312500, concurrency: 3, intro_coupon: "mcp-starter-1dollar-intro-12" },
|
|
9794
|
+
"price_1Tmg1nS8aAcsk3TGe8zcnGTM": { tier: "growth", label: "Growth", price_id: "price_1Tmg1nS8aAcsk3TGe8zcnGTM", monthly_usd: 100, credits_mc: 9075e4, concurrency: 10, intro_coupon: null },
|
|
9795
|
+
"price_1Tmg1nS8aAcsk3TGsZw34iXS": { tier: "scale", label: "Scale", price_id: "price_1Tmg1nS8aAcsk3TGsZw34iXS", monthly_usd: 250, credits_mc: 226875e3, concurrency: 20, intro_coupon: null }
|
|
9796
|
+
};
|
|
9797
|
+
SUBSCRIPTION_TIER_BY_KEY = Object.fromEntries(
|
|
9798
|
+
Object.values(SUBSCRIPTION_TIERS).map((t) => [t.tier, t])
|
|
9799
|
+
);
|
|
9770
9800
|
CONCURRENCY_SLOT_PRICE_USD = 5;
|
|
9771
9801
|
CONCURRENCY_SLOT_PRICE_CURRENCY = "usd";
|
|
9772
9802
|
CONCURRENCY_SLOT_PRICE_INTERVAL = "month";
|
|
9773
9803
|
CONCURRENCY_SLOT_PRICE_LABEL = "$5/month";
|
|
9774
9804
|
CONCURRENCY_SLOT_TERMINAL_COMMAND = "npx -y -p mcp-scraper@latest mcp-scraper-cli billing concurrency checkout";
|
|
9775
|
-
FREE_SIGNUP_MC = 5e5;
|
|
9776
|
-
FREE_MONTHLY_REFRESH_MC = 25e4;
|
|
9777
|
-
BALANCE_PRICE_IDS = {
|
|
9778
|
-
"price_1TZx6rS8aAcsk3TGNMc1Vgpo": 11e6,
|
|
9779
|
-
"price_1TZx6sS8aAcsk3TGxgqB7khO": 275e5,
|
|
9780
|
-
"price_1TZx6tS8aAcsk3TG8PnJqHlG": 605e5,
|
|
9781
|
-
"price_1TZx6tS8aAcsk3TGNgRMpy0e": 121e6
|
|
9782
|
-
};
|
|
9783
|
-
BALANCE_PACK_LABELS = {
|
|
9784
|
-
"price_1TZx6rS8aAcsk3TGNMc1Vgpo": "$10",
|
|
9785
|
-
"price_1TZx6sS8aAcsk3TGxgqB7khO": "$25",
|
|
9786
|
-
"price_1TZx6tS8aAcsk3TG8PnJqHlG": "$50",
|
|
9787
|
-
"price_1TZx6tS8aAcsk3TGNgRMpy0e": "$100"
|
|
9788
|
-
};
|
|
9789
9805
|
LedgerOperation = {
|
|
9790
9806
|
TOPUP: "topup",
|
|
9807
|
+
SUBSCRIPTION: "subscription",
|
|
9791
9808
|
SIGNUP_GRANT: "signup_grant",
|
|
9792
9809
|
MONTHLY_REFRESH: "monthly_free_refresh",
|
|
9793
9810
|
PAA: "paa",
|
|
@@ -12212,7 +12229,8 @@ var init_server_schemas = __esm({
|
|
|
12212
12229
|
|
|
12213
12230
|
// src/api/concurrency-gates.ts
|
|
12214
12231
|
function concurrencyLimitForUser(user) {
|
|
12215
|
-
|
|
12232
|
+
const tier = Math.max(0, Number(user.subscription_concurrency ?? 0));
|
|
12233
|
+
return Math.max(1, tier) + Math.max(0, Number(user.extra_concurrency_slots ?? 0));
|
|
12216
12234
|
}
|
|
12217
12235
|
function concurrencyLimitExceededResponse(gate) {
|
|
12218
12236
|
const upgrade = concurrencySlotBillingInfo();
|
|
@@ -23863,7 +23881,7 @@ var PACKAGE_VERSION;
|
|
|
23863
23881
|
var init_version = __esm({
|
|
23864
23882
|
"src/version.ts"() {
|
|
23865
23883
|
"use strict";
|
|
23866
|
-
PACKAGE_VERSION = "0.3.
|
|
23884
|
+
PACKAGE_VERSION = "0.3.21";
|
|
23867
23885
|
}
|
|
23868
23886
|
});
|
|
23869
23887
|
|
|
@@ -27997,6 +28015,18 @@ var init_browser_agent_console = __esm({
|
|
|
27997
28015
|
});
|
|
27998
28016
|
|
|
27999
28017
|
// src/api/stripe-routes.ts
|
|
28018
|
+
function linePriceId(line) {
|
|
28019
|
+
const l = line;
|
|
28020
|
+
return l?.price?.id ?? l?.pricing?.price_details?.price ?? l?.plan?.id;
|
|
28021
|
+
}
|
|
28022
|
+
async function resolveUser(customerId, emailFallback) {
|
|
28023
|
+
let user = await getUserByStripeCustomerId(customerId);
|
|
28024
|
+
if (user) return user;
|
|
28025
|
+
if (!emailFallback) return void 0;
|
|
28026
|
+
user = await getUserByEmail(emailFallback);
|
|
28027
|
+
if (user) await setStripeCustomerId(user.id, customerId);
|
|
28028
|
+
return user;
|
|
28029
|
+
}
|
|
28000
28030
|
var import_stripe, import_hono12, stripe, stripeApp;
|
|
28001
28031
|
var init_stripe_routes = __esm({
|
|
28002
28032
|
"src/api/stripe-routes.ts"() {
|
|
@@ -28018,46 +28048,43 @@ var init_stripe_routes = __esm({
|
|
|
28018
28048
|
}
|
|
28019
28049
|
const isNew = await recordStripeEvent(event.id);
|
|
28020
28050
|
if (!isNew) return c.json({ received: true });
|
|
28021
|
-
if (event.type === "
|
|
28022
|
-
const
|
|
28023
|
-
|
|
28024
|
-
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
const label = BALANCE_PACK_LABELS[priceId] ?? "unknown";
|
|
28031
|
-
let user = await getUserByStripeCustomerId(customerId);
|
|
28032
|
-
if (!user) {
|
|
28033
|
-
const email = session.customer_details?.email;
|
|
28034
|
-
if (!email) return c.json({ received: true });
|
|
28035
|
-
user = await getUserByEmail(email);
|
|
28036
|
-
if (!user) return c.json({ received: true });
|
|
28037
|
-
await setStripeCustomerId(user.id, customerId);
|
|
28051
|
+
if (event.type === "invoice.paid" || event.type === "invoice.payment_succeeded") {
|
|
28052
|
+
const invoice = event.data.object;
|
|
28053
|
+
const lineTierId = invoice.lines.data.map(linePriceId).find((id) => id && id in SUBSCRIPTION_TIERS);
|
|
28054
|
+
if (lineTierId) {
|
|
28055
|
+
const tier = SUBSCRIPTION_TIERS[lineTierId];
|
|
28056
|
+
const user = await resolveUser(invoice.customer, invoice.customer_email ?? void 0);
|
|
28057
|
+
if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
|
|
28058
|
+
await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id);
|
|
28059
|
+
await setSubscriptionTier(user.id, tier.tier, tier.concurrency, invoice.subscription ?? user.subscription_id);
|
|
28038
28060
|
}
|
|
28039
|
-
const pi = session.payment_intent;
|
|
28040
|
-
if (await ledgerExistsForStripePI(pi)) return c.json({ received: true });
|
|
28041
|
-
await creditMc(user.id, mc, LedgerOperation.TOPUP, `${label} balance top-up`, pi);
|
|
28042
28061
|
}
|
|
28043
28062
|
}
|
|
28044
|
-
if (event.type === "customer.subscription.created") {
|
|
28063
|
+
if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated") {
|
|
28045
28064
|
const sub = event.data.object;
|
|
28046
28065
|
const priceId = sub.items.data[0]?.price?.id;
|
|
28047
|
-
if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
|
|
28048
28066
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
28049
28067
|
if (!user) return c.json({ received: true });
|
|
28050
|
-
|
|
28051
|
-
|
|
28068
|
+
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
28069
|
+
const tier = SUBSCRIPTION_TIERS[priceId];
|
|
28070
|
+
const live = sub.status === "active" || sub.status === "trialing";
|
|
28071
|
+
await setSubscriptionTier(user.id, live ? tier.tier : null, live ? tier.concurrency : 0, live ? sub.id : null);
|
|
28072
|
+
} else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
|
|
28073
|
+
await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
|
|
28074
|
+
await setConcurrencySubId(user.id, sub.id);
|
|
28075
|
+
}
|
|
28052
28076
|
}
|
|
28053
28077
|
if (event.type === "customer.subscription.deleted") {
|
|
28054
28078
|
const sub = event.data.object;
|
|
28055
28079
|
const priceId = sub.items.data[0]?.price?.id;
|
|
28056
|
-
if (priceId !== CONCURRENCY_PRICE_ID) return c.json({ received: true });
|
|
28057
28080
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
28058
28081
|
if (!user) return c.json({ received: true });
|
|
28059
|
-
|
|
28060
|
-
|
|
28082
|
+
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
28083
|
+
await setSubscriptionTier(user.id, null, 0, null);
|
|
28084
|
+
} else if (priceId === CONCURRENCY_PRICE_ID) {
|
|
28085
|
+
await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
|
|
28086
|
+
await setConcurrencySubId(user.id, null);
|
|
28087
|
+
}
|
|
28061
28088
|
}
|
|
28062
28089
|
return c.json({ received: true });
|
|
28063
28090
|
});
|
|
@@ -28106,102 +28133,15 @@ var init_site_audit_worker = __esm({
|
|
|
28106
28133
|
}
|
|
28107
28134
|
});
|
|
28108
28135
|
|
|
28109
|
-
// src/api/billing-schemas.ts
|
|
28110
|
-
var import_zod28, BillingCheckoutBodySchema, FreeCreditBreakdownSchema, BillingBalanceResponseSchema, MonthlyRefreshSweepResultSchema;
|
|
28111
|
-
var init_billing_schemas = __esm({
|
|
28112
|
-
"src/api/billing-schemas.ts"() {
|
|
28113
|
-
"use strict";
|
|
28114
|
-
import_zod28 = require("zod");
|
|
28115
|
-
BillingCheckoutBodySchema = import_zod28.z.object({
|
|
28116
|
-
priceId: import_zod28.z.string().min(1)
|
|
28117
|
-
});
|
|
28118
|
-
FreeCreditBreakdownSchema = import_zod28.z.object({
|
|
28119
|
-
signup_grant_mc: import_zod28.z.number().int().nonnegative(),
|
|
28120
|
-
monthly_refresh_mc: import_zod28.z.number().int().nonnegative(),
|
|
28121
|
-
total_free_mc: import_zod28.z.number().int().nonnegative(),
|
|
28122
|
-
signup_grant_credits: import_zod28.z.number().nonnegative(),
|
|
28123
|
-
monthly_refresh_credits: import_zod28.z.number().nonnegative(),
|
|
28124
|
-
total_free_credits: import_zod28.z.number().nonnegative()
|
|
28125
|
-
});
|
|
28126
|
-
BillingBalanceResponseSchema = import_zod28.z.object({
|
|
28127
|
-
balance_mc: import_zod28.z.number().int().nonnegative(),
|
|
28128
|
-
balance_credits: import_zod28.z.number().nonnegative(),
|
|
28129
|
-
free_credits: FreeCreditBreakdownSchema,
|
|
28130
|
-
ledger: import_zod28.z.array(import_zod28.z.any())
|
|
28131
|
-
});
|
|
28132
|
-
MonthlyRefreshSweepResultSchema = import_zod28.z.object({
|
|
28133
|
-
usersRefreshed: import_zod28.z.number().int().nonnegative(),
|
|
28134
|
-
totalMcGranted: import_zod28.z.number().int().nonnegative()
|
|
28135
|
-
});
|
|
28136
|
-
}
|
|
28137
|
-
});
|
|
28138
|
-
|
|
28139
28136
|
// src/api/credit-operations.ts
|
|
28140
|
-
function
|
|
28141
|
-
return
|
|
28142
|
-
}
|
|
28143
|
-
function userCreatedBeforeThisMonth(user) {
|
|
28144
|
-
return user.created_at.slice(0, 7) < monthKey();
|
|
28145
|
-
}
|
|
28146
|
-
async function grantSignupCredit(userId) {
|
|
28147
|
-
if (await ledgerExistsForOperation(userId, LedgerOperation.SIGNUP_GRANT)) return;
|
|
28148
|
-
await creditMc(userId, FREE_SIGNUP_MC, LedgerOperation.SIGNUP_GRANT, "Welcome to MCP Scraper");
|
|
28137
|
+
async function grantSignupCredit(_userId) {
|
|
28138
|
+
return;
|
|
28149
28139
|
}
|
|
28150
28140
|
async function applyMonthlyFreeRefresh(user) {
|
|
28151
|
-
|
|
28152
|
-
const claimed = await claimMonthlyFreeRefresh(user.id, monthKey());
|
|
28153
|
-
if (!claimed) return user;
|
|
28154
|
-
const balance = await creditMc(
|
|
28155
|
-
user.id,
|
|
28156
|
-
FREE_MONTHLY_REFRESH_MC,
|
|
28157
|
-
LedgerOperation.MONTHLY_REFRESH,
|
|
28158
|
-
"Monthly free credits"
|
|
28159
|
-
);
|
|
28160
|
-
return { ...user, balance_mc: balance };
|
|
28141
|
+
return user;
|
|
28161
28142
|
}
|
|
28162
28143
|
async function runMonthlyRefreshSweep() {
|
|
28163
|
-
|
|
28164
|
-
const monthStart = /* @__PURE__ */ new Date();
|
|
28165
|
-
monthStart.setUTCDate(1);
|
|
28166
|
-
monthStart.setUTCHours(0, 0, 0, 0);
|
|
28167
|
-
const monthStartIso = monthStart.toISOString();
|
|
28168
|
-
const currentMonth = monthKey();
|
|
28169
|
-
const res = await db.execute({
|
|
28170
|
-
sql: `
|
|
28171
|
-
SELECT * FROM users
|
|
28172
|
-
WHERE created_at < ?
|
|
28173
|
-
AND active = 1
|
|
28174
|
-
AND id NOT IN (
|
|
28175
|
-
SELECT user_id FROM free_credit_refreshes WHERE month = ?
|
|
28176
|
-
)
|
|
28177
|
-
`,
|
|
28178
|
-
args: [monthStartIso, currentMonth]
|
|
28179
|
-
});
|
|
28180
|
-
const users = res.rows.map((r) => ({
|
|
28181
|
-
id: Number(r.id),
|
|
28182
|
-
email: String(r.email),
|
|
28183
|
-
name: r.name != null ? String(r.name) : null,
|
|
28184
|
-
api_key: String(r.api_key ?? ""),
|
|
28185
|
-
key_active: Number(r.key_active ?? 1),
|
|
28186
|
-
password_hash: r.password_hash != null ? String(r.password_hash) : null,
|
|
28187
|
-
created_at: String(r.created_at),
|
|
28188
|
-
active: Number(r.active),
|
|
28189
|
-
stripe_customer_id: r.stripe_customer_id != null ? String(r.stripe_customer_id) : null,
|
|
28190
|
-
balance_mc: Number(r.balance_mc ?? 0),
|
|
28191
|
-
extra_concurrency_slots: Number(r.extra_concurrency_slots ?? 0),
|
|
28192
|
-
concurrency_stripe_sub_id: r.concurrency_stripe_sub_id != null ? String(r.concurrency_stripe_sub_id) : null
|
|
28193
|
-
}));
|
|
28194
|
-
let usersRefreshed = 0;
|
|
28195
|
-
let totalMcGranted = 0;
|
|
28196
|
-
for (const user of users) {
|
|
28197
|
-
const before = user.balance_mc;
|
|
28198
|
-
const after = await applyMonthlyFreeRefresh(user);
|
|
28199
|
-
if (after.balance_mc !== before) {
|
|
28200
|
-
usersRefreshed++;
|
|
28201
|
-
totalMcGranted += FREE_MONTHLY_REFRESH_MC;
|
|
28202
|
-
}
|
|
28203
|
-
}
|
|
28204
|
-
return { usersRefreshed, totalMcGranted };
|
|
28144
|
+
return { usersRefreshed: 0, totalMcGranted: 0 };
|
|
28205
28145
|
}
|
|
28206
28146
|
async function getFreeCreditBreakdown(userId) {
|
|
28207
28147
|
const res = await getDb().execute({
|
|
@@ -28237,7 +28177,6 @@ var init_credit_operations = __esm({
|
|
|
28237
28177
|
"use strict";
|
|
28238
28178
|
init_db();
|
|
28239
28179
|
init_rates();
|
|
28240
|
-
init_db();
|
|
28241
28180
|
}
|
|
28242
28181
|
});
|
|
28243
28182
|
|
|
@@ -28587,7 +28526,6 @@ var init_server = __esm({
|
|
|
28587
28526
|
init_db();
|
|
28588
28527
|
import_stripe2 = __toESM(require("stripe"), 1);
|
|
28589
28528
|
init_rates();
|
|
28590
|
-
init_billing_schemas();
|
|
28591
28529
|
init_server_schemas();
|
|
28592
28530
|
init_schemas3();
|
|
28593
28531
|
init_credit_operations();
|
|
@@ -28781,8 +28719,10 @@ var init_server = __esm({
|
|
|
28781
28719
|
balance_mc: user.balance_mc,
|
|
28782
28720
|
balance_credits: user.balance_mc / 1e3,
|
|
28783
28721
|
extra_concurrency_slots: user.extra_concurrency_slots,
|
|
28784
|
-
concurrency_limit:
|
|
28722
|
+
concurrency_limit: concurrencyLimitForUser(user),
|
|
28785
28723
|
has_concurrency_sub: !!user.concurrency_stripe_sub_id,
|
|
28724
|
+
subscription_tier: user.subscription_tier,
|
|
28725
|
+
subscription_concurrency: user.subscription_concurrency,
|
|
28786
28726
|
...stats
|
|
28787
28727
|
});
|
|
28788
28728
|
});
|
|
@@ -29208,13 +29148,12 @@ var init_server = __esm({
|
|
|
29208
29148
|
});
|
|
29209
29149
|
});
|
|
29210
29150
|
app.post("/billing/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
29151
|
+
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);
|
|
29152
|
+
});
|
|
29153
|
+
app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
29211
29154
|
try {
|
|
29212
29155
|
const user = c.get("sessionUser");
|
|
29213
|
-
|
|
29214
|
-
const parsed = BillingCheckoutBodySchema.safeParse(raw);
|
|
29215
|
-
if (!parsed.success) return c.json({ error: "Invalid request" }, 400);
|
|
29216
|
-
const { priceId } = parsed.data;
|
|
29217
|
-
if (!(priceId in BALANCE_PRICE_IDS)) return c.json({ error: "Invalid price" }, 400);
|
|
29156
|
+
if (user.concurrency_stripe_sub_id) return c.json({ error: "Already subscribed \u2014 cancel existing slot first to change." }, 409);
|
|
29218
29157
|
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
29219
29158
|
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
29220
29159
|
const stripeClient = new import_stripe2.default(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
@@ -29226,26 +29165,28 @@ var init_server = __esm({
|
|
|
29226
29165
|
}
|
|
29227
29166
|
const session = await stripeClient.checkout.sessions.create({
|
|
29228
29167
|
customer: customerId,
|
|
29229
|
-
mode: "
|
|
29230
|
-
line_items: [{ price:
|
|
29168
|
+
mode: "subscription",
|
|
29169
|
+
line_items: [{ price: CONCURRENCY_PRICE_ID, quantity: 1 }],
|
|
29231
29170
|
ui_mode: "embedded",
|
|
29232
29171
|
automatic_tax: { enabled: true },
|
|
29233
29172
|
billing_address_collection: "required",
|
|
29234
29173
|
customer_update: { address: "auto", name: "auto" },
|
|
29235
29174
|
tax_id_collection: { enabled: true },
|
|
29236
|
-
return_url: `${appOrigin()}/billing?
|
|
29175
|
+
return_url: `${appOrigin()}/billing?slot_added=1`
|
|
29237
29176
|
});
|
|
29238
29177
|
return c.json({ clientSecret: session.client_secret });
|
|
29239
29178
|
} catch (err) {
|
|
29240
29179
|
const message = err instanceof Error ? err.message : "Unable to start checkout.";
|
|
29241
|
-
console.error("[billing/checkout]", message);
|
|
29180
|
+
console.error("[billing/concurrency/checkout]", message);
|
|
29242
29181
|
return c.json({ error: message }, 500);
|
|
29243
29182
|
}
|
|
29244
29183
|
});
|
|
29245
|
-
app.post("/billing/
|
|
29184
|
+
app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
29246
29185
|
try {
|
|
29247
29186
|
const user = c.get("sessionUser");
|
|
29248
|
-
|
|
29187
|
+
const raw = await c.req.json().catch(() => ({}));
|
|
29188
|
+
const tier = SUBSCRIPTION_TIER_BY_KEY[String(raw.tier ?? "")];
|
|
29189
|
+
if (!tier) return c.json({ error: "Invalid tier. Choose starter, growth, or scale." }, 400);
|
|
29249
29190
|
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
29250
29191
|
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
29251
29192
|
const stripeClient = new import_stripe2.default(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
@@ -29255,21 +29196,33 @@ var init_server = __esm({
|
|
|
29255
29196
|
customerId = customer.id;
|
|
29256
29197
|
await setStripeCustomerId(user.id, customerId);
|
|
29257
29198
|
}
|
|
29199
|
+
if (user.subscription_id) {
|
|
29200
|
+
const sub = await stripeClient.subscriptions.retrieve(user.subscription_id);
|
|
29201
|
+
const itemId = sub.items.data[0]?.id;
|
|
29202
|
+
if (itemId) {
|
|
29203
|
+
await stripeClient.subscriptions.update(user.subscription_id, {
|
|
29204
|
+
items: [{ id: itemId, price: tier.price_id }],
|
|
29205
|
+
proration_behavior: "create_prorations"
|
|
29206
|
+
});
|
|
29207
|
+
return c.json({ updated: true, tier: tier.tier });
|
|
29208
|
+
}
|
|
29209
|
+
}
|
|
29258
29210
|
const session = await stripeClient.checkout.sessions.create({
|
|
29259
29211
|
customer: customerId,
|
|
29260
29212
|
mode: "subscription",
|
|
29261
|
-
line_items: [{ price:
|
|
29213
|
+
line_items: [{ price: tier.price_id, quantity: 1 }],
|
|
29214
|
+
...tier.intro_coupon ? { discounts: [{ coupon: tier.intro_coupon }] } : {},
|
|
29262
29215
|
ui_mode: "embedded",
|
|
29263
29216
|
automatic_tax: { enabled: true },
|
|
29264
29217
|
billing_address_collection: "required",
|
|
29265
29218
|
customer_update: { address: "auto", name: "auto" },
|
|
29266
29219
|
tax_id_collection: { enabled: true },
|
|
29267
|
-
return_url: `${appOrigin()}/billing?
|
|
29220
|
+
return_url: `${appOrigin()}/billing?subscribed=${tier.tier}`
|
|
29268
29221
|
});
|
|
29269
29222
|
return c.json({ clientSecret: session.client_secret });
|
|
29270
29223
|
} catch (err) {
|
|
29271
|
-
const message = err instanceof Error ? err.message : "Unable to start
|
|
29272
|
-
console.error("[billing/
|
|
29224
|
+
const message = err instanceof Error ? err.message : "Unable to start subscription.";
|
|
29225
|
+
console.error("[billing/subscribe]", message);
|
|
29273
29226
|
return c.json({ error: message }, 500);
|
|
29274
29227
|
}
|
|
29275
29228
|
});
|
|
@@ -29317,7 +29270,7 @@ var init_server = __esm({
|
|
|
29317
29270
|
current_extra_slots: user.extra_concurrency_slots,
|
|
29318
29271
|
current_limit: concurrencyLimitForUser(user),
|
|
29319
29272
|
after_checkout_extra_slots: user.extra_concurrency_slots + 1,
|
|
29320
|
-
after_checkout_limit: concurrencyLimitForUser({ extra_concurrency_slots: user.extra_concurrency_slots + 1 })
|
|
29273
|
+
after_checkout_limit: concurrencyLimitForUser({ extra_concurrency_slots: user.extra_concurrency_slots + 1, subscription_concurrency: user.subscription_concurrency })
|
|
29321
29274
|
},
|
|
29322
29275
|
next_step: "Open checkout_url in a browser, complete checkout, then restart or retry the MCP request."
|
|
29323
29276
|
});
|