diff options
| author | Frank <[email protected]> | 2026-02-22 18:41:34 -0500 |
|---|---|---|
| committer | Frank <[email protected]> | 2026-02-22 22:19:44 -0500 |
| commit | 5712cff5c453a185ac75a160f76ca06135d6ab2d (patch) | |
| tree | 6838fedd40aed31421265fdc8b9fe5826d396f86 /packages/console/core/src | |
| parent | ee754c46f992dd4024e56e93246421246d16d13f (diff) | |
| download | opencode-5712cff5c453a185ac75a160f76ca06135d6ab2d.tar.gz opencode-5712cff5c453a185ac75a160f76ca06135d6ab2d.zip | |
zen: track session in usage
Diffstat (limited to 'packages/console/core/src')
| -rw-r--r-- | packages/console/core/src/black.ts | 74 | ||||
| -rw-r--r-- | packages/console/core/src/lite.ts | 28 | ||||
| -rw-r--r-- | packages/console/core/src/schema/billing.sql.ts | 5 | ||||
| -rw-r--r-- | packages/console/core/src/subscription.ts | 75 |
4 files changed, 106 insertions, 76 deletions
diff --git a/packages/console/core/src/black.ts b/packages/console/core/src/black.ts index 5f8db6273..b4cc27064 100644 --- a/packages/console/core/src/black.ts +++ b/packages/console/core/src/black.ts @@ -1,8 +1,6 @@ import { z } from "zod" import { fn } from "./util/fn" import { Resource } from "@opencode-ai/console-resource" -import { centsToMicroCents } from "./util/price" -import { getWeekBounds } from "./util/date" import { SubscriptionPlan } from "./schema/billing.sql" export namespace BlackData { @@ -60,75 +58,3 @@ export namespace BlackData { }, ) } - -export namespace Black { - export const analyzeRollingUsage = fn( - z.object({ - plan: z.enum(SubscriptionPlan), - usage: z.number().int(), - timeUpdated: z.date(), - }), - ({ plan, usage, timeUpdated }) => { - const now = new Date() - const black = BlackData.getLimits({ plan }) - const rollingWindowMs = black.rollingWindow * 3600 * 1000 - const rollingLimitInMicroCents = centsToMicroCents(black.rollingLimit * 100) - const windowStart = new Date(now.getTime() - rollingWindowMs) - if (timeUpdated < windowStart) { - return { - status: "ok" as const, - resetInSec: black.rollingWindow * 3600, - usagePercent: 0, - } - } - - const windowEnd = new Date(timeUpdated.getTime() + rollingWindowMs) - if (usage < rollingLimitInMicroCents) { - return { - status: "ok" as const, - resetInSec: Math.ceil((windowEnd.getTime() - now.getTime()) / 1000), - usagePercent: Math.ceil(Math.min(100, (usage / rollingLimitInMicroCents) * 100)), - } - } - return { - status: "rate-limited" as const, - resetInSec: Math.ceil((windowEnd.getTime() - now.getTime()) / 1000), - usagePercent: 100, - } - }, - ) - - export const analyzeWeeklyUsage = fn( - z.object({ - plan: z.enum(SubscriptionPlan), - usage: z.number().int(), - timeUpdated: z.date(), - }), - ({ plan, usage, timeUpdated }) => { - const black = BlackData.getLimits({ plan }) - const now = new Date() - const week = getWeekBounds(now) - const fixedLimitInMicroCents = centsToMicroCents(black.fixedLimit * 100) - if (timeUpdated < week.start) { - return { - status: "ok" as const, - resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), - usagePercent: 0, - } - } - if (usage < fixedLimitInMicroCents) { - return { - status: "ok" as const, - resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), - usagePercent: Math.ceil(Math.min(100, (usage / fixedLimitInMicroCents) * 100)), - } - } - - return { - status: "rate-limited" as const, - resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), - usagePercent: 100, - } - }, - ) -} diff --git a/packages/console/core/src/lite.ts b/packages/console/core/src/lite.ts new file mode 100644 index 000000000..d6679208d --- /dev/null +++ b/packages/console/core/src/lite.ts @@ -0,0 +1,28 @@ +import { z } from "zod" +import { fn } from "./util/fn" +import { Resource } from "@opencode-ai/console-resource" + +export namespace LiteData { + const Schema = z.object({ + fixedLimit: z.number().int(), + rollingLimit: z.number().int(), + rollingWindow: z.number().int(), + }) + + export const validate = fn(Schema, (input) => { + return input + }) + + export const getLimits = fn(z.void(), () => { + const json = JSON.parse(Resource.ZEN_LITE_LIMITS.value) + return Schema.parse(json) + }) + + export const planToPriceID = fn(z.void(), () => { + return Resource.ZEN_LITE_PRICE.price + }) + + export const priceIDToPlan = fn(z.void(), () => { + return "lite" + }) +} diff --git a/packages/console/core/src/schema/billing.sql.ts b/packages/console/core/src/schema/billing.sql.ts index ba8f89280..6d96fc7eb 100644 --- a/packages/console/core/src/schema/billing.sql.ts +++ b/packages/console/core/src/schema/billing.sql.ts @@ -67,7 +67,7 @@ export const PaymentTable = mysqlTable( timeRefunded: utc("time_refunded"), enrichment: json("enrichment").$type< | { - type: "subscription" + type: "subscription" | "lite" couponID?: string } | { @@ -93,8 +93,9 @@ export const UsageTable = mysqlTable( cacheWrite1hTokens: int("cache_write_1h_tokens"), cost: bigint("cost", { mode: "number" }).notNull(), keyID: ulid("key_id"), + sessionID: varchar("session_id", { length: 30 }), enrichment: json("enrichment").$type<{ - plan: "sub" + plan: "sub" | "byok" | "lite" }>(), }, (table) => [...workspaceIndexes(table), index("usage_time_created").on(table.workspaceID, table.timeCreated)], diff --git a/packages/console/core/src/subscription.ts b/packages/console/core/src/subscription.ts new file mode 100644 index 000000000..ca3b17042 --- /dev/null +++ b/packages/console/core/src/subscription.ts @@ -0,0 +1,75 @@ +import { z } from "zod" +import { fn } from "./util/fn" +import { centsToMicroCents } from "./util/price" +import { getWeekBounds } from "./util/date" + +export namespace Subscription { + export const analyzeRollingUsage = fn( + z.object({ + limit: z.number().int(), + window: z.number().int(), + usage: z.number().int(), + timeUpdated: z.date(), + }), + ({ limit, window, usage, timeUpdated }) => { + const now = new Date() + const rollingWindowMs = window * 3600 * 1000 + const rollingLimitInMicroCents = centsToMicroCents(limit * 100) + const windowStart = new Date(now.getTime() - rollingWindowMs) + if (timeUpdated < windowStart) { + return { + status: "ok" as const, + resetInSec: window * 3600, + usagePercent: 0, + } + } + + const windowEnd = new Date(timeUpdated.getTime() + rollingWindowMs) + if (usage < rollingLimitInMicroCents) { + return { + status: "ok" as const, + resetInSec: Math.ceil((windowEnd.getTime() - now.getTime()) / 1000), + usagePercent: Math.ceil(Math.min(100, (usage / rollingLimitInMicroCents) * 100)), + } + } + return { + status: "rate-limited" as const, + resetInSec: Math.ceil((windowEnd.getTime() - now.getTime()) / 1000), + usagePercent: 100, + } + }, + ) + + export const analyzeWeeklyUsage = fn( + z.object({ + limit: z.number().int(), + usage: z.number().int(), + timeUpdated: z.date(), + }), + ({ limit, usage, timeUpdated }) => { + const now = new Date() + const week = getWeekBounds(now) + const fixedLimitInMicroCents = centsToMicroCents(limit * 100) + if (timeUpdated < week.start) { + return { + status: "ok" as const, + resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), + usagePercent: 0, + } + } + if (usage < fixedLimitInMicroCents) { + return { + status: "ok" as const, + resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), + usagePercent: Math.ceil(Math.min(100, (usage / fixedLimitInMicroCents) * 100)), + } + } + + return { + status: "rate-limited" as const, + resetInSec: Math.ceil((week.end.getTime() - now.getTime()) / 1000), + usagePercent: 100, + } + }, + ) +} |
