diff --git a/bun.lock b/bun.lock
index 399b945c49..4d03c250af 100644
--- a/bun.lock
+++ b/bun.lock
@@ -56,6 +56,7 @@
"@solidjs/start": "^1.1.0",
"solid-js": "catalog:",
"vinxi": "^0.5.7",
+ "zod": "catalog:",
},
},
"packages/console/core": {
diff --git a/github/sst-env.d.ts b/github/sst-env.d.ts
index 6b69016e71..f742a12004 100644
--- a/github/sst-env.d.ts
+++ b/github/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/infra/console.ts b/infra/console.ts
index 6ca842380f..74c478aeea 100644
--- a/infra/console.ts
+++ b/infra/console.ts
@@ -99,11 +99,7 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
],
})
-const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY")
-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 ZEN_MODELS = new sst.Secret("ZEN_MODELS")
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_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", {
domain,
path: "packages/console/app",
- link: [
- 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,
- ],
+ link: [database, AUTH_API_URL, STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, ZEN_MODELS],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),
//VITE_API_URL: gateway.url.apply((url) => url!),
diff --git a/packages/app/sst-env.d.ts b/packages/app/sst-env.d.ts
index 0397645b50..b6a7e9066e 100644
--- a/packages/app/sst-env.d.ts
+++ b/packages/app/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/app/package.json b/packages/console/app/package.json
index e370915cb0..d5454c117e 100644
--- a/packages/console/app/package.json
+++ b/packages/console/app/package.json
@@ -12,12 +12,13 @@
"dependencies": {
"@ibm/plex": "6.4.1",
"@openauthjs/openauth": "0.0.0-20250322224806",
+ "@opencode/console-core": "workspace:*",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0",
"solid-js": "catalog:",
"vinxi": "^0.5.7",
- "@opencode/console-core": "workspace:*"
+ "zod": "catalog:"
},
"engines": {
"node": ">=22"
diff --git a/packages/console/app/src/component/workspace/privacy-section.module.css b/packages/console/app/src/component/workspace/privacy-section.module.css
new file mode 100644
index 0000000000..0bb5709cb8
--- /dev/null
+++ b/packages/console/app/src/component/workspace/privacy-section.module.css
@@ -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;
+ }
+ }
+ }
+}
diff --git a/packages/console/app/src/component/workspace/privacy-section.tsx b/packages/console/app/src/component/workspace/privacy-section.tsx
new file mode 100644
index 0000000000..12dd3b2037
--- /dev/null
+++ b/packages/console/app/src/component/workspace/privacy-section.tsx
@@ -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 (
+
+
+
Privacy controls
+
+ Some providers offer data-sharing programs. If you opt in, you voluntarily share your usage data with
+ them, which they may use to improve their services, including model training.
+
+
+
+ By opting in, you gain access to discounted pricing from the provider. You can opt in or out at any
+ time.
+
+
+
+
+ Learn more
+
+
+
+
+
+
+
+ You are currently opted in to the data-sharing program.
+
+
+
+
+
+
+ )
+}
diff --git a/packages/console/app/src/routes/workspace/[id].tsx b/packages/console/app/src/routes/workspace/[id].tsx
index ec21126c89..dc8e28cdbb 100644
--- a/packages/console/app/src/routes/workspace/[id].tsx
+++ b/packages/console/app/src/routes/workspace/[id].tsx
@@ -2,11 +2,15 @@ import "./[id].css"
import { MonthlyLimitSection } from "~/component/workspace/monthly-limit-section"
import { NewUserSection } from "~/component/workspace/new-user-section"
import { BillingSection } from "~/component/workspace/billing-section"
+import { PrivacySection } from "~/component/workspace/privacy-section"
import { PaymentSection } from "~/component/workspace/payment-section"
import { UsageSection } from "~/component/workspace/usage-section"
import { KeySection } from "~/component/workspace/key-section"
+import { Show } from "solid-js"
+import { useParams } from "@solidjs/router"
export default function () {
+ const params = useParams()
return (
@@ -25,9 +29,20 @@ export default function () {
+
+
+
)
}
+
+export function isBeta(workspaceID: string) {
+ return [
+ "wrk_01K46JDFR0E75SG2Q8K172KF3Y", // production
+ "wrk_01K4NFRR5P7FSYWH88307B4DDS", // dev
+ "wrk_01K4PJRKJ2WPQZN3FFYRV4673F", // frank
+ ].includes(workspaceID)
+}
diff --git a/packages/console/app/src/routes/zen/handler.ts b/packages/console/app/src/routes/zen/handler.ts
index 6065e2f76f..a60abeb70c 100644
--- a/packages/console/app/src/routes/zen/handler.ts
+++ b/packages/console/app/src/routes/zen/handler.ts
@@ -1,37 +1,15 @@
+import { z } from "zod"
import type { APIEvent } from "@solidjs/start/server"
import path from "node:path"
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 { 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 { Identifier } from "@opencode/console-core/identifier.js"
import { Resource } from "@opencode/console-resource"
import { Billing } from "../../../../core/src/billing"
import { Actor } from "@opencode/console-core/actor.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
- providers: Record<
- string,
- {
- api: string
- apiKey: string
- model: string
- weight?: number
- }
- >
-}
+import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js"
export async function handler(
input: APIEvent,
@@ -56,184 +34,32 @@ export async function handler(
class MonthlyLimitError extends Error {}
class ModelError extends Error {}
- const MODELS: Record = {
- "claude-opus-4-1": {
- id: "claude-opus-4-1" as const,
- auth: true,
- cost: {
- input: 0.000015,
- output: 0.000075,
- cacheRead: 0.0000015,
- cacheWrite5m: 0.00001875,
- cacheWrite1h: 0.00003,
- },
- headerMappings: {},
- providers: {
- anthropic: {
- api: "https://api.anthropic.com",
- apiKey: Resource.ANTHROPIC_API_KEY.value,
- model: "claude-opus-4-1-20250805",
- },
- },
- },
- "claude-sonnet-4": {
- id: "claude-sonnet-4" as const,
- auth: true,
- cost: (usage: any) => {
- const totalInputTokens =
- usage.inputTokens + usage.cacheReadTokens + usage.cacheWrite5mTokens + usage.cacheWrite1hTokens
- 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 ModelCostSchema = z.object({
+ input: z.number(),
+ output: z.number(),
+ cacheRead: z.number().optional(),
+ cacheWrite5m: z.number().optional(),
+ cacheWrite1h: z.number().optional(),
+ })
+
+ const ModelSchema = z.object({
+ cost: ModelCostSchema,
+ cost200K: ModelCostSchema.optional(),
+ providers: z.array(
+ z.object({
+ id: z.string(),
+ api: z.string(),
+ apiKey: z.string(),
+ model: z.string(),
+ weight: z.number().optional(),
+ allowAnonymous: z.boolean().optional(),
+ headerMappings: z.record(z.string(), z.string()).optional(),
+ disabled: z.boolean().optional(),
+ }),
+ ),
+ })
+
+ type Model = z.infer
const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
@@ -259,31 +85,28 @@ export async function handler(
session: input.request.headers.get("x-opencode-session"),
request: input.request.headers.get("x-opencode-request"),
})
- const MODEL = validateModel()
- const apiKey = await authenticate()
- const isFree = FREE_WORKSPACES.includes(apiKey?.workspaceID ?? "")
- await checkCreditsAndLimit()
- const providerName = selectProvider()
- const providerData = MODEL.providers[providerName]
- logger.metric({ provider: providerName })
+ const authInfo = await authenticate()
+ const modelInfo = validateModel(body.model, authInfo)
+ const providerInfo = selectProvider(modelInfo, authInfo)
+ logger.metric({ provider: providerInfo.id })
// Request to model provider
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",
headers: (() => {
const headers = input.request.headers
headers.delete("host")
headers.delete("content-length")
- opts.setAuthHeader(headers, providerData.apiKey)
- Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => {
+ opts.setAuthHeader(headers, providerInfo.apiKey)
+ Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
headers.set(k, headers.get(v)!)
})
return headers
})(),
body: JSON.stringify({
...(opts.modifyBody?.(body) ?? body),
- model: providerData.model,
+ model: providerInfo.model,
}),
})
@@ -302,8 +125,8 @@ export async function handler(
const body = JSON.stringify(json)
logger.metric({ response_length: body.length })
logger.debug(body)
- await trackUsage(json.usage)
- await reload()
+ await trackUsage(authInfo, modelInfo, providerInfo.id, json.usage)
+ await reload(authInfo)
return new Response(body, {
status: res.status,
statusText: res.statusText,
@@ -326,8 +149,8 @@ export async function handler(
logger.metric({ response_length: responseLength })
const usage = opts.getStreamUsage()
if (usage) {
- await trackUsage(usage)
- await reload()
+ await trackUsage(authInfo, modelInfo, providerInfo.id, usage)
+ await reload(authInfo)
}
c.close()
return
@@ -337,6 +160,7 @@ export async function handler(
logger.metric({ time_to_first_byte: Date.now() - startTimestamp })
}
responseLength += value.length
+ console.log(decoder.decode(value, { stream: true }))
buffer += decoder.decode(value, { stream: true })
const parts = buffer.split("\n\n")
@@ -363,202 +187,6 @@ export async function handler(
statusText: res.statusText,
headers: resHeaders,
})
-
- function validateModel() {
- if (!(body.model in MODELS)) {
- throw new ModelError(`Model ${body.model} not supported`)
- }
- const model = MODELS[body.model as keyof typeof MODELS]
- logger.metric({ model: model.id })
- return model
- }
-
- async function authenticate() {
- try {
- const apiKey = opts.parseApiKey(input.request.headers)
- if (!apiKey) throw new AuthError("Missing API key.")
-
- const key = await Database.use((tx) =>
- tx
- .select({
- id: KeyTable.id,
- workspaceID: KeyTable.workspaceID,
- })
- .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,
- paymentMethodID: BillingTable.paymentMethodID,
- monthlyLimit: BillingTable.monthlyLimit,
- monthlyUsage: BillingTable.monthlyUsage,
- timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated,
- })
- .from(BillingTable)
- .where(eq(BillingTable.workspaceID, apiKey.workspaceID))
- .then((rows) => rows[0]),
- )
-
- if (!billing.paymentMethodID) throw new CreditsError("No payment method")
- if (billing.balance <= 0) throw new CreditsError("Insufficient balance")
- if (
- billing.monthlyLimit &&
- billing.monthlyUsage &&
- billing.timeMonthlyUsageUpdated &&
- billing.monthlyUsage >= centsToMicroCents(billing.monthlyLimit * 100)
- ) {
- const now = new Date()
- const currentYear = now.getUTCFullYear()
- const currentMonth = now.getUTCMonth()
- const dateYear = billing.timeMonthlyUsageUpdated.getUTCFullYear()
- const dateMonth = billing.timeMonthlyUsageUpdated.getUTCMonth()
- if (currentYear === dateYear && currentMonth === dateMonth)
- throw new MonthlyLimitError(`You have reached your monthly spending limit of $${billing.monthlyLimit}.`)
- }
- }
-
- function selectProvider() {
- const picks = Object.entries(MODEL.providers).flatMap(([name, provider]) =>
- Array(provider.weight ?? 1).fill(name),
- )
- return picks[Math.floor(Math.random() * picks.length)]
- }
-
- async function trackUsage(usage: any) {
- const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
- opts.normalizeUsage(usage)
-
- const modelCost = typeof MODEL.cost === "function" ? MODEL.cost(usage) : MODEL.cost
-
- const inputCost = modelCost.input * inputTokens * 100
- const outputCost = modelCost.output * outputTokens * 100
- const reasoningCost = (() => {
- if (!reasoningTokens) return undefined
- return modelCost.output * reasoningTokens * 100
- })()
- const cacheReadCost = (() => {
- if (!cacheReadTokens) return undefined
- if (!modelCost.cacheRead) return undefined
- return modelCost.cacheRead * cacheReadTokens * 100
- })()
- const cacheWrite5mCost = (() => {
- if (!cacheWrite5mTokens) return undefined
- if (!modelCost.cacheWrite5m) return undefined
- return modelCost.cacheWrite5m * cacheWrite5mTokens * 100
- })()
- const cacheWrite1hCost = (() => {
- if (!cacheWrite1hTokens) return undefined
- if (!modelCost.cacheWrite1h) return undefined
- return modelCost.cacheWrite1h * cacheWrite1hTokens * 100
- })()
- const totalCostInCent =
- inputCost +
- outputCost +
- (reasoningCost ?? 0) +
- (cacheReadCost ?? 0) +
- (cacheWrite5mCost ?? 0) +
- (cacheWrite1hCost ?? 0)
-
- logger.metric({
- "tokens.input": inputTokens,
- "tokens.output": outputTokens,
- "tokens.reasoning": reasoningTokens,
- "tokens.cache_read": cacheReadTokens,
- "tokens.cache_write_5m": cacheWrite5mTokens,
- "tokens.cache_write_1h": cacheWrite1hTokens,
- "cost.input": Math.round(inputCost),
- "cost.output": Math.round(outputCost),
- "cost.reasoning": reasoningCost ? Math.round(reasoningCost) : undefined,
- "cost.cache_read": cacheReadCost ? Math.round(cacheReadCost) : undefined,
- "cost.cache_write_5m": cacheWrite5mCost ? Math.round(cacheWrite5mCost) : undefined,
- "cost.cache_write_1h": cacheWrite1hCost ? Math.round(cacheWrite1hCost) : undefined,
- "cost.total": Math.round(totalCostInCent),
- })
-
- if (!apiKey) return
-
- const cost = isFree ? 0 : centsToMicroCents(totalCostInCent)
- await Database.transaction(async (tx) => {
- await tx.insert(UsageTable).values({
- workspaceID: apiKey.workspaceID,
- id: Identifier.create("usage"),
- model: MODEL.id,
- provider: providerName,
- inputTokens,
- outputTokens,
- reasoningTokens,
- cacheReadTokens,
- cacheWrite5mTokens,
- cacheWrite1hTokens,
- cost,
- })
- await tx
- .update(BillingTable)
- .set({
- balance: sql`${BillingTable.balance} - ${cost}`,
- monthlyUsage: sql`
- CASE
- WHEN MONTH(${BillingTable.timeMonthlyUsageUpdated}) = MONTH(now()) AND YEAR(${BillingTable.timeMonthlyUsageUpdated}) = YEAR(now()) THEN ${BillingTable.monthlyUsage} + ${cost}
- ELSE ${cost}
- END
- `,
- timeMonthlyUsageUpdated: sql`now()`,
- })
- .where(eq(BillingTable.workspaceID, apiKey.workspaceID))
- })
-
- await Database.use((tx) =>
- tx
- .update(KeyTable)
- .set({ timeUsed: sql`now()` })
- .where(eq(KeyTable.id, apiKey.id)),
- )
- }
-
- async function reload() {
- if (!apiKey) return
-
- const lock = await Database.use((tx) =>
- tx
- .update(BillingTable)
- .set({
- timeReloadLockedTill: sql`now() + interval 1 minute`,
- })
- .where(
- and(
- eq(BillingTable.workspaceID, apiKey.workspaceID),
- eq(BillingTable.reload, true),
- lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)),
- or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)),
- ),
- ),
- )
- if (lock.rowsAffected === 0) return
-
- await Actor.provide("system", { workspaceID: apiKey.workspaceID }, async () => {
- await Billing.reload()
- })
- }
} catch (error: any) {
logger.metric({
"error.type": error.constructor.name,
@@ -591,4 +219,222 @@ export async function handler(
{ status: 500 },
)
}
+
+ async function authenticate() {
+ const apiKey = opts.parseApiKey(input.request.headers)
+ if (!apiKey) return
+
+ const data = await Database.use((tx) =>
+ tx
+ .select({
+ apiKey: KeyTable.id,
+ workspaceID: KeyTable.workspaceID,
+ dataShare: WorkspaceTable.dataShare,
+ balance: BillingTable.balance,
+ paymentMethodID: BillingTable.paymentMethodID,
+ monthlyLimit: BillingTable.monthlyLimit,
+ monthlyUsage: BillingTable.monthlyUsage,
+ timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated,
+ })
+ .from(KeyTable)
+ .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]),
+ )
+
+ if (!data) throw new AuthError("Invalid API key.")
+ 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 (
+ data.monthlyLimit &&
+ data.monthlyUsage &&
+ data.timeMonthlyUsageUpdated &&
+ data.monthlyUsage >= centsToMicroCents(data.monthlyLimit * 100)
+ ) {
+ const now = new Date()
+ const currentYear = now.getUTCFullYear()
+ const currentMonth = now.getUTCMonth()
+ const dateYear = data.timeMonthlyUsageUpdated.getUTCFullYear()
+ const dateMonth = data.timeMonthlyUsageUpdated.getUTCMonth()
+ if (currentYear === dateYear && currentMonth === dateMonth)
+ throw new MonthlyLimitError(`You have reached your monthly spending limit of $${data.monthlyLimit}.`)
+ }
+ }
+
+ return {
+ apiKeyId: data.apiKey,
+ workspaceID: data.workspaceID,
+ dataShare: data.dataShare,
+ isFree,
+ }
+ }
+
+ function validateModel(reqModel: string, authInfo: Awaited>) {
+ 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>) {
+ 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(provider.weight ?? 1).fill(provider))
+ return picks[Math.floor(Math.random() * picks.length)]
+ }
+
+ async function trackUsage(
+ authInfo: Awaited>,
+ modelInfo: ReturnType,
+ providerId: string,
+ usage: any,
+ ) {
+ const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
+ opts.normalizeUsage(usage)
+
+ 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 outputCost = modelCost.output * outputTokens * 100
+ const reasoningCost = (() => {
+ if (!reasoningTokens) return undefined
+ return modelCost.output * reasoningTokens * 100
+ })()
+ const cacheReadCost = (() => {
+ if (!cacheReadTokens) return undefined
+ if (!modelCost.cacheRead) return undefined
+ return modelCost.cacheRead * cacheReadTokens * 100
+ })()
+ const cacheWrite5mCost = (() => {
+ if (!cacheWrite5mTokens) return undefined
+ if (!modelCost.cacheWrite5m) return undefined
+ return modelCost.cacheWrite5m * cacheWrite5mTokens * 100
+ })()
+ const cacheWrite1hCost = (() => {
+ if (!cacheWrite1hTokens) return undefined
+ if (!modelCost.cacheWrite1h) return undefined
+ return modelCost.cacheWrite1h * cacheWrite1hTokens * 100
+ })()
+ const totalCostInCent =
+ inputCost +
+ outputCost +
+ (reasoningCost ?? 0) +
+ (cacheReadCost ?? 0) +
+ (cacheWrite5mCost ?? 0) +
+ (cacheWrite1hCost ?? 0)
+
+ logger.metric({
+ "tokens.input": inputTokens,
+ "tokens.output": outputTokens,
+ "tokens.reasoning": reasoningTokens,
+ "tokens.cache_read": cacheReadTokens,
+ "tokens.cache_write_5m": cacheWrite5mTokens,
+ "tokens.cache_write_1h": cacheWrite1hTokens,
+ "cost.input": Math.round(inputCost),
+ "cost.output": Math.round(outputCost),
+ "cost.reasoning": reasoningCost ? Math.round(reasoningCost) : undefined,
+ "cost.cache_read": cacheReadCost ? Math.round(cacheReadCost) : undefined,
+ "cost.cache_write_5m": cacheWrite5mCost ? Math.round(cacheWrite5mCost) : undefined,
+ "cost.cache_write_1h": cacheWrite1hCost ? Math.round(cacheWrite1hCost) : undefined,
+ "cost.total": Math.round(totalCostInCent),
+ })
+
+ if (!authInfo) return
+
+ const cost = authInfo.isFree ? 0 : centsToMicroCents(totalCostInCent)
+ await Database.transaction(async (tx) => {
+ await tx.insert(UsageTable).values({
+ workspaceID: authInfo.workspaceID,
+ id: Identifier.create("usage"),
+ model: modelInfo.id,
+ provider: providerId,
+ inputTokens,
+ outputTokens,
+ reasoningTokens,
+ cacheReadTokens,
+ cacheWrite5mTokens,
+ cacheWrite1hTokens,
+ cost,
+ })
+ await tx
+ .update(BillingTable)
+ .set({
+ balance: sql`${BillingTable.balance} - ${cost}`,
+ monthlyUsage: sql`
+ CASE
+ WHEN MONTH(${BillingTable.timeMonthlyUsageUpdated}) = MONTH(now()) AND YEAR(${BillingTable.timeMonthlyUsageUpdated}) = YEAR(now()) THEN ${BillingTable.monthlyUsage} + ${cost}
+ ELSE ${cost}
+ END
+ `,
+ timeMonthlyUsageUpdated: sql`now()`,
+ })
+ .where(eq(BillingTable.workspaceID, authInfo.workspaceID))
+ })
+
+ await Database.use((tx) =>
+ tx
+ .update(KeyTable)
+ .set({ timeUsed: sql`now()` })
+ .where(eq(KeyTable.id, authInfo.apiKeyId)),
+ )
+ }
+
+ async function reload(authInfo: Awaited>) {
+ if (!authInfo) return
+
+ const lock = await Database.use((tx) =>
+ tx
+ .update(BillingTable)
+ .set({
+ timeReloadLockedTill: sql`now() + interval 1 minute`,
+ })
+ .where(
+ and(
+ eq(BillingTable.workspaceID, authInfo.workspaceID),
+ eq(BillingTable.reload, true),
+ lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)),
+ or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)),
+ ),
+ ),
+ )
+ if (lock.rowsAffected === 0) return
+
+ await Actor.provide("system", { workspaceID: authInfo.workspaceID }, async () => {
+ await Billing.reload()
+ })
+ }
}
diff --git a/packages/console/app/src/routes/zen/v1/chat/completions.ts b/packages/console/app/src/routes/zen/v1/chat/completions.ts
index 801557324e..0db14c7ad0 100644
--- a/packages/console/app/src/routes/zen/v1/chat/completions.ts
+++ b/packages/console/app/src/routes/zen/v1/chat/completions.ts
@@ -5,6 +5,9 @@ type Usage = {
prompt_tokens?: number
completion_tokens?: number
total_tokens?: number
+ // used by moonshot
+ cached_tokens?: number
+ // used by xai
prompt_tokens_details?: {
text_tokens?: number
audio_tokens?: number
@@ -48,7 +51,7 @@ export function POST(input: APIEvent) {
inputTokens: usage.prompt_tokens ?? 0,
outputTokens: usage.completion_tokens ?? 0,
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,
}),
})
}
diff --git a/packages/console/app/sst-env.d.ts b/packages/console/app/sst-env.d.ts
index bd55882173..9b9de73273 100644
--- a/packages/console/app/sst-env.d.ts
+++ b/packages/console/app/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/core/migrations/0014_demonic_princess_powerful.sql b/packages/console/core/migrations/0014_demonic_princess_powerful.sql
new file mode 100644
index 0000000000..c2c228be1e
--- /dev/null
+++ b/packages/console/core/migrations/0014_demonic_princess_powerful.sql
@@ -0,0 +1 @@
+ALTER TABLE `workspace` ADD `data_share` boolean;
\ No newline at end of file
diff --git a/packages/console/core/migrations/meta/0014_snapshot.json b/packages/console/core/migrations/meta/0014_snapshot.json
new file mode 100644
index 0000000000..54fcc4ff0b
--- /dev/null
+++ b/packages/console/core/migrations/meta/0014_snapshot.json
@@ -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": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/console/core/migrations/meta/_journal.json b/packages/console/core/migrations/meta/_journal.json
index 1b7d45da33..e1eb8da649 100644
--- a/packages/console/core/migrations/meta/_journal.json
+++ b/packages/console/core/migrations/meta/_journal.json
@@ -99,6 +99,13 @@
"when": 1757956978089,
"tag": "0013_absurd_hobgoblin",
"breakpoints": true
+ },
+ {
+ "idx": 14,
+ "version": "5",
+ "when": 1758289919722,
+ "tag": "0014_demonic_princess_powerful",
+ "breakpoints": true
}
]
-}
+}
\ No newline at end of file
diff --git a/packages/console/core/src/schema/workspace.sql.ts b/packages/console/core/src/schema/workspace.sql.ts
index 9792554289..67fddb08c2 100644
--- a/packages/console/core/src/schema/workspace.sql.ts
+++ b/packages/console/core/src/schema/workspace.sql.ts
@@ -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"
export const WorkspaceTable = mysqlTable(
@@ -7,6 +7,7 @@ export const WorkspaceTable = mysqlTable(
id: ulid("id").notNull().primaryKey(),
slug: varchar("slug", { length: 255 }),
name: varchar("name", { length: 255 }),
+ dataShare: boolean("data_share"),
...timestamps,
},
(table) => [uniqueIndex("slug").on(table.slug)],
diff --git a/packages/console/core/src/workspace.ts b/packages/console/core/src/workspace.ts
index a9fb923d6f..79959f708d 100644
--- a/packages/console/core/src/workspace.ts
+++ b/packages/console/core/src/workspace.ts
@@ -1,6 +1,5 @@
import { z } from "zod"
import { fn } from "./util/fn"
-import { centsToMicroCents } from "./util/price"
import { Actor } from "./actor"
import { Database, eq } from "./drizzle"
import { Identifier } from "./identifier"
diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts
index bd55882173..9b9de73273 100644
--- a/packages/console/core/sst-env.d.ts
+++ b/packages/console/core/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts
index afa8c6fe78..b07b55f628 100644
--- a/packages/console/function/sst-env.d.ts
+++ b/packages/console/function/sst-env.d.ts
@@ -6,91 +6,75 @@
import "sst"
declare module "sst" {
export interface Resource {
- ANTHROPIC_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- BASETEN_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- FIREWORKS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
}
- OPENAI_API_KEY: {
- type: "sst.sst.Secret"
- 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
+ "ZEN_MODELS": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts
index afa8c6fe78..b07b55f628 100644
--- a/packages/console/resource/sst-env.d.ts
+++ b/packages/console/resource/sst-env.d.ts
@@ -6,91 +6,75 @@
import "sst"
declare module "sst" {
export interface Resource {
- ANTHROPIC_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- BASETEN_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- FIREWORKS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
}
- OPENAI_API_KEY: {
- type: "sst.sst.Secret"
- 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
+ "ZEN_MODELS": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/scripts/sst-env.d.ts b/packages/console/scripts/sst-env.d.ts
index bd55882173..9b9de73273 100644
--- a/packages/console/scripts/sst-env.d.ts
+++ b/packages/console/scripts/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts
index afa8c6fe78..b07b55f628 100644
--- a/packages/function/sst-env.d.ts
+++ b/packages/function/sst-env.d.ts
@@ -6,91 +6,75 @@
import "sst"
declare module "sst" {
export interface Resource {
- ANTHROPIC_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- BASETEN_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- FIREWORKS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
}
- OPENAI_API_KEY: {
- type: "sst.sst.Secret"
- 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
+ "ZEN_MODELS": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts
index 0397645b50..b6a7e9066e 100644
--- a/packages/opencode/sst-env.d.ts
+++ b/packages/opencode/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/plugin/sst-env.d.ts b/packages/plugin/sst-env.d.ts
index 0397645b50..b6a7e9066e 100644
--- a/packages/plugin/sst-env.d.ts
+++ b/packages/plugin/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/sdk/js/sst-env.d.ts b/packages/sdk/js/sst-env.d.ts
index bd55882173..9b9de73273 100644
--- a/packages/sdk/js/sst-env.d.ts
+++ b/packages/sdk/js/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/web/sst-env.d.ts b/packages/web/sst-env.d.ts
index 0397645b50..b6a7e9066e 100644
--- a/packages/web/sst-env.d.ts
+++ b/packages/web/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/sdks/vscode/sst-env.d.ts b/sdks/vscode/sst-env.d.ts
index 0397645b50..b6a7e9066e 100644
--- a/sdks/vscode/sst-env.d.ts
+++ b/sdks/vscode/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/sst-env.d.ts b/sst-env.d.ts
index 22caba969d..c2160547a6 100644
--- a/sst-env.d.ts
+++ b/sst-env.d.ts
@@ -5,99 +5,83 @@
declare module "sst" {
export interface Resource {
- ANTHROPIC_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "Api": {
+ "type": "sst.cloudflare.Worker"
+ "url": string
}
- Api: {
- type: "sst.cloudflare.Worker"
- url: string
+ "AuthApi": {
+ "type": "sst.cloudflare.Worker"
+ "url": string
}
- AuthApi: {
- type: "sst.cloudflare.Worker"
- url: string
+ "AuthStorage": {
+ "type": "sst.cloudflare.Kv"
}
- AuthStorage: {
- type: "sst.cloudflare.Kv"
+ "Bucket": {
+ "name": string
+ "type": "sst.cloudflare.Bucket"
}
- BASETEN_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- Bucket: {
- name: string
- type: "sst.cloudflare.Bucket"
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- FIREWORKS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "LogProcessor": {
+ "type": "sst.cloudflare.Worker"
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- LogProcessor: {
- type: "sst.cloudflare.Worker"
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
}
- OPENAI_API_KEY: {
- type: "sst.sst.Secret"
- 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
+ "ZEN_MODELS": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
///
import "sst"
-export {}
+export {}
\ No newline at end of file