zen: data share

This commit is contained in:
Frank
2025-09-19 14:08:02 -04:00
parent c21161b75e
commit 9420d80b73
27 changed files with 1408 additions and 719 deletions

View File

@@ -56,6 +56,7 @@
"@solidjs/start": "^1.1.0", "@solidjs/start": "^1.1.0",
"solid-js": "catalog:", "solid-js": "catalog:",
"vinxi": "^0.5.7", "vinxi": "^0.5.7",
"zod": "catalog:",
}, },
}, },
"packages/console/core": { "packages/console/core": {

View File

@@ -99,11 +99,7 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
], ],
}) })
const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY") const ZEN_MODELS = new sst.Secret("ZEN_MODELS")
const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY")
const XAI_API_KEY = new sst.Secret("XAI_API_KEY")
const BASETEN_API_KEY = new sst.Secret("BASETEN_API_KEY")
const FIREWORKS_API_KEY = new sst.Secret("FIREWORKS_API_KEY")
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
properties: { value: auth.url.apply((url) => url!) }, properties: { value: auth.url.apply((url) => url!) },
@@ -128,17 +124,7 @@ if ($app.stage === "production" || $app.stage === "frank") {
new sst.cloudflare.x.SolidStart("Console", { new sst.cloudflare.x.SolidStart("Console", {
domain, domain,
path: "packages/console/app", path: "packages/console/app",
link: [ link: [database, AUTH_API_URL, STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, ZEN_MODELS],
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
STRIPE_SECRET_KEY,
ANTHROPIC_API_KEY,
OPENAI_API_KEY,
XAI_API_KEY,
BASETEN_API_KEY,
FIREWORKS_API_KEY,
],
environment: { environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!), //VITE_DOCS_URL: web.url.apply((url) => url!),
//VITE_API_URL: gateway.url.apply((url) => url!), //VITE_API_URL: gateway.url.apply((url) => url!),

View File

@@ -12,12 +12,13 @@
"dependencies": { "dependencies": {
"@ibm/plex": "6.4.1", "@ibm/plex": "6.4.1",
"@openauthjs/openauth": "0.0.0-20250322224806", "@openauthjs/openauth": "0.0.0-20250322224806",
"@opencode/console-core": "workspace:*",
"@solidjs/meta": "^0.29.4", "@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0", "@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0", "@solidjs/start": "^1.1.0",
"solid-js": "catalog:", "solid-js": "catalog:",
"vinxi": "^0.5.7", "vinxi": "^0.5.7",
"@opencode/console-core": "workspace:*" "zod": "catalog:"
}, },
"engines": { "engines": {
"node": ">=22" "node": ">=22"

View File

@@ -0,0 +1,114 @@
.root {
[data-slot="section-content"] {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
[data-slot="reload-error"] {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-4);
padding: var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
p {
color: var(--color-danger);
font-size: var(--font-size-sm);
line-height: 1.4;
margin: 0;
flex: 1;
}
[data-slot="create-form"] {
display: flex;
gap: var(--space-2);
margin: 0;
flex-shrink: 0;
}
}
[data-slot="payment"] {
display: flex;
flex-direction: column;
gap: var(--space-3);
padding: var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
min-width: 14.5rem;
width: fit-content;
@media (max-width: 30rem) {
width: 100%;
}
[data-slot="credit-card"] {
padding: var(--space-3-5) var(--space-4);
background-color: var(--color-bg-surface);
border-radius: var(--border-radius-sm);
display: flex;
align-items: center;
justify-content: space-between;
[data-slot="card-icon"] {
display: flex;
align-items: center;
color: var(--color-text-muted);
}
[data-slot="card-details"] {
display: flex;
align-items: baseline;
gap: var(--space-1);
[data-slot="secret"] {
position: relative;
bottom: 2px;
font-size: var(--font-size-lg);
color: var(--color-text-muted);
font-weight: 400;
}
[data-slot="number"] {
font-size: var(--font-size-3xl);
font-weight: 500;
color: var(--color-text);
}
}
}
[data-slot="button-row"] {
display: flex;
gap: var(--space-2);
align-items: center;
@media (max-width: 30rem) {
flex-direction: column;
> button {
width: 100%;
}
}
[data-slot="create-form"] {
margin: 0;
}
/* Make Enable Billing button full width when it's the only button */
> button {
flex: 1;
}
}
}
[data-slot="usage"] {
p {
font-size: var(--font-size-sm);
line-height: 1.5;
color: var(--color-text-secondary);
b {
font-weight: 600;
}
}
}
}

View File

@@ -0,0 +1,98 @@
import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router"
import { withActor } from "~/context/auth.withActor"
import styles from "./billing-section.module.css"
import { Database, eq } from "@opencode/console-core/drizzle/index.js"
import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js"
import { Show } from "solid-js"
const updateShare = action(async (form: FormData) => {
"use server"
const workspaceID = form.get("workspaceID")?.toString()
if (!workspaceID) return { error: "Workspace ID is required" }
const dataShare = form.get("dataShare")?.toString() === "true" ? true : null
return json(
await withActor(() => {
return Database.use((tx) =>
tx
.update(WorkspaceTable)
.set({
dataShare,
})
.where(eq(WorkspaceTable.id, workspaceID)),
)
}, workspaceID),
{ revalidate: getWorkspaceInfo.key },
)
}, "workspace.disableShare")
const getWorkspaceInfo = query(async (workspaceID: string) => {
"use server"
return withActor(() => {
return Database.use((tx) =>
tx
.select({
dataShare: WorkspaceTable.dataShare,
})
.from(WorkspaceTable)
.where(eq(WorkspaceTable.id, workspaceID))
.then((r) => r[0]),
)
}, workspaceID)
}, "workspace.get")
export function PrivacySection() {
const params = useParams()
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id))
const updateShareSubmission = useSubmission(updateShare)
return (
<section class={styles.root}>
<div data-slot="section-title">
<h2>Privacy controls</h2>
<p>
Some providers offer data-sharing programs. If you opt in, you voluntarily <b>share your usage data</b> with
them, which they may use to improve their services, including <b>model training</b>.
</p>
<br />
<p>
By opting in, you gain access to <b>discounted pricing</b> from the provider. You can opt in or out at any
time.
</p>
<br />
<p>
<a target="_blank" href="/docs/zen">
Learn more
</a>
</p>
</div>
<Show when={workspaceInfo()?.dataShare}>
<div data-slot="payment">
<div data-slot="credit-card">
<div data-slot="card-details">
<span data-slot="number">You are currently opted in to the data-sharing program.</span>
</div>
</div>
</div>
</Show>
<div data-slot="section-content">
<div data-slot="payment">
<div data-slot="button-row">
<form action={updateShare} method="post" data-slot="create-form">
<input type="hidden" name="workspaceID" value={params.id} />
<input type="hidden" name="dataShare" value={workspaceInfo()?.dataShare ? "false" : "true"} />
<button data-color="ghost" type="submit" disabled={updateShareSubmission.pending}>
{workspaceInfo()?.dataShare
? updateShareSubmission.pending
? "Opting out..."
: "Opt out"
: updateShareSubmission.pending
? "Opting in..."
: "Opt in"}
</button>
</form>
</div>
</div>
</div>
</section>
)
}

View File

@@ -2,11 +2,15 @@ import "./[id].css"
import { MonthlyLimitSection } from "~/component/workspace/monthly-limit-section" import { MonthlyLimitSection } from "~/component/workspace/monthly-limit-section"
import { NewUserSection } from "~/component/workspace/new-user-section" import { NewUserSection } from "~/component/workspace/new-user-section"
import { BillingSection } from "~/component/workspace/billing-section" import { BillingSection } from "~/component/workspace/billing-section"
import { PrivacySection } from "~/component/workspace/privacy-section"
import { PaymentSection } from "~/component/workspace/payment-section" import { PaymentSection } from "~/component/workspace/payment-section"
import { UsageSection } from "~/component/workspace/usage-section" import { UsageSection } from "~/component/workspace/usage-section"
import { KeySection } from "~/component/workspace/key-section" import { KeySection } from "~/component/workspace/key-section"
import { Show } from "solid-js"
import { useParams } from "@solidjs/router"
export default function () { export default function () {
const params = useParams()
return ( return (
<div data-page="workspace-[id]"> <div data-page="workspace-[id]">
<section data-component="title-section"> <section data-component="title-section">
@@ -25,9 +29,20 @@ export default function () {
<KeySection /> <KeySection />
<BillingSection /> <BillingSection />
<MonthlyLimitSection /> <MonthlyLimitSection />
<Show when={isBeta(params.id)}>
<PrivacySection />
</Show>
<UsageSection /> <UsageSection />
<PaymentSection /> <PaymentSection />
</div> </div>
</div> </div>
) )
} }
export function isBeta(workspaceID: string) {
return [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // production
"wrk_01K4NFRR5P7FSYWH88307B4DDS", // dev
"wrk_01K4PJRKJ2WPQZN3FFYRV4673F", // frank
].includes(workspaceID)
}

View File

@@ -1,37 +1,15 @@
import { z } from "zod"
import type { APIEvent } from "@solidjs/start/server" import type { APIEvent } from "@solidjs/start/server"
import path from "node:path" import path from "node:path"
import { and, Database, eq, isNull, lt, or, sql } from "@opencode/console-core/drizzle/index.js" import { and, Database, eq, isNull, lt, or, sql } from "@opencode/console-core/drizzle/index.js"
import { KeyTable } from "@opencode/console-core/schema/key.sql.js" import { KeyTable } from "@opencode/console-core/schema/key.sql.js"
import { BillingTable, PaymentTable, UsageTable } from "@opencode/console-core/schema/billing.sql.js" import { BillingTable, UsageTable } from "@opencode/console-core/schema/billing.sql.js"
import { centsToMicroCents } from "@opencode/console-core/util/price.js" import { centsToMicroCents } from "@opencode/console-core/util/price.js"
import { Identifier } from "@opencode/console-core/identifier.js" import { Identifier } from "@opencode/console-core/identifier.js"
import { Resource } from "@opencode/console-resource" import { Resource } from "@opencode/console-resource"
import { Billing } from "../../../../core/src/billing" import { Billing } from "../../../../core/src/billing"
import { Actor } from "@opencode/console-core/actor.js" import { Actor } from "@opencode/console-core/actor.js"
import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js"
type ModelCost = {
input: number
output: number
cacheRead?: number
cacheWrite5m?: number
cacheWrite1h?: number
}
type Model = {
id: string
auth: boolean
cost: ModelCost | ((usage: any) => ModelCost)
headerMappings: Record<string, string>
providers: Record<
string,
{
api: string
apiKey: string
model: string
weight?: number
}
>
}
export async function handler( export async function handler(
input: APIEvent, input: APIEvent,
@@ -56,184 +34,32 @@ export async function handler(
class MonthlyLimitError extends Error {} class MonthlyLimitError extends Error {}
class ModelError extends Error {} class ModelError extends Error {}
const MODELS: Record<string, Model> = { const ModelCostSchema = z.object({
"claude-opus-4-1": { input: z.number(),
id: "claude-opus-4-1" as const, output: z.number(),
auth: true, cacheRead: z.number().optional(),
cost: { cacheWrite5m: z.number().optional(),
input: 0.000015, cacheWrite1h: z.number().optional(),
output: 0.000075, })
cacheRead: 0.0000015,
cacheWrite5m: 0.00001875, const ModelSchema = z.object({
cacheWrite1h: 0.00003, cost: ModelCostSchema,
}, cost200K: ModelCostSchema.optional(),
headerMappings: {}, providers: z.array(
providers: { z.object({
anthropic: { id: z.string(),
api: "https://api.anthropic.com", api: z.string(),
apiKey: Resource.ANTHROPIC_API_KEY.value, apiKey: z.string(),
model: "claude-opus-4-1-20250805", model: z.string(),
}, weight: z.number().optional(),
}, allowAnonymous: z.boolean().optional(),
}, headerMappings: z.record(z.string(), z.string()).optional(),
"claude-sonnet-4": { disabled: z.boolean().optional(),
id: "claude-sonnet-4" as const, }),
auth: true, ),
cost: (usage: any) => { })
const totalInputTokens =
usage.inputTokens + usage.cacheReadTokens + usage.cacheWrite5mTokens + usage.cacheWrite1hTokens type Model = z.infer<typeof ModelSchema>
return totalInputTokens <= 200_000
? {
input: 0.000003,
output: 0.000015,
cacheRead: 0.0000003,
cacheWrite5m: 0.00000375,
cacheWrite1h: 0.000006,
}
: {
input: 0.000006,
output: 0.0000225,
cacheRead: 0.0000006,
cacheWrite5m: 0.0000075,
cacheWrite1h: 0.000012,
}
},
headerMappings: {},
providers: {
anthropic: {
api: "https://api.anthropic.com",
apiKey: Resource.ANTHROPIC_API_KEY.value,
model: "claude-sonnet-4-20250514",
},
},
},
"claude-3-5-haiku": {
id: "claude-3-5-haiku" as const,
auth: true,
cost: {
input: 0.0000008,
output: 0.000004,
cacheRead: 0.00000008,
cacheWrite5m: 0.000001,
cacheWrite1h: 0.0000016,
},
headerMappings: {},
providers: {
anthropic: {
api: "https://api.anthropic.com",
apiKey: Resource.ANTHROPIC_API_KEY.value,
model: "claude-3-5-haiku-20241022",
},
},
},
"gpt-5": {
id: "gpt-5" as const,
auth: true,
cost: {
input: 0.00000125,
output: 0.00001,
cacheRead: 0.000000125,
},
headerMappings: {},
providers: {
openai: {
api: "https://api.openai.com",
apiKey: Resource.OPENAI_API_KEY.value,
model: "gpt-5",
},
},
},
"qwen3-coder": {
id: "qwen3-coder" as const,
auth: true,
cost: {
input: 0.00000045,
output: 0.0000018,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
weight: 4,
},
fireworks: {
api: "https://api.fireworks.ai/inference",
apiKey: Resource.FIREWORKS_API_KEY.value,
model: "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct",
weight: 1,
},
},
},
"kimi-k2": {
id: "kimi-k2" as const,
auth: true,
cost: {
input: 0.0000006,
output: 0.0000025,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "moonshotai/Kimi-K2-Instruct-0905",
//weight: 4,
},
//fireworks: {
// api: "https://api.fireworks.ai/inference",
// apiKey: Resource.FIREWORKS_API_KEY.value,
// model: "accounts/fireworks/models/kimi-k2-instruct-0905",
// weight: 1,
//},
},
},
"grok-code": {
id: "grok-code" as const,
auth: false,
cost: {
input: 0,
output: 0,
cacheRead: 0,
},
headerMappings: {
"x-grok-conv-id": "x-opencode-session",
"x-grok-req-id": "x-opencode-request",
},
providers: {
xai: {
api: "https://api.x.ai",
apiKey: Resource.XAI_API_KEY.value,
model: "grok-code",
},
},
},
// deprecated
"qwen/qwen3-coder": {
id: "qwen/qwen3-coder" as const,
auth: true,
cost: {
input: 0.00000038,
output: 0.00000153,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
weight: 5,
},
fireworks: {
api: "https://api.fireworks.ai/inference",
apiKey: Resource.FIREWORKS_API_KEY.value,
model: "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct",
weight: 1,
},
},
},
}
const FREE_WORKSPACES = [ const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank "wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
@@ -259,31 +85,28 @@ export async function handler(
session: input.request.headers.get("x-opencode-session"), session: input.request.headers.get("x-opencode-session"),
request: input.request.headers.get("x-opencode-request"), request: input.request.headers.get("x-opencode-request"),
}) })
const MODEL = validateModel() const authInfo = await authenticate()
const apiKey = await authenticate() const modelInfo = validateModel(body.model, authInfo)
const isFree = FREE_WORKSPACES.includes(apiKey?.workspaceID ?? "") const providerInfo = selectProvider(modelInfo, authInfo)
await checkCreditsAndLimit() logger.metric({ provider: providerInfo.id })
const providerName = selectProvider()
const providerData = MODEL.providers[providerName]
logger.metric({ provider: providerName })
// Request to model provider // Request to model provider
const startTimestamp = Date.now() const startTimestamp = Date.now()
const res = await fetch(path.posix.join(providerData.api, url.pathname.replace(/^\/zen/, "") + url.search), { const res = await fetch(path.posix.join(providerInfo.api, url.pathname.replace(/^\/zen/, "") + url.search), {
method: "POST", method: "POST",
headers: (() => { headers: (() => {
const headers = input.request.headers const headers = input.request.headers
headers.delete("host") headers.delete("host")
headers.delete("content-length") headers.delete("content-length")
opts.setAuthHeader(headers, providerData.apiKey) opts.setAuthHeader(headers, providerInfo.apiKey)
Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => { Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
headers.set(k, headers.get(v)!) headers.set(k, headers.get(v)!)
}) })
return headers return headers
})(), })(),
body: JSON.stringify({ body: JSON.stringify({
...(opts.modifyBody?.(body) ?? body), ...(opts.modifyBody?.(body) ?? body),
model: providerData.model, model: providerInfo.model,
}), }),
}) })
@@ -302,8 +125,8 @@ export async function handler(
const body = JSON.stringify(json) const body = JSON.stringify(json)
logger.metric({ response_length: body.length }) logger.metric({ response_length: body.length })
logger.debug(body) logger.debug(body)
await trackUsage(json.usage) await trackUsage(authInfo, modelInfo, providerInfo.id, json.usage)
await reload() await reload(authInfo)
return new Response(body, { return new Response(body, {
status: res.status, status: res.status,
statusText: res.statusText, statusText: res.statusText,
@@ -326,8 +149,8 @@ export async function handler(
logger.metric({ response_length: responseLength }) logger.metric({ response_length: responseLength })
const usage = opts.getStreamUsage() const usage = opts.getStreamUsage()
if (usage) { if (usage) {
await trackUsage(usage) await trackUsage(authInfo, modelInfo, providerInfo.id, usage)
await reload() await reload(authInfo)
} }
c.close() c.close()
return return
@@ -337,6 +160,7 @@ export async function handler(
logger.metric({ time_to_first_byte: Date.now() - startTimestamp }) logger.metric({ time_to_first_byte: Date.now() - startTimestamp })
} }
responseLength += value.length responseLength += value.length
console.log(decoder.decode(value, { stream: true }))
buffer += decoder.decode(value, { stream: true }) buffer += decoder.decode(value, { stream: true })
const parts = buffer.split("\n\n") const parts = buffer.split("\n\n")
@@ -363,92 +187,146 @@ export async function handler(
statusText: res.statusText, statusText: res.statusText,
headers: resHeaders, headers: resHeaders,
}) })
} catch (error: any) {
logger.metric({
"error.type": error.constructor.name,
"error.message": error.message,
})
function validateModel() { // Note: both top level "type" and "error.type" fields are used by the @ai-sdk/anthropic client to render the error message.
if (!(body.model in MODELS)) { if (
throw new ModelError(`Model ${body.model} not supported`) error instanceof AuthError ||
} error instanceof CreditsError ||
const model = MODELS[body.model as keyof typeof MODELS] error instanceof MonthlyLimitError ||
logger.metric({ model: model.id }) error instanceof ModelError
return model )
return new Response(
JSON.stringify({
type: "error",
error: { type: error.constructor.name, message: error.message },
}),
{ status: 401 },
)
return new Response(
JSON.stringify({
type: "error",
error: {
type: "error",
message: error.message,
},
}),
{ status: 500 },
)
} }
async function authenticate() { async function authenticate() {
try {
const apiKey = opts.parseApiKey(input.request.headers) const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey) throw new AuthError("Missing API key.") if (!apiKey) return
const key = await Database.use((tx) => const data = await Database.use((tx) =>
tx tx
.select({ .select({
id: KeyTable.id, apiKey: KeyTable.id,
workspaceID: KeyTable.workspaceID, workspaceID: KeyTable.workspaceID,
}) dataShare: WorkspaceTable.dataShare,
.from(KeyTable)
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
.then((rows) => rows[0]),
)
if (!key) throw new AuthError("Invalid API key.")
logger.metric({
api_key: key.id,
workspace: key.workspaceID,
})
return key
} catch (e) {
// ignore error if model does not require authentication
if (!MODEL.auth) return
throw e
}
}
async function checkCreditsAndLimit() {
if (!apiKey || !MODEL.auth || isFree) return
const billing = await Database.use((tx) =>
tx
.select({
balance: BillingTable.balance, balance: BillingTable.balance,
paymentMethodID: BillingTable.paymentMethodID, paymentMethodID: BillingTable.paymentMethodID,
monthlyLimit: BillingTable.monthlyLimit, monthlyLimit: BillingTable.monthlyLimit,
monthlyUsage: BillingTable.monthlyUsage, monthlyUsage: BillingTable.monthlyUsage,
timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated, timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated,
}) })
.from(BillingTable) .from(KeyTable)
.where(eq(BillingTable.workspaceID, apiKey.workspaceID)) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
.innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID))
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
.then((rows) => rows[0]), .then((rows) => rows[0]),
) )
if (!billing.paymentMethodID) throw new CreditsError("No payment method") if (!data) throw new AuthError("Invalid API key.")
if (billing.balance <= 0) throw new CreditsError("Insufficient balance") logger.metric({
api_key: data.apiKey,
workspace: data.workspaceID,
})
const isFree = FREE_WORKSPACES.includes(data.workspaceID)
if (!isFree) {
if (!data.paymentMethodID) throw new CreditsError("No payment method")
if (data.balance <= 0) throw new CreditsError("Insufficient balance")
if ( if (
billing.monthlyLimit && data.monthlyLimit &&
billing.monthlyUsage && data.monthlyUsage &&
billing.timeMonthlyUsageUpdated && data.timeMonthlyUsageUpdated &&
billing.monthlyUsage >= centsToMicroCents(billing.monthlyLimit * 100) data.monthlyUsage >= centsToMicroCents(data.monthlyLimit * 100)
) { ) {
const now = new Date() const now = new Date()
const currentYear = now.getUTCFullYear() const currentYear = now.getUTCFullYear()
const currentMonth = now.getUTCMonth() const currentMonth = now.getUTCMonth()
const dateYear = billing.timeMonthlyUsageUpdated.getUTCFullYear() const dateYear = data.timeMonthlyUsageUpdated.getUTCFullYear()
const dateMonth = billing.timeMonthlyUsageUpdated.getUTCMonth() const dateMonth = data.timeMonthlyUsageUpdated.getUTCMonth()
if (currentYear === dateYear && currentMonth === dateMonth) if (currentYear === dateYear && currentMonth === dateMonth)
throw new MonthlyLimitError(`You have reached your monthly spending limit of $${billing.monthlyLimit}.`) throw new MonthlyLimitError(`You have reached your monthly spending limit of $${data.monthlyLimit}.`)
} }
} }
function selectProvider() { return {
const picks = Object.entries(MODEL.providers).flatMap(([name, provider]) => apiKeyId: data.apiKey,
Array<string>(provider.weight ?? 1).fill(name), workspaceID: data.workspaceID,
dataShare: data.dataShare,
isFree,
}
}
function validateModel(reqModel: string, authInfo: Awaited<ReturnType<typeof authenticate>>) {
const json = JSON.parse(Resource.ZEN_MODELS.value)
const allModels = z
.record(
z.string(),
z.object({
standard: ModelSchema,
dataShare: ModelSchema.optional(),
}),
) )
.parse(json)
if (!(reqModel in allModels)) {
throw new ModelError(`Model ${reqModel} not supported`)
}
const modelId = reqModel as keyof typeof allModels
const modelData = authInfo?.dataShare
? (allModels[modelId].dataShare ?? allModels[modelId].standard)
: allModels[modelId].standard
logger.metric({ model: modelId })
return { id: modelId, ...modelData }
}
function selectProvider(model: Model, authInfo: Awaited<ReturnType<typeof authenticate>>) {
let providers = model.providers.filter((provider) => !provider.disabled)
if (!authInfo) {
providers = providers.filter((provider) => provider.allowAnonymous)
if (providers.length === 0) throw new AuthError("Missing API key.")
}
const picks = providers.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
return picks[Math.floor(Math.random() * picks.length)] return picks[Math.floor(Math.random() * picks.length)]
} }
async function trackUsage(usage: any) { async function trackUsage(
authInfo: Awaited<ReturnType<typeof authenticate>>,
modelInfo: ReturnType<typeof validateModel>,
providerId: string,
usage: any,
) {
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } = const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
opts.normalizeUsage(usage) opts.normalizeUsage(usage)
const modelCost = typeof MODEL.cost === "function" ? MODEL.cost(usage) : MODEL.cost const modelCost =
modelInfo.cost200K &&
usage.inputTokens + usage.cacheReadTokens + usage.cacheWrite5mTokens + usage.cacheWrite1hTokens > 200_000
? modelInfo.cost200K
: modelInfo.cost
const inputCost = modelCost.input * inputTokens * 100 const inputCost = modelCost.input * inputTokens * 100
const outputCost = modelCost.output * outputTokens * 100 const outputCost = modelCost.output * outputTokens * 100
@@ -495,15 +373,15 @@ export async function handler(
"cost.total": Math.round(totalCostInCent), "cost.total": Math.round(totalCostInCent),
}) })
if (!apiKey) return if (!authInfo) return
const cost = isFree ? 0 : centsToMicroCents(totalCostInCent) const cost = authInfo.isFree ? 0 : centsToMicroCents(totalCostInCent)
await Database.transaction(async (tx) => { await Database.transaction(async (tx) => {
await tx.insert(UsageTable).values({ await tx.insert(UsageTable).values({
workspaceID: apiKey.workspaceID, workspaceID: authInfo.workspaceID,
id: Identifier.create("usage"), id: Identifier.create("usage"),
model: MODEL.id, model: modelInfo.id,
provider: providerName, provider: providerId,
inputTokens, inputTokens,
outputTokens, outputTokens,
reasoningTokens, reasoningTokens,
@@ -524,19 +402,19 @@ export async function handler(
`, `,
timeMonthlyUsageUpdated: sql`now()`, timeMonthlyUsageUpdated: sql`now()`,
}) })
.where(eq(BillingTable.workspaceID, apiKey.workspaceID)) .where(eq(BillingTable.workspaceID, authInfo.workspaceID))
}) })
await Database.use((tx) => await Database.use((tx) =>
tx tx
.update(KeyTable) .update(KeyTable)
.set({ timeUsed: sql`now()` }) .set({ timeUsed: sql`now()` })
.where(eq(KeyTable.id, apiKey.id)), .where(eq(KeyTable.id, authInfo.apiKeyId)),
) )
} }
async function reload() { async function reload(authInfo: Awaited<ReturnType<typeof authenticate>>) {
if (!apiKey) return if (!authInfo) return
const lock = await Database.use((tx) => const lock = await Database.use((tx) =>
tx tx
@@ -546,7 +424,7 @@ export async function handler(
}) })
.where( .where(
and( and(
eq(BillingTable.workspaceID, apiKey.workspaceID), eq(BillingTable.workspaceID, authInfo.workspaceID),
eq(BillingTable.reload, true), eq(BillingTable.reload, true),
lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)), lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)),
or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)), or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)),
@@ -555,40 +433,8 @@ export async function handler(
) )
if (lock.rowsAffected === 0) return if (lock.rowsAffected === 0) return
await Actor.provide("system", { workspaceID: apiKey.workspaceID }, async () => { await Actor.provide("system", { workspaceID: authInfo.workspaceID }, async () => {
await Billing.reload() await Billing.reload()
}) })
} }
} catch (error: any) {
logger.metric({
"error.type": error.constructor.name,
"error.message": error.message,
})
// Note: both top level "type" and "error.type" fields are used by the @ai-sdk/anthropic client to render the error message.
if (
error instanceof AuthError ||
error instanceof CreditsError ||
error instanceof MonthlyLimitError ||
error instanceof ModelError
)
return new Response(
JSON.stringify({
type: "error",
error: { type: error.constructor.name, message: error.message },
}),
{ status: 401 },
)
return new Response(
JSON.stringify({
type: "error",
error: {
type: "error",
message: error.message,
},
}),
{ status: 500 },
)
}
} }

View File

@@ -5,6 +5,9 @@ type Usage = {
prompt_tokens?: number prompt_tokens?: number
completion_tokens?: number completion_tokens?: number
total_tokens?: number total_tokens?: number
// used by moonshot
cached_tokens?: number
// used by xai
prompt_tokens_details?: { prompt_tokens_details?: {
text_tokens?: number text_tokens?: number
audio_tokens?: number audio_tokens?: number
@@ -48,7 +51,7 @@ export function POST(input: APIEvent) {
inputTokens: usage.prompt_tokens ?? 0, inputTokens: usage.prompt_tokens ?? 0,
outputTokens: usage.completion_tokens ?? 0, outputTokens: usage.completion_tokens ?? 0,
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? undefined, reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? undefined,
cacheReadTokens: usage.prompt_tokens_details?.cached_tokens ?? undefined, cacheReadTokens: usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined,
}), }),
}) })
} }

View File

@@ -0,0 +1 @@
ALTER TABLE `workspace` ADD `data_share` boolean;

View File

@@ -0,0 +1,681 @@
{
"version": "5",
"dialect": "mysql",
"id": "12189a4e-5083-4b17-b8e3-8279c9a3e61a",
"prevId": "28336c91-553c-4d1d-9875-1ee761e47582",
"tables": {
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"monthly_limit": {
"name": "monthly_limit",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"monthly_usage": {
"name": "monthly_usage",
"type": "bigint",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_monthly_usage_updated": {
"name": "time_monthly_usage_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reload_error": {
"name": "reload_error",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_reload_error": {
"name": "time_reload_error",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_reload_locked_till": {
"name": "time_reload_locked_till",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_customer_id": {
"name": "global_customer_id",
"columns": [
"customer_id"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_5m_tokens": {
"name": "cache_write_5m_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_1h_tokens": {
"name": "cache_write_1h_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"isUnique": true
},
"name": {
"name": "name",
"columns": [
"workspace_id",
"name"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"data_share": {
"name": "data_share",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -99,6 +99,13 @@
"when": 1757956978089, "when": 1757956978089,
"tag": "0013_absurd_hobgoblin", "tag": "0013_absurd_hobgoblin",
"breakpoints": true "breakpoints": true
},
{
"idx": 14,
"version": "5",
"when": 1758289919722,
"tag": "0014_demonic_princess_powerful",
"breakpoints": true
} }
] ]
} }

View File

@@ -1,4 +1,4 @@
import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core" import { primaryKey, mysqlTable, uniqueIndex, varchar, boolean } from "drizzle-orm/mysql-core"
import { timestamps, ulid } from "../drizzle/types" import { timestamps, ulid } from "../drizzle/types"
export const WorkspaceTable = mysqlTable( export const WorkspaceTable = mysqlTable(
@@ -7,6 +7,7 @@ export const WorkspaceTable = mysqlTable(
id: ulid("id").notNull().primaryKey(), id: ulid("id").notNull().primaryKey(),
slug: varchar("slug", { length: 255 }), slug: varchar("slug", { length: 255 }),
name: varchar("name", { length: 255 }), name: varchar("name", { length: 255 }),
dataShare: boolean("data_share"),
...timestamps, ...timestamps,
}, },
(table) => [uniqueIndex("slug").on(table.slug)], (table) => [uniqueIndex("slug").on(table.slug)],

View File

@@ -1,6 +1,5 @@
import { z } from "zod" import { z } from "zod"
import { fn } from "./util/fn" import { fn } from "./util/fn"
import { centsToMicroCents } from "./util/price"
import { Actor } from "./actor" import { Actor } from "./actor"
import { Database, eq } from "./drizzle" import { Database, eq } from "./drizzle"
import { Identifier } from "./identifier" import { Identifier } from "./identifier"

View File

@@ -6,89 +6,73 @@
import "sst" import "sst"
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
ANTHROPIC_API_KEY: { "AUTH_API_URL": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
AUTH_API_URL: { "Console": {
type: "sst.sst.Linkable" "type": "sst.cloudflare.SolidStart"
value: string "url": string
} }
BASETEN_API_KEY: { "Database": {
type: "sst.sst.Secret" "database": string
value: string "host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
} }
Console: { "GITHUB_APP_ID": {
type: "sst.cloudflare.SolidStart" "type": "sst.sst.Secret"
url: string "value": string
} }
Database: { "GITHUB_APP_PRIVATE_KEY": {
database: string "type": "sst.sst.Secret"
host: string "value": string
password: string
port: number
type: "sst.sst.Linkable"
username: string
} }
FIREWORKS_API_KEY: { "GITHUB_CLIENT_ID_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_ID: { "GITHUB_CLIENT_SECRET_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_PRIVATE_KEY: { "GOOGLE_CLIENT_ID": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_ID_CONSOLE: { "HONEYCOMB_API_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_SECRET_CONSOLE: { "STRIPE_SECRET_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GOOGLE_CLIENT_ID: { "STRIPE_WEBHOOK_SECRET": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
HONEYCOMB_API_KEY: { "Web": {
type: "sst.sst.Secret" "type": "sst.cloudflare.Astro"
value: string "url": string
} }
OPENAI_API_KEY: { "ZEN_MODELS": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
} }
} }
} }
// cloudflare // cloudflare
import * as cloudflare from "@cloudflare/workers-types" import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
Api: cloudflare.Service "Api": cloudflare.Service
AuthApi: cloudflare.Service "AuthApi": cloudflare.Service
AuthStorage: cloudflare.KVNamespace "AuthStorage": cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket "Bucket": cloudflare.R2Bucket
LogProcessor: cloudflare.Service "LogProcessor": cloudflare.Service
} }
} }

View File

@@ -6,89 +6,73 @@
import "sst" import "sst"
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
ANTHROPIC_API_KEY: { "AUTH_API_URL": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
AUTH_API_URL: { "Console": {
type: "sst.sst.Linkable" "type": "sst.cloudflare.SolidStart"
value: string "url": string
} }
BASETEN_API_KEY: { "Database": {
type: "sst.sst.Secret" "database": string
value: string "host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
} }
Console: { "GITHUB_APP_ID": {
type: "sst.cloudflare.SolidStart" "type": "sst.sst.Secret"
url: string "value": string
} }
Database: { "GITHUB_APP_PRIVATE_KEY": {
database: string "type": "sst.sst.Secret"
host: string "value": string
password: string
port: number
type: "sst.sst.Linkable"
username: string
} }
FIREWORKS_API_KEY: { "GITHUB_CLIENT_ID_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_ID: { "GITHUB_CLIENT_SECRET_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_PRIVATE_KEY: { "GOOGLE_CLIENT_ID": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_ID_CONSOLE: { "HONEYCOMB_API_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_SECRET_CONSOLE: { "STRIPE_SECRET_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GOOGLE_CLIENT_ID: { "STRIPE_WEBHOOK_SECRET": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
HONEYCOMB_API_KEY: { "Web": {
type: "sst.sst.Secret" "type": "sst.cloudflare.Astro"
value: string "url": string
} }
OPENAI_API_KEY: { "ZEN_MODELS": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
} }
} }
} }
// cloudflare // cloudflare
import * as cloudflare from "@cloudflare/workers-types" import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
Api: cloudflare.Service "Api": cloudflare.Service
AuthApi: cloudflare.Service "AuthApi": cloudflare.Service
AuthStorage: cloudflare.KVNamespace "AuthStorage": cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket "Bucket": cloudflare.R2Bucket
LogProcessor: cloudflare.Service "LogProcessor": cloudflare.Service
} }
} }

View File

@@ -6,89 +6,73 @@
import "sst" import "sst"
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
ANTHROPIC_API_KEY: { "AUTH_API_URL": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
AUTH_API_URL: { "Console": {
type: "sst.sst.Linkable" "type": "sst.cloudflare.SolidStart"
value: string "url": string
} }
BASETEN_API_KEY: { "Database": {
type: "sst.sst.Secret" "database": string
value: string "host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
} }
Console: { "GITHUB_APP_ID": {
type: "sst.cloudflare.SolidStart" "type": "sst.sst.Secret"
url: string "value": string
} }
Database: { "GITHUB_APP_PRIVATE_KEY": {
database: string "type": "sst.sst.Secret"
host: string "value": string
password: string
port: number
type: "sst.sst.Linkable"
username: string
} }
FIREWORKS_API_KEY: { "GITHUB_CLIENT_ID_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_ID: { "GITHUB_CLIENT_SECRET_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_PRIVATE_KEY: { "GOOGLE_CLIENT_ID": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_ID_CONSOLE: { "HONEYCOMB_API_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_SECRET_CONSOLE: { "STRIPE_SECRET_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GOOGLE_CLIENT_ID: { "STRIPE_WEBHOOK_SECRET": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
HONEYCOMB_API_KEY: { "Web": {
type: "sst.sst.Secret" "type": "sst.cloudflare.Astro"
value: string "url": string
} }
OPENAI_API_KEY: { "ZEN_MODELS": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
} }
} }
} }
// cloudflare // cloudflare
import * as cloudflare from "@cloudflare/workers-types" import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
Api: cloudflare.Service "Api": cloudflare.Service
AuthApi: cloudflare.Service "AuthApi": cloudflare.Service
AuthStorage: cloudflare.KVNamespace "AuthStorage": cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket "Bucket": cloudflare.R2Bucket
LogProcessor: cloudflare.Service "LogProcessor": cloudflare.Service
} }
} }

128
sst-env.d.ts vendored
View File

@@ -5,95 +5,79 @@
declare module "sst" { declare module "sst" {
export interface Resource { export interface Resource {
ANTHROPIC_API_KEY: { "AUTH_API_URL": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
AUTH_API_URL: { "Api": {
type: "sst.sst.Linkable" "type": "sst.cloudflare.Worker"
value: string "url": string
} }
Api: { "AuthApi": {
type: "sst.cloudflare.Worker" "type": "sst.cloudflare.Worker"
url: string "url": string
} }
AuthApi: { "AuthStorage": {
type: "sst.cloudflare.Worker" "type": "sst.cloudflare.Kv"
url: string
} }
AuthStorage: { "Bucket": {
type: "sst.cloudflare.Kv" "name": string
"type": "sst.cloudflare.Bucket"
} }
BASETEN_API_KEY: { "Console": {
type: "sst.sst.Secret" "type": "sst.cloudflare.SolidStart"
value: string "url": string
} }
Bucket: { "Database": {
name: string "database": string
type: "sst.cloudflare.Bucket" "host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
} }
Console: { "GITHUB_APP_ID": {
type: "sst.cloudflare.SolidStart" "type": "sst.sst.Secret"
url: string "value": string
} }
Database: { "GITHUB_APP_PRIVATE_KEY": {
database: string "type": "sst.sst.Secret"
host: string "value": string
password: string
port: number
type: "sst.sst.Linkable"
username: string
} }
FIREWORKS_API_KEY: { "GITHUB_CLIENT_ID_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_ID: { "GITHUB_CLIENT_SECRET_CONSOLE": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_APP_PRIVATE_KEY: { "GOOGLE_CLIENT_ID": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_ID_CONSOLE: { "HONEYCOMB_API_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
GITHUB_CLIENT_SECRET_CONSOLE: { "LogProcessor": {
type: "sst.sst.Secret" "type": "sst.cloudflare.Worker"
value: string
} }
GOOGLE_CLIENT_ID: { "STRIPE_SECRET_KEY": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
} }
HONEYCOMB_API_KEY: { "STRIPE_WEBHOOK_SECRET": {
type: "sst.sst.Secret" "type": "sst.sst.Linkable"
value: string "value": string
} }
LogProcessor: { "Web": {
type: "sst.cloudflare.Worker" "type": "sst.cloudflare.Astro"
"url": string
} }
OPENAI_API_KEY: { "ZEN_MODELS": {
type: "sst.sst.Secret" "type": "sst.sst.Secret"
value: string "value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
} }
} }
} }