hive-intelligence 1.16.1 → 1.17.1
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/agent-skills/packs/hive-build-onboarding/SKILL.md +17 -13
- package/agent-skills/packs/hive-mcp/references/client-install-matrix.md +0 -1
- package/build/{chunk-6M6O3DZM.js → chunk-TLADUAUA.js} +1 -1
- package/build/{chunk-757ZNB6W.js → chunk-UP47NKED.js} +272 -680
- package/build/cli.js +5 -5
- package/build/{doctor-SP73PMX2.js → doctor-PAVENKK7.js} +1 -1
- package/build/{mcpServer-4JE5FTVP.js → mcpServer-TJEF6MZ6.js} +1 -1
- package/build/monitor-worker.js +2 -2
- package/build/release.json +4 -4
- package/build/{serve-YV4RZIK5.js → serve-R6GMEH2G.js} +1 -1
- package/build/server.js +13 -644
- package/build/stdio.js +1 -1
- package/package.json +1 -1
package/build/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getWalletStatus,
|
|
9
9
|
refundCredits,
|
|
10
10
|
refundSettled
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-TLADUAUA.js";
|
|
12
12
|
import {
|
|
13
13
|
CategoryEndpoints,
|
|
14
14
|
HERO_TOOL_NAMES,
|
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
validateEndpointArgs,
|
|
98
98
|
verifyApiKey,
|
|
99
99
|
waitForRedisAvailability
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-UP47NKED.js";
|
|
101
101
|
import {
|
|
102
102
|
HIVE_AGENT_SKILLS
|
|
103
103
|
} from "./chunk-QYK3EWTE.js";
|
|
@@ -754,7 +754,7 @@ function resolveTrustedProxies(raw = getEnvVar("TRUSTED_PROXY_ADDRESSES")) {
|
|
|
754
754
|
}
|
|
755
755
|
|
|
756
756
|
// src/server.ts
|
|
757
|
-
import { randomUUID as randomUUID4, timingSafeEqual as
|
|
757
|
+
import { randomUUID as randomUUID4, timingSafeEqual as timingSafeEqual5 } from "crypto";
|
|
758
758
|
import { resolve } from "path";
|
|
759
759
|
import { fileURLToPath } from "url";
|
|
760
760
|
import express2 from "express";
|
|
@@ -1774,7 +1774,7 @@ function buildCatalogIntegrityDimension(history) {
|
|
|
1774
1774
|
}
|
|
1775
1775
|
};
|
|
1776
1776
|
}
|
|
1777
|
-
var RATE_LIMITER_SCOPE = "This instance's shared rate-limit store
|
|
1777
|
+
var RATE_LIMITER_SCOPE = "This instance's shared rate-limit store and idempotency keys.";
|
|
1778
1778
|
function buildRateLimiterDimension(signal) {
|
|
1779
1779
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1780
1780
|
if (!signal.configured) {
|
|
@@ -3745,181 +3745,17 @@ import {
|
|
|
3745
3745
|
} from "express";
|
|
3746
3746
|
import { z as z2 } from "zod";
|
|
3747
3747
|
|
|
3748
|
-
// src/services/
|
|
3748
|
+
// src/services/llamapay-service.ts
|
|
3749
3749
|
import { createHmac, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
3750
|
-
var
|
|
3751
|
-
var STRIPE_SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
3750
|
+
var LLAMAPAY_API_BASE = "https://api.llamapay.io";
|
|
3752
3751
|
var PAID_PLAN_ID = "analyst";
|
|
3753
3752
|
var FREE_PLAN_ID = "demo";
|
|
3754
|
-
function getStripeSecretKey() {
|
|
3755
|
-
return getEnvVar("STRIPE_SECRET_KEY");
|
|
3756
|
-
}
|
|
3757
|
-
function getStripeWebhookSecret() {
|
|
3758
|
-
return getEnvVar("STRIPE_WEBHOOK_SECRET");
|
|
3759
|
-
}
|
|
3760
|
-
function isStripeCheckoutConfigured() {
|
|
3761
|
-
return Boolean(getStripeSecretKey());
|
|
3762
|
-
}
|
|
3763
|
-
var STRIPE_PRICE_ENV = {
|
|
3764
|
-
starter: {
|
|
3765
|
-
monthly: "STRIPE_PRICE_STARTER_MONTHLY",
|
|
3766
|
-
yearly: "STRIPE_PRICE_STARTER_YEARLY"
|
|
3767
|
-
},
|
|
3768
|
-
analyst: {
|
|
3769
|
-
monthly: "STRIPE_PRICE_PRO_MONTHLY",
|
|
3770
|
-
yearly: "STRIPE_PRICE_PRO_YEARLY"
|
|
3771
|
-
}
|
|
3772
|
-
};
|
|
3773
|
-
function priceIdForPlanCycle(planId, cycle) {
|
|
3774
|
-
const row = STRIPE_PRICE_ENV[planId];
|
|
3775
|
-
if (!row) return void 0;
|
|
3776
|
-
return getEnvVar(row[cycle]);
|
|
3777
|
-
}
|
|
3778
|
-
function planIdForPrice(priceId) {
|
|
3779
|
-
if (!priceId) return null;
|
|
3780
|
-
for (const [planId, cycles] of Object.entries(STRIPE_PRICE_ENV)) {
|
|
3781
|
-
for (const envName of Object.values(cycles)) {
|
|
3782
|
-
if (priceId === getEnvVar(envName)) return planId;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
return null;
|
|
3786
|
-
}
|
|
3787
|
-
var STRIPE_SUBSCRIPTION_STATUSES = [
|
|
3788
|
-
"incomplete",
|
|
3789
|
-
"incomplete_expired",
|
|
3790
|
-
"trialing",
|
|
3791
|
-
"active",
|
|
3792
|
-
"past_due",
|
|
3793
|
-
"canceled",
|
|
3794
|
-
"unpaid",
|
|
3795
|
-
"paused"
|
|
3796
|
-
];
|
|
3797
|
-
function registrySubscriptionStatus(status) {
|
|
3798
|
-
if (!status) return null;
|
|
3799
|
-
return STRIPE_SUBSCRIPTION_STATUSES.includes(status) ? status : null;
|
|
3800
|
-
}
|
|
3801
3753
|
function getBillingSuccessUrl() {
|
|
3802
3754
|
return getEnvVar("BILLING_SUCCESS_URL") ?? "https://www.hiveintelligence.xyz/dashboard?checkout=success";
|
|
3803
3755
|
}
|
|
3804
3756
|
function getBillingCancelUrl() {
|
|
3805
3757
|
return getEnvVar("BILLING_CANCEL_URL") ?? "https://www.hiveintelligence.xyz/dashboard?checkout=cancelled";
|
|
3806
3758
|
}
|
|
3807
|
-
function verifyStripeSignature(args) {
|
|
3808
|
-
const {
|
|
3809
|
-
payload,
|
|
3810
|
-
header,
|
|
3811
|
-
secret,
|
|
3812
|
-
toleranceSeconds = STRIPE_SIGNATURE_TOLERANCE_SECONDS,
|
|
3813
|
-
nowSeconds = Math.floor(Date.now() / 1e3)
|
|
3814
|
-
} = args;
|
|
3815
|
-
if (typeof header !== "string" || header.length === 0) {
|
|
3816
|
-
return { valid: false, reason: "missing_header" };
|
|
3817
|
-
}
|
|
3818
|
-
let timestamp = null;
|
|
3819
|
-
const candidates = [];
|
|
3820
|
-
for (const part of header.split(",")) {
|
|
3821
|
-
const eq = part.indexOf("=");
|
|
3822
|
-
if (eq <= 0) continue;
|
|
3823
|
-
const key = part.slice(0, eq).trim();
|
|
3824
|
-
const value = part.slice(eq + 1).trim();
|
|
3825
|
-
if (key === "t") {
|
|
3826
|
-
const parsed = Number.parseInt(value, 10);
|
|
3827
|
-
if (Number.isFinite(parsed)) timestamp = parsed;
|
|
3828
|
-
} else if (key === "v1" && /^[0-9a-f]+$/i.test(value)) {
|
|
3829
|
-
candidates.push(value);
|
|
3830
|
-
}
|
|
3831
|
-
}
|
|
3832
|
-
if (timestamp === null || candidates.length === 0) {
|
|
3833
|
-
return { valid: false, reason: "malformed_header" };
|
|
3834
|
-
}
|
|
3835
|
-
if (Math.abs(nowSeconds - timestamp) > toleranceSeconds) {
|
|
3836
|
-
return { valid: false, reason: "timestamp_out_of_tolerance" };
|
|
3837
|
-
}
|
|
3838
|
-
const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(payload).digest();
|
|
3839
|
-
for (const candidate of candidates) {
|
|
3840
|
-
const provided = Buffer.from(candidate, "hex");
|
|
3841
|
-
if (provided.length === expected.length && timingSafeEqual3(provided, expected)) {
|
|
3842
|
-
return { valid: true };
|
|
3843
|
-
}
|
|
3844
|
-
}
|
|
3845
|
-
return { valid: false, reason: "signature_mismatch" };
|
|
3846
|
-
}
|
|
3847
|
-
var StripeApiError = class extends Error {
|
|
3848
|
-
constructor(status, message) {
|
|
3849
|
-
super(message);
|
|
3850
|
-
this.status = status;
|
|
3851
|
-
this.name = "StripeApiError";
|
|
3852
|
-
}
|
|
3853
|
-
status;
|
|
3854
|
-
};
|
|
3855
|
-
async function stripePost(path, params) {
|
|
3856
|
-
const key = getStripeSecretKey();
|
|
3857
|
-
if (!key) {
|
|
3858
|
-
throw new StripeApiError(503, "STRIPE_SECRET_KEY is not configured");
|
|
3859
|
-
}
|
|
3860
|
-
const response = await fetch(`${STRIPE_API_BASE}${path}`, {
|
|
3861
|
-
method: "POST",
|
|
3862
|
-
headers: {
|
|
3863
|
-
authorization: `Bearer ${key}`,
|
|
3864
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
3865
|
-
},
|
|
3866
|
-
body: new URLSearchParams(params).toString()
|
|
3867
|
-
});
|
|
3868
|
-
let json = {};
|
|
3869
|
-
try {
|
|
3870
|
-
json = await response.json();
|
|
3871
|
-
} catch {
|
|
3872
|
-
json = {};
|
|
3873
|
-
}
|
|
3874
|
-
if (!response.ok) {
|
|
3875
|
-
const error = json.error;
|
|
3876
|
-
throw new StripeApiError(
|
|
3877
|
-
response.status,
|
|
3878
|
-
error?.message ?? `Stripe API request to ${path} failed`
|
|
3879
|
-
);
|
|
3880
|
-
}
|
|
3881
|
-
return json;
|
|
3882
|
-
}
|
|
3883
|
-
async function createStripeCustomer(args) {
|
|
3884
|
-
const params = {
|
|
3885
|
-
"metadata[user_id]": args.userId
|
|
3886
|
-
};
|
|
3887
|
-
if (args.email) params.email = args.email;
|
|
3888
|
-
const customer = await stripePost("/v1/customers", params);
|
|
3889
|
-
const id = customer.id;
|
|
3890
|
-
if (typeof id !== "string" || !id) {
|
|
3891
|
-
throw new StripeApiError(502, "Stripe customer creation returned no id");
|
|
3892
|
-
}
|
|
3893
|
-
return id;
|
|
3894
|
-
}
|
|
3895
|
-
async function createStripeCheckoutSession(args) {
|
|
3896
|
-
const session = await stripePost("/v1/checkout/sessions", {
|
|
3897
|
-
mode: "subscription",
|
|
3898
|
-
customer: args.customerId,
|
|
3899
|
-
"line_items[0][price]": args.priceId,
|
|
3900
|
-
"line_items[0][quantity]": "1",
|
|
3901
|
-
success_url: getBillingSuccessUrl(),
|
|
3902
|
-
cancel_url: getBillingCancelUrl(),
|
|
3903
|
-
client_reference_id: args.userId,
|
|
3904
|
-
"metadata[user_id]": args.userId,
|
|
3905
|
-
"metadata[plan_id]": args.planId,
|
|
3906
|
-
"subscription_data[metadata][user_id]": args.userId,
|
|
3907
|
-
"subscription_data[metadata][plan_id]": args.planId
|
|
3908
|
-
});
|
|
3909
|
-
const id = session.id;
|
|
3910
|
-
const url = session.url;
|
|
3911
|
-
if (typeof id !== "string" || typeof url !== "string" || !url) {
|
|
3912
|
-
throw new StripeApiError(
|
|
3913
|
-
502,
|
|
3914
|
-
"Stripe checkout session creation returned no URL"
|
|
3915
|
-
);
|
|
3916
|
-
}
|
|
3917
|
-
return { id, url };
|
|
3918
|
-
}
|
|
3919
|
-
|
|
3920
|
-
// src/services/llamapay-service.ts
|
|
3921
|
-
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
3922
|
-
var LLAMAPAY_API_BASE = "https://api.llamapay.io";
|
|
3923
3759
|
var LLAMAPAY_SIGNATURE_HEADER = "x-cc-webhook-signature";
|
|
3924
3760
|
function getLlamaPayApiKey() {
|
|
3925
3761
|
const key = process.env.LLAMAPAY_API_KEY?.trim();
|
|
@@ -4069,13 +3905,13 @@ function verifyLlamaPaySignature(args) {
|
|
|
4069
3905
|
if (!/^[0-9a-f]+$/.test(received)) {
|
|
4070
3906
|
return { valid: false, reason: "malformed_header" };
|
|
4071
3907
|
}
|
|
4072
|
-
const expected =
|
|
3908
|
+
const expected = createHmac("sha256", args.secret).update(args.payload).digest("hex");
|
|
4073
3909
|
const receivedBuf = Buffer.from(received, "utf8");
|
|
4074
3910
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
4075
3911
|
if (receivedBuf.length !== expectedBuf.length) {
|
|
4076
3912
|
return { valid: false, reason: "no_match" };
|
|
4077
3913
|
}
|
|
4078
|
-
if (!
|
|
3914
|
+
if (!timingSafeEqual3(receivedBuf, expectedBuf)) {
|
|
4079
3915
|
return { valid: false, reason: "no_match" };
|
|
4080
3916
|
}
|
|
4081
3917
|
return { valid: true };
|
|
@@ -4103,7 +3939,7 @@ function requireBillingPrincipal(req, res, next) {
|
|
|
4103
3939
|
verifyApiKey(apiKeyHeader, [
|
|
4104
3940
|
"service=hive",
|
|
4105
3941
|
"transport=rest",
|
|
4106
|
-
"route=/api/v1/billing
|
|
3942
|
+
"route=/api/v1/billing"
|
|
4107
3943
|
]).then((result) => {
|
|
4108
3944
|
if (!result.valid || !result.userId) {
|
|
4109
3945
|
res.status(401).json(
|
|
@@ -4135,13 +3971,6 @@ function requireBillingPrincipal(req, res, next) {
|
|
|
4135
3971
|
next();
|
|
4136
3972
|
});
|
|
4137
3973
|
}
|
|
4138
|
-
var CheckoutSchema = z2.object({
|
|
4139
|
-
// Was absent, and the route hardcoded PAID_PLAN_ID. The crypto lane sold
|
|
4140
|
-
// Starter and this one did not, so the two processors sold different
|
|
4141
|
-
// catalogues -- with Starter the emphasised middle card on /pricing.
|
|
4142
|
-
plan_id: z2.string().min(1).default(PAID_PLAN_ID),
|
|
4143
|
-
billing_cycle: z2.enum(["monthly", "yearly"]).default("monthly")
|
|
4144
|
-
});
|
|
4145
3974
|
var OCCUPYING_BILLING_STATUSES = /* @__PURE__ */ new Set(["active", "past_due", "unpaid"]);
|
|
4146
3975
|
var CHECKOUT_LOCK_TTL_MS = 6e4;
|
|
4147
3976
|
var CHECKOUT_LOCK_PREFIX = "billing:checkout:";
|
|
@@ -4230,24 +4059,6 @@ async function findOpenCharge(userId) {
|
|
|
4230
4059
|
const row = Array.isArray(data) ? data[0] : void 0;
|
|
4231
4060
|
return typeof row?.charge_id === "string" ? row.charge_id : null;
|
|
4232
4061
|
}
|
|
4233
|
-
async function settleChargeRecord(chargeId, status, eventType) {
|
|
4234
|
-
if (!chargeId || !isSupabaseConfigured()) return;
|
|
4235
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4236
|
-
try {
|
|
4237
|
-
const { error } = await getSupabaseAdmin().from("billing_charges").update({
|
|
4238
|
-
status,
|
|
4239
|
-
settled_at: now,
|
|
4240
|
-
last_event_at: now,
|
|
4241
|
-
last_event_type: eventType
|
|
4242
|
-
}).eq("charge_id", chargeId);
|
|
4243
|
-
if (error) throw new Error(error.message);
|
|
4244
|
-
} catch (err) {
|
|
4245
|
-
secureLogger.error("Failed to settle billing charge record", err, {
|
|
4246
|
-
chargeId,
|
|
4247
|
-
eventType
|
|
4248
|
-
});
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
4062
|
async function recordChargeCreated(charge) {
|
|
4252
4063
|
const { error } = await getSupabaseAdmin().from("billing_charges").insert({
|
|
4253
4064
|
charge_id: charge.chargeId,
|
|
@@ -4264,452 +4075,15 @@ async function recordChargeCreated(charge) {
|
|
|
4264
4075
|
}
|
|
4265
4076
|
}
|
|
4266
4077
|
var billingRouter = Router6();
|
|
4267
|
-
billingRouter.post(
|
|
4268
|
-
"/checkout-session",
|
|
4269
|
-
requireBillingPrincipal,
|
|
4270
|
-
withCheckoutLock,
|
|
4271
|
-
asyncHandler(async (req, res) => {
|
|
4272
|
-
const userId = req.billingUserId;
|
|
4273
|
-
if (!isStripeCheckoutConfigured()) {
|
|
4274
|
-
return res.status(503).json(BILLING_UNAVAILABLE);
|
|
4275
|
-
}
|
|
4276
|
-
if (!isSupabaseConfigured()) {
|
|
4277
|
-
return res.status(503).json(BILLING_UNAVAILABLE);
|
|
4278
|
-
}
|
|
4279
|
-
const parsed = CheckoutSchema.safeParse(req.body ?? {});
|
|
4280
|
-
if (!parsed.success) {
|
|
4281
|
-
return res.status(400).json(
|
|
4282
|
-
apiError(
|
|
4283
|
-
"invalid_request_error",
|
|
4284
|
-
"VALIDATION_ERROR",
|
|
4285
|
-
'billing_cycle must be "monthly" or "yearly".'
|
|
4286
|
-
)
|
|
4287
|
-
);
|
|
4288
|
-
}
|
|
4289
|
-
const { plan_id: planId, billing_cycle: cycle } = parsed.data;
|
|
4290
|
-
if (!isSellablePlan(planId)) {
|
|
4291
|
-
return res.status(400).json(
|
|
4292
|
-
apiError(
|
|
4293
|
-
"invalid_request_error",
|
|
4294
|
-
"PLAN_NOT_SELLABLE",
|
|
4295
|
-
`Plan "${planId}" is not sold self-serve. Next action: choose starter or ${PAID_PLAN_ID}, or contact support for Enterprise.`
|
|
4296
|
-
)
|
|
4297
|
-
);
|
|
4298
|
-
}
|
|
4299
|
-
const priceId = priceIdForPlanCycle(planId, cycle);
|
|
4300
|
-
if (!priceId) {
|
|
4301
|
-
return res.status(503).json(BILLING_UNAVAILABLE);
|
|
4302
|
-
}
|
|
4303
|
-
const { data: profile } = await getSupabaseAdmin().from("user_profiles").select(
|
|
4304
|
-
"plan_id, billing_customer_id, billing_subscription_id, billing_status"
|
|
4305
|
-
).eq("id", userId).maybeSingle();
|
|
4306
|
-
if (profile?.billing_subscription_id && typeof profile.billing_status === "string" && OCCUPYING_BILLING_STATUSES.has(profile.billing_status)) {
|
|
4307
|
-
return res.status(409).json(
|
|
4308
|
-
apiError(
|
|
4309
|
-
"invalid_request_error",
|
|
4310
|
-
"SUBSCRIPTION_EXISTS",
|
|
4311
|
-
"This account already has an active subscription. Next action: manage it from the dashboard billing tab instead of creating a new checkout."
|
|
4312
|
-
)
|
|
4313
|
-
);
|
|
4314
|
-
}
|
|
4315
|
-
const openChargeId = await findOpenCharge(userId);
|
|
4316
|
-
if (openChargeId) {
|
|
4317
|
-
return res.status(409).json(checkoutAlreadyOpen(openChargeId));
|
|
4318
|
-
}
|
|
4319
|
-
try {
|
|
4320
|
-
let customerId = profile?.billing_customer_id ?? null;
|
|
4321
|
-
if (!customerId) {
|
|
4322
|
-
customerId = await createStripeCustomer({
|
|
4323
|
-
userId,
|
|
4324
|
-
email: req.billingUserEmail
|
|
4325
|
-
});
|
|
4326
|
-
const { error: bindError } = await getSupabaseAdmin().from("user_profiles").update({ billing_customer_id: customerId }).eq("id", userId);
|
|
4327
|
-
if (bindError) {
|
|
4328
|
-
throw new Error(
|
|
4329
|
-
`failed to store billing_customer_id: ${bindError.message}`
|
|
4330
|
-
);
|
|
4331
|
-
}
|
|
4332
|
-
}
|
|
4333
|
-
const session = await createStripeCheckoutSession({
|
|
4334
|
-
customerId,
|
|
4335
|
-
priceId,
|
|
4336
|
-
userId,
|
|
4337
|
-
planId
|
|
4338
|
-
});
|
|
4339
|
-
await recordChargeCreated({
|
|
4340
|
-
chargeId: session.id,
|
|
4341
|
-
processor: "stripe",
|
|
4342
|
-
userId,
|
|
4343
|
-
planId,
|
|
4344
|
-
cycle,
|
|
4345
|
-
amountUsd: priceForPlanCycle(planId, cycle) ?? 0,
|
|
4346
|
-
planIdBefore: asString(profile?.plan_id)
|
|
4347
|
-
});
|
|
4348
|
-
return res.json({
|
|
4349
|
-
ok: true,
|
|
4350
|
-
data: {
|
|
4351
|
-
url: session.url,
|
|
4352
|
-
session_id: session.id,
|
|
4353
|
-
plan_id: planId,
|
|
4354
|
-
billing_cycle: cycle
|
|
4355
|
-
}
|
|
4356
|
-
});
|
|
4357
|
-
} catch (err) {
|
|
4358
|
-
if (err instanceof StripeApiError) {
|
|
4359
|
-
secureLogger.error("Stripe checkout-session creation failed", err, {
|
|
4360
|
-
userId,
|
|
4361
|
-
status: err.status
|
|
4362
|
-
});
|
|
4363
|
-
return res.status(502).json(
|
|
4364
|
-
apiError(
|
|
4365
|
-
"provider_error",
|
|
4366
|
-
"BILLING_PROVIDER_ERROR",
|
|
4367
|
-
"Stripe rejected the checkout request. Next action: retry; if it persists, contact support@hiveintelligence.xyz."
|
|
4368
|
-
)
|
|
4369
|
-
);
|
|
4370
|
-
}
|
|
4371
|
-
throw err;
|
|
4372
|
-
}
|
|
4373
|
-
})
|
|
4374
|
-
);
|
|
4375
4078
|
function asString(value) {
|
|
4376
4079
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
4377
4080
|
}
|
|
4378
|
-
function refId(value) {
|
|
4379
|
-
if (typeof value === "string") return value || null;
|
|
4380
|
-
if (value && typeof value === "object") {
|
|
4381
|
-
return asString(value.id);
|
|
4382
|
-
}
|
|
4383
|
-
return null;
|
|
4384
|
-
}
|
|
4385
4081
|
function metadataOf(value) {
|
|
4386
4082
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
4387
4083
|
return value;
|
|
4388
4084
|
}
|
|
4389
4085
|
return {};
|
|
4390
4086
|
}
|
|
4391
|
-
function isoFromUnixSeconds(value) {
|
|
4392
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
4393
|
-
return new Date(value * 1e3).toISOString();
|
|
4394
|
-
}
|
|
4395
|
-
async function resolveUserIdByCustomer(customerId) {
|
|
4396
|
-
if (!customerId || !isSupabaseConfigured()) return null;
|
|
4397
|
-
const { data, error } = await getSupabaseAdmin().from("user_profiles").select("id").eq("billing_customer_id", customerId).limit(1);
|
|
4398
|
-
if (error || !data || data.length === 0) return null;
|
|
4399
|
-
return asString(data[0]?.id);
|
|
4400
|
-
}
|
|
4401
|
-
function mapSubscriptionStatusToBilling(status) {
|
|
4402
|
-
switch (status) {
|
|
4403
|
-
case "active":
|
|
4404
|
-
case "trialing":
|
|
4405
|
-
return "active";
|
|
4406
|
-
case "past_due":
|
|
4407
|
-
return "past_due";
|
|
4408
|
-
case "unpaid":
|
|
4409
|
-
return "unpaid";
|
|
4410
|
-
case "canceled":
|
|
4411
|
-
case "incomplete_expired":
|
|
4412
|
-
return "canceled";
|
|
4413
|
-
default:
|
|
4414
|
-
return null;
|
|
4415
|
-
}
|
|
4416
|
-
}
|
|
4417
|
-
function invoiceSubscriptionContext(invoice) {
|
|
4418
|
-
const parent = metadataOf(invoice.parent);
|
|
4419
|
-
const parentDetails = metadataOf(parent.subscription_details);
|
|
4420
|
-
const details = metadataOf(invoice.subscription_details);
|
|
4421
|
-
return {
|
|
4422
|
-
subscriptionId: refId(invoice.subscription) ?? refId(parentDetails.subscription),
|
|
4423
|
-
metadata: {
|
|
4424
|
-
...metadataOf(parentDetails.metadata),
|
|
4425
|
-
...metadataOf(details.metadata)
|
|
4426
|
-
}
|
|
4427
|
-
};
|
|
4428
|
-
}
|
|
4429
|
-
function invoicePeriod(invoice) {
|
|
4430
|
-
const lines = metadataOf(invoice.lines);
|
|
4431
|
-
const data = Array.isArray(lines.data) ? lines.data : [];
|
|
4432
|
-
let best = null;
|
|
4433
|
-
let bestEnd = -Infinity;
|
|
4434
|
-
for (const raw of data) {
|
|
4435
|
-
const line = metadataOf(raw);
|
|
4436
|
-
const period2 = metadataOf(line.period);
|
|
4437
|
-
const end = typeof period2.end === "number" ? period2.end : null;
|
|
4438
|
-
if (end !== null && end > bestEnd) {
|
|
4439
|
-
bestEnd = end;
|
|
4440
|
-
best = line;
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
if (!best) return { cycleStart: null, cycleEnd: null, priceId: null };
|
|
4444
|
-
const period = metadataOf(best.period);
|
|
4445
|
-
const price = metadataOf(best.price);
|
|
4446
|
-
const pricing = metadataOf(best.pricing);
|
|
4447
|
-
const priceDetails = metadataOf(pricing.price_details);
|
|
4448
|
-
return {
|
|
4449
|
-
cycleStart: isoFromUnixSeconds(period.start),
|
|
4450
|
-
cycleEnd: isoFromUnixSeconds(period.end),
|
|
4451
|
-
priceId: asString(price.id) ?? asString(priceDetails.price)
|
|
4452
|
-
};
|
|
4453
|
-
}
|
|
4454
|
-
function cappedCreditCycleEnd(cycleStart, cycleEnd) {
|
|
4455
|
-
if (!cycleStart) return cycleEnd;
|
|
4456
|
-
const start = new Date(cycleStart);
|
|
4457
|
-
if (Number.isNaN(start.getTime())) return cycleEnd;
|
|
4458
|
-
const monthly = creditCycleEndFrom(start);
|
|
4459
|
-
if (!cycleEnd) return monthly.toISOString();
|
|
4460
|
-
const end = new Date(cycleEnd);
|
|
4461
|
-
if (Number.isNaN(end.getTime())) return monthly.toISOString();
|
|
4462
|
-
return (end < monthly ? end : monthly).toISOString();
|
|
4463
|
-
}
|
|
4464
|
-
async function decideWebhookAction(event) {
|
|
4465
|
-
const object = metadataOf(event.data?.object);
|
|
4466
|
-
switch (event.type) {
|
|
4467
|
-
case "checkout.session.completed": {
|
|
4468
|
-
const mode = asString(object.mode);
|
|
4469
|
-
if (mode !== null && mode !== "subscription") {
|
|
4470
|
-
return { kind: "ignore", note: "non-subscription checkout" };
|
|
4471
|
-
}
|
|
4472
|
-
const metadata = metadataOf(object.metadata);
|
|
4473
|
-
const userId = asString(metadata.user_id) ?? asString(object.client_reference_id);
|
|
4474
|
-
if (!userId) {
|
|
4475
|
-
return { kind: "ignore", note: "checkout session without user_id" };
|
|
4476
|
-
}
|
|
4477
|
-
return {
|
|
4478
|
-
kind: "apply",
|
|
4479
|
-
args: {
|
|
4480
|
-
eventId: event.id,
|
|
4481
|
-
eventType: event.type,
|
|
4482
|
-
userId,
|
|
4483
|
-
newPlanId: asString(metadata.plan_id) ?? PAID_PLAN_ID,
|
|
4484
|
-
grantCredits: true,
|
|
4485
|
-
billingStatus: "active",
|
|
4486
|
-
customerId: refId(object.customer),
|
|
4487
|
-
subscriptionId: refId(object.subscription),
|
|
4488
|
-
subscriptionStatus: "active",
|
|
4489
|
-
ledgerReason: "plan_change"
|
|
4490
|
-
}
|
|
4491
|
-
};
|
|
4492
|
-
}
|
|
4493
|
-
case "customer.subscription.updated":
|
|
4494
|
-
case "customer.subscription.deleted": {
|
|
4495
|
-
const metadata = metadataOf(object.metadata);
|
|
4496
|
-
const customerId = refId(object.customer);
|
|
4497
|
-
const userId = asString(metadata.user_id) ?? await resolveUserIdByCustomer(customerId);
|
|
4498
|
-
if (!userId) {
|
|
4499
|
-
return {
|
|
4500
|
-
kind: "unresolved_user",
|
|
4501
|
-
note: `no user binding for customer ${customerId ?? "<none>"}`
|
|
4502
|
-
};
|
|
4503
|
-
}
|
|
4504
|
-
if (event.type === "customer.subscription.deleted") {
|
|
4505
|
-
return {
|
|
4506
|
-
kind: "apply",
|
|
4507
|
-
args: {
|
|
4508
|
-
eventId: event.id,
|
|
4509
|
-
eventType: event.type,
|
|
4510
|
-
userId,
|
|
4511
|
-
newPlanId: FREE_PLAN_ID,
|
|
4512
|
-
grantCredits: true,
|
|
4513
|
-
billingStatus: "canceled",
|
|
4514
|
-
customerId,
|
|
4515
|
-
subscriptionId: refId(object.id),
|
|
4516
|
-
subscriptionStatus: "canceled",
|
|
4517
|
-
ledgerReason: "plan_change"
|
|
4518
|
-
}
|
|
4519
|
-
};
|
|
4520
|
-
}
|
|
4521
|
-
const status = asString(object.status);
|
|
4522
|
-
const items = metadataOf(object.items);
|
|
4523
|
-
const firstItem = metadataOf(
|
|
4524
|
-
Array.isArray(items.data) ? items.data[0] : void 0
|
|
4525
|
-
);
|
|
4526
|
-
const priceId = asString(metadataOf(firstItem.price).id);
|
|
4527
|
-
return {
|
|
4528
|
-
kind: "apply",
|
|
4529
|
-
args: {
|
|
4530
|
-
eventId: event.id,
|
|
4531
|
-
eventType: event.type,
|
|
4532
|
-
userId,
|
|
4533
|
-
// Tier/status sync only — credits ride the money event
|
|
4534
|
-
// (invoice.paid), which is the one wallet clock.
|
|
4535
|
-
newPlanId: planIdForPrice(priceId) ?? asString(metadata.plan_id) ?? null,
|
|
4536
|
-
grantCredits: false,
|
|
4537
|
-
billingStatus: mapSubscriptionStatusToBilling(status),
|
|
4538
|
-
customerId,
|
|
4539
|
-
subscriptionId: refId(object.id),
|
|
4540
|
-
// Whitelisted, never forwarded raw. stripe_subscriptions.status has
|
|
4541
|
-
// a CHECK over eight literals; a status Stripe adds later raises
|
|
4542
|
-
// 23514 *inside* apply_plan_change, which rolls back the dedupe
|
|
4543
|
-
// claim and the wallet grant with it, 500s, and leaves Stripe
|
|
4544
|
-
// retrying forever. Null here means "skip the registry upsert",
|
|
4545
|
-
// which still applies the tier and still acks 200.
|
|
4546
|
-
subscriptionStatus: registrySubscriptionStatus(status),
|
|
4547
|
-
priceId,
|
|
4548
|
-
ledgerReason: "plan_change"
|
|
4549
|
-
}
|
|
4550
|
-
};
|
|
4551
|
-
}
|
|
4552
|
-
case "invoice.paid":
|
|
4553
|
-
case "invoice.payment_failed": {
|
|
4554
|
-
const { subscriptionId, metadata } = invoiceSubscriptionContext(object);
|
|
4555
|
-
if (!subscriptionId) {
|
|
4556
|
-
return { kind: "ignore", note: "invoice without a subscription" };
|
|
4557
|
-
}
|
|
4558
|
-
const customerId = refId(object.customer);
|
|
4559
|
-
const userId = asString(metadata.user_id) ?? await resolveUserIdByCustomer(customerId);
|
|
4560
|
-
if (!userId) {
|
|
4561
|
-
return {
|
|
4562
|
-
kind: "unresolved_user",
|
|
4563
|
-
note: `no user binding for customer ${customerId ?? "<none>"}`
|
|
4564
|
-
};
|
|
4565
|
-
}
|
|
4566
|
-
if (event.type === "invoice.payment_failed") {
|
|
4567
|
-
return {
|
|
4568
|
-
kind: "apply",
|
|
4569
|
-
args: {
|
|
4570
|
-
eventId: event.id,
|
|
4571
|
-
eventType: event.type,
|
|
4572
|
-
userId,
|
|
4573
|
-
billingStatus: "past_due",
|
|
4574
|
-
customerId,
|
|
4575
|
-
subscriptionId,
|
|
4576
|
-
ledgerReason: "plan_change"
|
|
4577
|
-
}
|
|
4578
|
-
};
|
|
4579
|
-
}
|
|
4580
|
-
const period = invoicePeriod(object);
|
|
4581
|
-
const creditCycleEnd = cappedCreditCycleEnd(
|
|
4582
|
-
period.cycleStart,
|
|
4583
|
-
period.cycleEnd
|
|
4584
|
-
);
|
|
4585
|
-
return {
|
|
4586
|
-
kind: "apply",
|
|
4587
|
-
args: {
|
|
4588
|
-
eventId: event.id,
|
|
4589
|
-
eventType: event.type,
|
|
4590
|
-
userId,
|
|
4591
|
-
newPlanId: planIdForPrice(period.priceId) ?? asString(metadata.plan_id) ?? null,
|
|
4592
|
-
grantCredits: true,
|
|
4593
|
-
cycleStart: period.cycleStart,
|
|
4594
|
-
cycleEnd: creditCycleEnd,
|
|
4595
|
-
termEnd: period.cycleEnd,
|
|
4596
|
-
billingStatus: "active",
|
|
4597
|
-
customerId,
|
|
4598
|
-
subscriptionId,
|
|
4599
|
-
priceId: period.priceId,
|
|
4600
|
-
ledgerReason: "refill"
|
|
4601
|
-
}
|
|
4602
|
-
};
|
|
4603
|
-
}
|
|
4604
|
-
default:
|
|
4605
|
-
return { kind: "ignore", note: `unhandled event type ${event.type}` };
|
|
4606
|
-
}
|
|
4607
|
-
}
|
|
4608
|
-
var stripeWebhookHandler = asyncHandler(
|
|
4609
|
-
async (req, res) => {
|
|
4610
|
-
const secret = getStripeWebhookSecret();
|
|
4611
|
-
if (!secret) {
|
|
4612
|
-
return res.status(503).json(BILLING_UNAVAILABLE);
|
|
4613
|
-
}
|
|
4614
|
-
const raw = req.body;
|
|
4615
|
-
if (!Buffer.isBuffer(raw)) {
|
|
4616
|
-
secureLogger.error(
|
|
4617
|
-
"Stripe webhook received a non-raw body \u2014 express.raw must mount before the JSON parser",
|
|
4618
|
-
void 0,
|
|
4619
|
-
{ path: req.path }
|
|
4620
|
-
);
|
|
4621
|
-
return res.status(500).json(
|
|
4622
|
-
apiError(
|
|
4623
|
-
"internal_error",
|
|
4624
|
-
"WEBHOOK_BODY_NOT_RAW",
|
|
4625
|
-
"Webhook raw body unavailable; server middleware misconfigured."
|
|
4626
|
-
)
|
|
4627
|
-
);
|
|
4628
|
-
}
|
|
4629
|
-
const verification = verifyStripeSignature({
|
|
4630
|
-
payload: raw,
|
|
4631
|
-
header: req.headers["stripe-signature"],
|
|
4632
|
-
secret
|
|
4633
|
-
});
|
|
4634
|
-
if (!verification.valid) {
|
|
4635
|
-
secureLogger.warn("Stripe webhook signature rejected", {
|
|
4636
|
-
reason: verification.reason
|
|
4637
|
-
});
|
|
4638
|
-
return res.status(400).json(
|
|
4639
|
-
apiError(
|
|
4640
|
-
"authentication_error",
|
|
4641
|
-
"INVALID_WEBHOOK_SIGNATURE",
|
|
4642
|
-
`Stripe signature verification failed (${verification.reason}).`
|
|
4643
|
-
)
|
|
4644
|
-
);
|
|
4645
|
-
}
|
|
4646
|
-
let event;
|
|
4647
|
-
try {
|
|
4648
|
-
event = JSON.parse(raw.toString("utf8"));
|
|
4649
|
-
} catch {
|
|
4650
|
-
return res.status(400).json(
|
|
4651
|
-
apiError(
|
|
4652
|
-
"invalid_request_error",
|
|
4653
|
-
"INVALID_JSON",
|
|
4654
|
-
"Webhook payload is not valid JSON."
|
|
4655
|
-
)
|
|
4656
|
-
);
|
|
4657
|
-
}
|
|
4658
|
-
if (!asString(event?.id) || !asString(event?.type)) {
|
|
4659
|
-
return res.status(400).json(
|
|
4660
|
-
apiError(
|
|
4661
|
-
"invalid_request_error",
|
|
4662
|
-
"INVALID_EVENT",
|
|
4663
|
-
"Webhook payload is missing event id or type."
|
|
4664
|
-
)
|
|
4665
|
-
);
|
|
4666
|
-
}
|
|
4667
|
-
try {
|
|
4668
|
-
const decision = await decideWebhookAction(event);
|
|
4669
|
-
if (decision.kind === "ignore") {
|
|
4670
|
-
return res.json({ received: true, handled: false });
|
|
4671
|
-
}
|
|
4672
|
-
if (decision.kind === "unresolved_user") {
|
|
4673
|
-
secureLogger.warn("Stripe webhook user unresolved \u2014 asking for retry", {
|
|
4674
|
-
eventType: event.type,
|
|
4675
|
-
note: decision.note
|
|
4676
|
-
});
|
|
4677
|
-
return res.status(500).json(
|
|
4678
|
-
apiError(
|
|
4679
|
-
"internal_error",
|
|
4680
|
-
"WEBHOOK_USER_UNRESOLVED",
|
|
4681
|
-
"Could not resolve the Hive user for this event yet; Stripe should retry."
|
|
4682
|
-
)
|
|
4683
|
-
);
|
|
4684
|
-
}
|
|
4685
|
-
const result = await applyPlanChange(decision.args);
|
|
4686
|
-
if (event.type === "checkout.session.completed") {
|
|
4687
|
-
await settleChargeRecord(
|
|
4688
|
-
asString(metadataOf(event.data?.object).id),
|
|
4689
|
-
"confirmed",
|
|
4690
|
-
event.type
|
|
4691
|
-
);
|
|
4692
|
-
}
|
|
4693
|
-
return res.json({
|
|
4694
|
-
received: true,
|
|
4695
|
-
handled: true,
|
|
4696
|
-
outcome: result.outcome,
|
|
4697
|
-
replayed: result.replayed
|
|
4698
|
-
});
|
|
4699
|
-
} catch (err) {
|
|
4700
|
-
secureLogger.error("Stripe webhook processing failed", err, {
|
|
4701
|
-
eventType: event.type
|
|
4702
|
-
});
|
|
4703
|
-
return res.status(500).json(
|
|
4704
|
-
apiError(
|
|
4705
|
-
"internal_error",
|
|
4706
|
-
"WEBHOOK_PROCESSING_FAILED",
|
|
4707
|
-
"Webhook processing failed; Stripe should retry. Processing is idempotent by event id."
|
|
4708
|
-
)
|
|
4709
|
-
);
|
|
4710
|
-
}
|
|
4711
|
-
}
|
|
4712
|
-
);
|
|
4713
4087
|
var LLAMAPAY_UNAVAILABLE = apiError(
|
|
4714
4088
|
"internal_error",
|
|
4715
4089
|
"BILLING_UNAVAILABLE",
|
|
@@ -5879,7 +5253,7 @@ function buildServerCard() {
|
|
|
5879
5253
|
}
|
|
5880
5254
|
|
|
5881
5255
|
// src/auth/oauth/router.ts
|
|
5882
|
-
import { timingSafeEqual as
|
|
5256
|
+
import { timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
5883
5257
|
import express from "express";
|
|
5884
5258
|
import rateLimit2 from "express-rate-limit";
|
|
5885
5259
|
import { z as z4 } from "zod";
|
|
@@ -5932,7 +5306,7 @@ function monitorSecretAuthorized(req) {
|
|
|
5932
5306
|
if (!expected || typeof supplied !== "string") return false;
|
|
5933
5307
|
const left = Buffer.from(supplied);
|
|
5934
5308
|
const right = Buffer.from(expected);
|
|
5935
|
-
return left.length === right.length &&
|
|
5309
|
+
return left.length === right.length && timingSafeEqual4(left, right);
|
|
5936
5310
|
}
|
|
5937
5311
|
function deploymentCanaryUserId() {
|
|
5938
5312
|
const userId = getEnvVar("HIVE_CANARY_USER_ID");
|
|
@@ -6774,11 +6148,6 @@ app.use(
|
|
|
6774
6148
|
type: "application/json"
|
|
6775
6149
|
})
|
|
6776
6150
|
);
|
|
6777
|
-
app.post(
|
|
6778
|
-
"/api/v1/billing/webhook",
|
|
6779
|
-
express2.raw({ type: "application/json", limit: "1mb" }),
|
|
6780
|
-
stripeWebhookHandler
|
|
6781
|
-
);
|
|
6782
6151
|
app.post(
|
|
6783
6152
|
"/api/v1/billing/webhook/llamapay",
|
|
6784
6153
|
express2.raw({ type: "application/json", limit: "1mb" }),
|
|
@@ -7334,7 +6703,7 @@ function isMonitorSecretRequest(req) {
|
|
|
7334
6703
|
if (!secret || typeof provided !== "string") return false;
|
|
7335
6704
|
const a = Buffer.from(provided);
|
|
7336
6705
|
const b = Buffer.from(secret);
|
|
7337
|
-
return a.length === b.length &&
|
|
6706
|
+
return a.length === b.length && timingSafeEqual5(a, b);
|
|
7338
6707
|
}
|
|
7339
6708
|
function getValidatedMcpHiveToolName(scope, body) {
|
|
7340
6709
|
if (body?.method !== "tools/call") return void 0;
|
|
@@ -8058,7 +7427,7 @@ app.post(
|
|
|
8058
7427
|
if (monitorSecret && typeof authToken === "string") {
|
|
8059
7428
|
const a = Buffer.from(authToken);
|
|
8060
7429
|
const b = Buffer.from(monitorSecret);
|
|
8061
|
-
isAuthorized = a.length === b.length &&
|
|
7430
|
+
isAuthorized = a.length === b.length && timingSafeEqual5(a, b);
|
|
8062
7431
|
}
|
|
8063
7432
|
secureLogger.authEvent("monitor-tests-access", isAuthorized, {
|
|
8064
7433
|
hasToken: !!authToken,
|