import "./[id].css" import { Billing } from "@opencode/cloud-core/billing.js" import { Key } from "@opencode/cloud-core/key.js" import { json, query, action, useParams, useAction, createAsync, useSubmission } from "@solidjs/router" import { createMemo, createSignal, For, Show } from "solid-js" import { withActor } from "~/context/auth.withActor" import { IconCopy, IconCheck } from "~/component/icon" function formatDateForTable(date: Date) { const options: Intl.DateTimeFormatOptions = { day: "numeric", month: "short", hour: "numeric", minute: "2-digit", hour12: true, } return date.toLocaleDateString("en-GB", options).replace(",", ",") } function formatDateUTC(date: Date) { const options: Intl.DateTimeFormatOptions = { weekday: "short", year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit", second: "2-digit", timeZoneName: "short", timeZone: "UTC", } return date.toLocaleDateString("en-US", options) } ///////////////////////////////////// // Keys related queries and actions ///////////////////////////////////// const listKeys = query(async (workspaceID: string) => { "use server" return withActor(() => Key.list(), workspaceID) }, "key.list") const createKey = action(async (workspaceID: string, name: string) => { "use server" return json( withActor(() => Key.create({ name }), workspaceID), { revalidate: listKeys.key }, ) }, "key.create") const removeKey = action(async (workspaceID: string, id: string) => { "use server" return json( withActor(() => Key.remove({ id }), workspaceID), { revalidate: listKeys.key }, ) }, "key.remove") ///////////////////////////////////// // Billing related queries and actions ///////////////////////////////////// const getBalanceInfo = query(async (workspaceID: string) => { "use server" return withActor(async () => { return await Billing.get() }, workspaceID) }, "balanceInfo") const getUsageInfo = query(async (workspaceID: string) => { "use server" return withActor(async () => { return await Billing.usages() }, workspaceID) }, "usageInfo") const getPaymentsInfo = query(async (workspaceID: string) => { "use server" return withActor(async () => { return await Billing.payments() }, workspaceID) }, "paymentsInfo") const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => { "use server" return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID) }, "checkoutUrl") // const createPortalUrl = action(async (workspaceID: string, returnUrl: string) => { // "use server" // return withActor(() => Billing.generatePortalUrl({ returnUrl }), workspaceID) // }, "portalUrl") function KeysSection() { // Dummy data for testing const dummyKeys = [ { id: "key_1", name: "Development API Key", key: "oc_dev_1234567890abcdef1234567890abcdef12345678", timeCreated: new Date("2024-01-15T10:30:00Z"), }, { id: "key_2", name: "Production API Key", key: "oc_prod_abcdef1234567890abcdef1234567890abcdef12", timeCreated: new Date("2024-02-01T14:22:00Z"), }, { id: "key_3", name: "Testing Environment", key: "oc_test_9876543210fedcba9876543210fedcba98765432", timeCreated: new Date("2024-02-10T09:15:00Z"), }, ] const params = useParams() const keys = createAsync(() => listKeys(params.id)) // const keys = () => dummyKeys const [showForm, setShowForm] = createSignal(false) const [name, setName] = createSignal("") const removeAction = useAction(removeKey) const createAction = useAction(createKey) const createSubmission = useSubmission(createKey) const [copiedId, setCopiedId] = createSignal(null) function formatKey(key: string) { if (key.length <= 11) return key return `${key.slice(0, 7)}...${key.slice(-4)}` } async function handleCreateKey() { if (!name().trim()) return try { await createAction(params.id, name().trim()) setName("") setShowForm(false) } catch (error) { console.error("Failed to create API key:", error) } } async function copyKeyToClipboard(text: string, keyId: string) { try { await navigator.clipboard.writeText(text) setCopiedId(keyId) setTimeout(() => setCopiedId(null), 1500) } catch (error) { console.error("Failed to copy to clipboard:", error) } } async function handleDeleteKey(keyId: string) { if (!confirm("Are you sure you want to delete this API key?")) { return } try { await removeAction(params.id, keyId) } catch (error) { console.error("Failed to delete API key:", error) } } return (

API Keys

Manage your API keys for accessing opencode services.

setName(e.currentTarget.value)} onKeyPress={(e) => e.key === "Enter" && handleCreateKey()} />
} >

Create an opencode Gateway API key

} > {(key) => ( )}
Name Key Created
{key.name} {formatDateForTable(key.timeCreated)}
) } function BalanceSection() { const params = useParams() const dummyBalanceInfo = { balance: 2500000000 } // $25.00 in cents const balanceInfo = createAsync(() => getBalanceInfo(params.id)) // const balanceInfo = () => dummyBalanceInfo const createCheckoutUrlAction = useAction(createCheckoutUrl) const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl) async function handleBuyCredits() { try { const baseUrl = window.location.href const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl) if (checkoutUrl) { window.location.href = checkoutUrl } } catch (error) { console.error("Failed to get checkout URL:", error) } } return (

Balance

Add credits to your account.

{ const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) return balanceStr === "0.00" || balanceStr === "-0.00" ? "danger" : undefined })()} > $ {(() => { const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) return balanceStr === "-0.00" ? "0.00" : balanceStr })()}
) } function UsageSection() { const params = useParams() const dummyUsage = [ { id: "usage_1", model: "claude-3-sonnet-20240229", inputTokens: 1250, outputTokens: 890, cost: 125000000, // $1.25 in cents timeCreated: "2024-02-10T15:30:00Z", }, { id: "usage_2", model: "gpt-4-turbo-preview", inputTokens: 2100, outputTokens: 1456, cost: 340000000, // $3.40 in cents timeCreated: "2024-02-09T09:45:00Z", }, { id: "usage_3", model: "claude-3-haiku-20240307", inputTokens: 850, outputTokens: 620, cost: 45000000, // $0.45 in cents timeCreated: "2024-02-08T13:22:00Z", }, { id: "usage_4", model: "gpt-3.5-turbo", inputTokens: 1800, outputTokens: 1200, cost: 85000000, // $0.85 in cents timeCreated: "2024-02-07T11:15:00Z", }, ] const usage = createAsync(() => getUsageInfo(params.id)) // const usage = () => dummyUsage return (

Usage History

Recent API usage and costs.

0} fallback={

Make your first API call to get started.

} > {(usage) => { const date = createMemo(() => new Date(usage.timeCreated)) return ( ) }}
Date Model Input Output Cost
{formatDateForTable(date())} {usage.model} {usage.inputTokens} {usage.outputTokens} ${((usage.cost ?? 0) / 100000000).toFixed(4)}
) } function PaymentsSection() { const params = useParams() const dummyPayments = [ { id: "pi_1234567890", amount: 5000000000, // $50.00 in cents timeCreated: "2024-02-01T10:00:00Z", }, { id: "pi_0987654321", amount: 2500000000, // $25.00 in cents timeCreated: "2024-01-15T14:30:00Z", }, ] const payments = createAsync(() => getPaymentsInfo(params.id)) // const payments = () => dummyPayments return ( payments() && payments()!.length > 0 && (

Payments History

Recent payment transactions.

{(payment) => { const date = new Date(payment.timeCreated) return ( ) }}
Date Payment ID Amount
{formatDateForTable(date)} {payment.id} ${((payment.amount ?? 0) / 100000000).toFixed(2)}
) ) } function NewUserSection() { const params = useParams() const keys = createAsync(() => listKeys(params.id)) const [copiedKey, setCopiedKey] = createSignal(false) async function copyKeyToClipboard(text: string) { try { await navigator.clipboard.writeText(text) setCopiedKey(true) setTimeout(() => setCopiedKey(false), 2000) } catch (error) { console.error("Failed to copy to clipboard:", error) } } return (

Tested & Verified Models

We've benchmarked and tested models specifically for coding agents to ensure the best performance.

Highest Quality

Access models configured for optimal performance - no downgrades or routing to cheaper providers.

No Lock-in

Use Zen with any coding agent, and continue using other providers with opencode whenever you want.

Your API Key

{keys()![0].key}

Next Steps

  1. Copy your API key above
  2. Run opencode auth login and select opencode
  3. Paste your API key when prompted
  4. Run /models to see available models
) } export default function () { const params = useParams() const keys = createAsync(() => listKeys(params.id)) const usage = createAsync(() => getUsageInfo(params.id)) const isNewUser = createMemo(() => { const keysList = keys() const usageList = usage() return keysList?.length === 1 && (!usageList || usageList.length === 0) }) return (

Zen

Curated list of models provided by opencode.{" "} Learn more .

}>
) }