import { Billing } from "@opencode/cloud-core/billing.js" import { Key } from "@opencode/cloud-core/key.js" import { action, createAsync, revalidate, query, useAction, useSubmission } from "@solidjs/router" import { createEffect, createSignal, For, onMount, Show } from "solid-js" import { getActor } from "~/context/auth" import { withActor } from "~/context/auth.withActor" import "./index.css" ///////////////////////////////////// // Keys related queries and actions ///////////////////////////////////// const listKeys = query(async () => { "use server" return withActor(() => Key.list()) }, "keys") const createKey = action(async (name: string) => { "use server" return withActor(() => Key.create({ name })) }, "createKey") const removeKey = action(async (id: string) => { "use server" return withActor(() => Key.remove({ id })) }, "removeKey") ///////////////////////////////////// // Billing related queries and actions ///////////////////////////////////// const getBillingInfo = query(async () => { "use server" return withActor(async () => { const billing = await Billing.get() const payments = await Billing.payments() const usage = await Billing.usages() return { billing, payments, usage } }) }, "billingInfo") const createCheckoutUrl = action(async (successUrl: string, cancelUrl: string) => { "use server" return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl })) }, "checkoutUrl") const createPortalUrl = action(async (returnUrl: string) => { "use server" return withActor(() => Billing.generatePortalUrl({ returnUrl })) }, "portalUrl") const dummyUsageData = [ { model: "claude-3-5-sonnet-20241022", inputTokens: 1250, outputTokens: 890, reasoningTokens: 150, cacheReadTokens: 0, cacheWriteTokens: 45, cost: 12340000, timeCreated: new Date("2025-01-28T10:30:00Z"), }, { model: "claude-3-haiku-20240307", inputTokens: 2100, outputTokens: 450, reasoningTokens: null, cacheReadTokens: 120, cacheWriteTokens: 0, cost: 5670000, timeCreated: new Date("2025-01-27T15:22:00Z"), }, { model: "claude-3-5-sonnet-20241022", inputTokens: 850, outputTokens: 1200, reasoningTokens: 220, cacheReadTokens: 30, cacheWriteTokens: 15, cost: 18990000, timeCreated: new Date("2025-01-27T09:15:00Z"), }, { model: "claude-3-opus-20240229", inputTokens: 3200, outputTokens: 1800, reasoningTokens: 400, cacheReadTokens: 0, cacheWriteTokens: 100, cost: 45670000, timeCreated: new Date("2025-01-26T14:45:00Z"), }, { model: "claude-3-haiku-20240307", inputTokens: 650, outputTokens: 280, reasoningTokens: null, cacheReadTokens: 200, cacheWriteTokens: 0, cost: 2340000, timeCreated: new Date("2025-01-25T16:18:00Z"), }, ] export default function() { const actor = createAsync(() => getActor()) onMount(() => { console.log("MOUNTED", actor()) }) ///////////////// // Keys section ///////////////// const keys = createAsync(() => listKeys()) const createKeyAction = useAction(createKey) const removeKeyAction = useAction(removeKey) const createKeySubmission = useSubmission(createKey) const [showCreateForm, setShowCreateForm] = createSignal(false) const [keyName, setKeyName] = createSignal("") const formatDate = (date: Date) => { return date.toLocaleDateString() } const 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(",", ",") } const 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) } const formatKey = (key: string) => { if (key.length <= 11) return key return `${key.slice(0, 7)}...${key.slice(-4)}` } const copyToClipboard = async (text: string) => { try { await navigator.clipboard.writeText(text) } catch (error) { console.error("Failed to copy to clipboard:", error) } } const handleCreateKey = async () => { if (!keyName().trim()) return try { await createKeyAction(keyName().trim()) revalidate("keys") setKeyName("") setShowCreateForm(false) } catch (error) { console.error("Failed to create API key:", error) } } const handleDeleteKey = async (keyId: string) => { if (!confirm("Are you sure you want to delete this API key? This action cannot be undone.")) { return } try { await removeKeyAction(keyId) revalidate("keys") } catch (error) { console.error("Failed to delete API key:", error) } } ///////////////// // Billing section ///////////////// const billingInfo = createAsync(() => getBillingInfo()) const [isLoading, setIsLoading] = createSignal(false) const createCheckoutUrlAction = useAction(createCheckoutUrl) // Run once on component mount to check URL parameters onMount(() => { const url = new URL(window.location.href) const result = url.hash console.log("STRIPE RESULT", result) if (url.hash === "#success") { setIsLoading(true) // Remove the hash from the URL window.history.replaceState(null, "", window.location.pathname + window.location.search) } }) createEffect((old?: number) => { if (old && old !== billingInfo()?.billing?.balance) { setIsLoading(false) } return billingInfo()?.billing?.balance }) const handleBuyCredits = async () => { try { setIsLoading(true) const baseUrl = window.location.href const successUrl = new URL(baseUrl) successUrl.hash = "success" const checkoutUrl = await createCheckoutUrlAction(successUrl.toString(), baseUrl) if (checkoutUrl) { window.location.href = checkoutUrl } } catch (error) { console.error("Failed to get checkout URL:", error) setIsLoading(false) } } return (
Coding models optimized for use with opencode. Learn more.
Current authenticated user information and session details.
Manage your API keys for accessing opencode services.
Create an API key to access opencode gateway
Manage your billing and add credits to your account.
{(() => { const balanceStr = ((billingInfo()?.billing?.balance ?? 0) / 100000000).toFixed(2) return `$${balanceStr === "-0.00" ? "0.00" : balanceStr}` })()}
Your recent payment transactions.
Your recent API usage and costs.
Make your first API call to get started.
| Date | Model | Tokens | Cost |
|---|---|---|---|
| {formatDateForTable(date)} | {usage.model} | {totalTokens.toLocaleString()} | ${((usage.cost ?? 0) / 100000000).toFixed(4)} |