From 8714f235090ca7977662c36bdff2413bbe200943 Mon Sep 17 00:00:00 2001 From: Jay V Date: Sat, 30 Aug 2025 15:05:18 -0400 Subject: [PATCH 01/10] ignore: cloud styles --- cloud/app/src/routes/workspace/[id].css | 788 ++++++++++++------------ cloud/app/src/routes/workspace/[id].tsx | 38 +- 2 files changed, 408 insertions(+), 418 deletions(-) diff --git a/cloud/app/src/routes/workspace/[id].css b/cloud/app/src/routes/workspace/[id].css index 887469e337..d29f5af32f 100644 --- a/cloud/app/src/routes/workspace/[id].css +++ b/cloud/app/src/routes/workspace/[id].css @@ -23,453 +23,451 @@ padding-bottom: var(--space-16); } } -} -/* Common elements */ -button { - padding: var(--space-3) var(--space-4); - border: 1px solid var(--color-border); - border-radius: var(--border-radius-sm); - background-color: var(--color-bg); - color: var(--color-text); - font-size: var(--font-size-sm); - font-family: var(--font-sans); - font-weight: 500; - text-transform: uppercase; - cursor: pointer; - transition: all 0.15s ease; - - &:hover { - background-color: var(--color-surface-hover); - border-color: var(--color-accent); - } - - &:active { - transform: translateY(1px); - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - - &:hover { - background-color: var(--color-bg); - border-color: var(--color-border); - transform: none; - } - } - - &[color="primary"] { - background-color: var(--color-primary); - border-color: var(--color-primary); - color: var(--color-primary-text); - - &:hover { - background-color: var(--color-primary-hover); - border-color: var(--color-primary-hover); - } - } - - &[color="ghost"] { - background-color: transparent; - border-color: transparent; - color: var(--color-text-muted); + /* Common elements */ + button { + padding: var(--space-3) var(--space-4); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + background-color: var(--color-bg); + color: var(--color-text); + font-size: var(--font-size-sm); + font-family: var(--font-sans); + font-weight: 500; + text-transform: uppercase; + cursor: pointer; + transition: all 0.15s ease; &:hover { background-color: var(--color-surface-hover); - border-color: var(--color-border); - color: var(--color-text); + border-color: var(--color-accent); } - } -} -a { - color: var(--color-text); - text-decoration: underline; - text-underline-offset: var(--space-0-75); - text-decoration-thickness: 1px; -} + &:active { + transform: translateY(1px); + } -[data-slot="empty-state"] { - padding: var(--space-20) var(--space-6); - text-align: center; - border: 1px dashed var(--color-border); - border-radius: var(--border-radius-sm); - display: flex; - flex-direction: column; - gap: var(--space-2); + &:disabled { + opacity: 0.5; + cursor: not-allowed; - p { - font-size: var(--font-size-sm); - color: var(--color-text-muted); - margin: 0; - } -} + &:hover { + background-color: var(--color-bg); + border-color: var(--color-border); + transform: none; + } + } -/* Title section */ -[data-slot="title-section"] { - display: flex; - flex-direction: column; - gap: var(--space-2); - padding-bottom: var(--space-8); - border-bottom: 1px solid var(--color-border); + &[data-color="primary"] { + background-color: var(--color-primary); + border-color: var(--color-primary); + color: var(--color-primary-text); - h1 { - font-size: var(--font-size-2xl); - font-weight: 500; - line-height: 1.2; - letter-spacing: -0.03125rem; - margin: 0; - text-transform: uppercase; + &:hover { + background-color: var(--color-primary-hover); + border-color: var(--color-primary-hover); + } + } - @media (max-width: 30rem) { - font-size: var(--font-size-xl); - line-height: 1.25; + &[data-color="ghost"] { + background-color: transparent; + border-color: transparent; + color: var(--color-text-muted); + + &:hover { + background-color: var(--color-surface-hover); + border-color: var(--color-border); + color: var(--color-text); + } } } - p { - font-size: var(--font-size-md); - color: var(--color-text-muted); + a { + color: var(--color-text); + text-decoration: underline; + text-underline-offset: var(--space-0-75); + text-decoration-thickness: 1px; + } - a { + [data-slot="empty-state"] { + padding: var(--space-20) var(--space-6); + text-align: center; + border: 1px dashed var(--color-border); + border-radius: var(--border-radius-sm); + display: flex; + flex-direction: column; + gap: var(--space-2); + + p { + font-size: var(--font-size-sm); + color: var(--color-text-muted); + margin: 0; + } + } + + /* Title section */ + [data-slot="title-section"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding-bottom: var(--space-8); + border-bottom: 1px solid var(--color-border); + + h1 { + font-size: var(--font-size-2xl); + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-xl); + line-height: 1.25; + } + } + + p { + font-size: var(--font-size-md); + color: var(--color-text-muted); + + a { + color: var(--color-text-muted); + } + } + } + + /* Section titles */ + [data-slot="section-title"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + + h2 { + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.03125rem; + margin: 0; + color: var(--color-text-secondary); + text-transform: uppercase; + + @media (max-width: 30rem) { + font-size: var(--font-size-lg); + line-height: 1.25; + } + } + + p { + font-size: var(--font-size-sm); color: var(--color-text-muted); } } -} -/* Section titles */ -[data-slot="section-title"] { - display: flex; - flex-direction: column; - gap: var(--space-1); - - h2 { - font-size: var(--font-size-md); - font-weight: 600; - line-height: 1.2; - letter-spacing: -0.03125rem; - margin: 0; - color: var(--color-text-secondary); - text-transform: uppercase; - - @media (max-width: 30rem) { - font-size: var(--font-size-lg); - line-height: 1.25; - } - } - - p { - font-size: var(--font-size-sm); - color: var(--color-text-muted); - } -} - -/* API Keys Section */ -[data-slot="api-keys-section"] { - [data-slot="create-form"] { - display: flex; - flex-direction: column; - gap: var(--space-3); - padding: var(--space-4); - background-color: var(--color-bg-surface); - border: 1px solid var(--color-border); - border-radius: var(--border-radius-sm); - max-width: 32rem; - - input { - padding: var(--space-2) var(--space-3); + /* API Keys Section */ + [data-slot="api-keys-section"] { + [data-slot="create-form"] { + 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); - background-color: var(--color-bg); - color: var(--color-text); + + input { + padding: var(--space-2) var(--space-3); + border: 1px solid var(--color-border); + border-radius: var(--border-radius-sm); + background-color: var(--color-bg); + color: var(--color-text); + font-size: var(--font-size-sm); + font-family: var(--font-mono); + + &:focus { + outline: none; + border-color: var(--color-accent); + } + + &::placeholder { + color: var(--color-text-disabled); + } + } + + [data-slot="form-actions"] { + display: flex; + gap: var(--space-2); + justify-content: flex-end; + } + } + + [data-slot="api-keys-table"] { + overflow-x: auto; + } + + [data-slot="api-keys-table-element"] { + width: 100%; + border-collapse: collapse; font-size: var(--font-size-sm); - font-family: var(--font-mono); - &:focus { - outline: none; - border-color: var(--color-accent); - } - - &::placeholder { - color: var(--color-text-disabled); - } - } - - [data-slot="form-actions"] { - display: flex; - gap: var(--space-2); - justify-content: flex-end; - } - } - - [data-slot="api-keys-table"] { - overflow-x: auto; - } - - [data-slot="api-keys-table-element"] { - width: 100%; - border-collapse: collapse; - font-size: var(--font-size-sm); - - thead { - border-bottom: 1px solid var(--color-border); - } - - th { - padding: var(--space-3) var(--space-4); - text-align: left; - font-weight: normal; - color: var(--color-text-muted); - text-transform: uppercase; - } - - td { - padding: var(--space-3) var(--space-4); - border-bottom: 1px solid var(--color-border-muted); - color: var(--color-text-muted); - font-family: var(--font-mono); - - &[data-slot="key-name"] { - color: var(--color-text); - font-family: var(--font-sans); - font-weight: 500; - } - - &[data-slot="key-value"] { - font-family: var(--font-mono); - - div { - cursor: pointer; - display: flex; - align-items: center; - gap: var(--space-2); - } - } - - &[data-slot="key-date"] { - color: var(--color-text); - } - - &[data-slot="key-actions"] { - font-family: var(--font-sans); - } - } - - tbody tr { - &:last-child td { - border-bottom: none; - } - } - - @media (max-width: 40rem) { - th, - td { - padding: var(--space-2) var(--space-3); - font-size: var(--font-size-xs); + thead { + border-bottom: 1px solid var(--color-border); } th { - &:nth-child(3) /* Date */ { - display: none; - } + padding: var(--space-3) var(--space-4); + text-align: left; + font-weight: normal; + color: var(--color-text-muted); + text-transform: uppercase; } td { - &:nth-child(3) /* Date */ { - display: none; + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid var(--color-border-muted); + color: var(--color-text-muted); + font-family: var(--font-mono); + + &[data-slot="key-name"] { + color: var(--color-text); + font-family: var(--font-sans); + font-weight: 500; + } + + &[data-slot="key-value"] { + font-family: var(--font-mono); + + div { + cursor: pointer; + display: flex; + align-items: center; + gap: var(--space-2); + } + } + + &[data-slot="key-date"] { + color: var(--color-text); + } + + &[data-slot="key-actions"] { + font-family: var(--font-sans); + } + } + + tbody tr { + &:last-child td { + border-bottom: none; + } + } + + @media (max-width: 40rem) { + th, + td { + padding: var(--space-2) var(--space-3); + font-size: var(--font-size-xs); + } + + th { + &:nth-child(3) /* Date */ { + display: none; + } + } + + td { + &:nth-child(3) /* Date */ { + display: none; + } } } } } -} -/* Balance Section */ -[data-slot="balance-section"] { - [data-slot="balance"] { - 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; - - [data-slot="amount"] { - padding: var(--space-3-5) var(--space-4); - background-color: var(--color-bg-surface); + /* Balance Section */ + [data-slot="balance-section"] { + [data-slot="balance"] { + 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); - display: flex; - align-items: baseline; - gap: var(--space-1); - justify-content: flex-end; + min-width: 14.5rem; + width: fit-content; + + [data-slot="amount"] { + padding: var(--space-3-5) var(--space-4); + background-color: var(--color-bg-surface); + border-radius: var(--border-radius-sm); + display: flex; + align-items: baseline; + gap: var(--space-1); + justify-content: flex-end; + + &.danger { + [data-slot="value"] { + color: var(--color-danger); + } + } + + [data-slot="currency"] { + position: relative; + bottom: 2px; + font-size: var(--font-size-lg); + color: var(--color-text-muted); + font-weight: 400; + } - &.danger { [data-slot="value"] { - color: var(--color-danger); + font-size: var(--font-size-3xl); + font-weight: 500; + color: var(--color-text); } } + } + } - [data-slot="currency"] { - position: relative; - bottom: 2px; - font-size: var(--font-size-lg); + /* Payments Section */ + [data-slot="payments-section"] { + [data-slot="payments-table"] { + overflow-x: auto; + } + + [data-slot="payments-table-element"] { + width: 100%; + border-collapse: collapse; + font-size: var(--font-size-sm); + + thead { + border-bottom: 1px solid var(--color-border); + } + + th { + padding: var(--space-3) var(--space-4); + text-align: left; + font-weight: normal; color: var(--color-text-muted); - font-weight: 400; + text-transform: uppercase; } - [data-slot="value"] { - font-size: var(--font-size-3xl); - font-weight: 500; - color: var(--color-text); - } - } - } -} - -/* Payments Section */ -[data-slot="payments-section"] { - [data-slot="payments-table"] { - overflow-x: auto; - } - - [data-slot="payments-table-element"] { - width: 100%; - border-collapse: collapse; - font-size: var(--font-size-sm); - - thead { - border-bottom: 1px solid var(--color-border); - } - - th { - padding: var(--space-3) var(--space-4); - text-align: left; - font-weight: normal; - color: var(--color-text-muted); - text-transform: uppercase; - } - - td { - padding: var(--space-3) var(--space-4); - border-bottom: 1px solid var(--color-border-muted); - color: var(--color-text-muted); - font-family: var(--font-mono); - - &[data-slot="payment-date"] { - color: var(--color-text); - } - - &[data-slot="payment-id"] { + td { + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid var(--color-border-muted); + color: var(--color-text-muted); font-family: var(--font-mono); - font-weight: 400; + + &[data-slot="payment-date"] { + color: var(--color-text); + } + + &[data-slot="payment-id"] { + font-family: var(--font-mono); + font-weight: 400; + color: var(--color-text-muted); + max-width: 200px; + word-break: break-word; + } + + &[data-slot="payment-amount"] { + color: var(--color-text); + } + } + + tbody tr { + &:last-child td { + border-bottom: none; + } + } + + @media (max-width: 40rem) { + th, + td { + padding: var(--space-2) var(--space-3); + font-size: var(--font-size-xs); + } + + th { + &:nth-child(2) /* Payment ID */ { + display: none; + } + } + + td { + &:nth-child(2) /* Payment ID */ { + display: none; + } + } + } + } + } + + /* Usage Section */ + [data-slot="usage-section"] { + [data-slot="usage-table"] { + overflow-x: auto; + } + + [data-slot="usage-table-element"] { + width: 100%; + border-collapse: collapse; + font-size: var(--font-size-sm); + + thead { + border-bottom: 1px solid var(--color-border); + } + + th { + padding: var(--space-3) var(--space-4); + text-align: left; + font-weight: normal; color: var(--color-text-muted); - max-width: 200px; - word-break: break-word; + text-transform: uppercase; } - &[data-slot="payment-amount"] { - color: var(--color-text); - } - } - - tbody tr { - &:last-child td { - border-bottom: none; - } - } - - @media (max-width: 40rem) { - th, td { - padding: var(--space-2) var(--space-3); - font-size: var(--font-size-xs); - } + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid var(--color-border-muted); + color: var(--color-text-muted); + font-family: var(--font-mono); - th { - &:nth-child(2) /* Payment ID */ { - display: none; + &[data-slot="usage-date"] { + color: var(--color-text); + } + + &[data-slot="usage-model"] { + font-family: var(--font-sans); + font-weight: 400; + color: var(--color-text-secondary); + max-width: 200px; + word-break: break-word; + } + + &[data-slot="usage-cost"] { + color: var(--color-text); } } - td { - &:nth-child(2) /* Payment ID */ { - display: none; - } - } - } - } -} - -/* Usage Section */ -[data-slot="usage-section"] { - [data-slot="usage-table"] { - overflow-x: auto; - } - - [data-slot="usage-table-element"] { - width: 100%; - border-collapse: collapse; - font-size: var(--font-size-sm); - - thead { - border-bottom: 1px solid var(--color-border); - } - - th { - padding: var(--space-3) var(--space-4); - text-align: left; - font-weight: normal; - color: var(--color-text-muted); - text-transform: uppercase; - } - - td { - padding: var(--space-3) var(--space-4); - border-bottom: 1px solid var(--color-border-muted); - color: var(--color-text-muted); - font-family: var(--font-mono); - - &[data-slot="usage-date"] { - color: var(--color-text); - } - - &[data-slot="usage-model"] { - font-family: var(--font-sans); - font-weight: 400; - color: var(--color-text-secondary); - max-width: 200px; - word-break: break-word; - } - - &[data-slot="usage-cost"] { - color: var(--color-text); - } - } - - tbody tr { - &:last-child td { - border-bottom: none; - } - } - - @media (max-width: 40rem) { - th, - td { - padding: var(--space-2) var(--space-3); - font-size: var(--font-size-xs); - } - - th { - &:nth-child(2) /* Model */ { - display: none; - } - } - - td { - &:nth-child(2) /* Model */ { - display: none; + tbody tr { + &:last-child td { + border-bottom: none; + } + } + + @media (max-width: 40rem) { + th, + td { + padding: var(--space-2) var(--space-3); + font-size: var(--font-size-xs); + } + + th { + &:nth-child(2) /* Model */ { + display: none; + } + } + + td { + &:nth-child(2) /* Model */ { + display: none; + } } } } diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index ccdfe42f52..70f1a7cdb9 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -168,7 +168,7 @@ const dummyApiKeyData = [ }, ] -export default function () { +export default function() { const actor = createAsync(() => getActor()) onMount(() => { console.log("MOUNTED", actor()) @@ -292,15 +292,6 @@ export default function () {
- {/* Actor Section */} -
-
-

Actor

-

Current authenticated user information and session details.

-
-
{JSON.stringify(actor())}
-
- {/* API Keys Section */}
@@ -321,14 +312,7 @@ export default function () { />
- +
} >
+ {/* when={keys()?.length */} 0} fallback={

Create an opencode Gateway API key

@@ -369,7 +361,7 @@ export default function () { - + {/* Real data: keys() */} {(key) => ( @@ -389,7 +381,7 @@ export default function () { {formatDateForTable(key.timeCreated)} - @@ -426,7 +418,7 @@ export default function () { })()}
-
From d3bbaa141ca74853d5f4c9f2a13575adedf824d8 Mon Sep 17 00:00:00 2001 From: Jay V Date: Sat, 30 Aug 2025 15:28:35 -0400 Subject: [PATCH 02/10] ignore: cloud --- cloud/app/src/routes/workspace.css | 3 + cloud/app/src/routes/workspace.tsx | 20 ++- cloud/app/src/routes/workspace/[id].css | 3 +- cloud/app/src/routes/workspace/[id].tsx | 195 +++++------------------- 4 files changed, 64 insertions(+), 157 deletions(-) diff --git a/cloud/app/src/routes/workspace.css b/cloud/app/src/routes/workspace.css index e18b410ee1..e378ef461c 100644 --- a/cloud/app/src/routes/workspace.css +++ b/cloud/app/src/routes/workspace.css @@ -54,7 +54,10 @@ a, button { + appearance: none; + background: none; border: none; + cursor: pointer; padding: 0; color: var(--color-text); text-decoration: underline; diff --git a/cloud/app/src/routes/workspace.tsx b/cloud/app/src/routes/workspace.tsx index 6876ae962f..f75af8e74d 100644 --- a/cloud/app/src/routes/workspace.tsx +++ b/cloud/app/src/routes/workspace.tsx @@ -1,7 +1,20 @@ import "./workspace.css" import { useAuthSession } from "~/context/auth.session" import { IconLogo } from "../component/icon" -import { action, redirect, RouteSectionProps } from "@solidjs/router" +import { withActor } from "~/context/auth.withActor" +import "./workspace.css" +import { query, action, redirect, createAsync, RouteSectionProps } from "@solidjs/router" +import { User } from "@opencode/cloud-core/user.js" +import { Actor } from "@opencode/cloud-core/actor.js" + +const getUserInfo = query(async () => { + "use server" + return withActor(async () => { + const actor = Actor.assert("user") + const user = await User.fromID(actor.properties.userID) + return { user } + }) +}, "userInfo") const logout = action(async () => { "use server" @@ -17,6 +30,7 @@ const logout = action(async () => { }) export default function WorkspaceLayout(props: RouteSectionProps) { + const userInfo = createAsync(() => getUserInfo()) return (
@@ -26,7 +40,9 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
- name@example.com + {userInfo() && + {userInfo()!.user.email} + }
diff --git a/cloud/app/src/routes/workspace/[id].css b/cloud/app/src/routes/workspace/[id].css index d29f5af32f..651cae627b 100644 --- a/cloud/app/src/routes/workspace/[id].css +++ b/cloud/app/src/routes/workspace/[id].css @@ -168,13 +168,13 @@ [data-slot="api-keys-section"] { [data-slot="create-form"] { 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); input { + flex: 1; padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); border-radius: var(--border-radius-sm); @@ -196,7 +196,6 @@ [data-slot="form-actions"] { display: flex; gap: var(--space-2); - justify-content: flex-end; } } diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index 70f1a7cdb9..a03811c178 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -63,116 +63,8 @@ const createPortalUrl = action(async (returnUrl: string) => { 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"), - }, -] - -const dummyPaymentData = [ - { - id: "pay_1Ab2Cd3Ef4Gh5678", - amount: 2000000000, - timeCreated: new Date("2025-01-28T14:32:00Z"), - }, - { - id: "pay_9Ij8Kl7Mn6Op5432", - amount: 1000000000, - timeCreated: new Date("2025-01-25T09:18:00Z"), - }, - { - id: "pay_5Qr4St3Uv2Wx1098", - amount: 5000000000, - timeCreated: new Date("2025-01-20T16:45:00Z"), - }, - { - id: "pay_7Yz6Ab5Cd4Ef3210", - amount: 1500000000, - timeCreated: new Date("2025-01-15T11:22:00Z"), - }, - { - id: "pay_3Gh2Ij1Kl0Mn9876", - amount: 3000000000, - timeCreated: new Date("2025-01-10T13:55:00Z"), - }, -] - -const dummyApiKeyData = [ - { - id: "key_1Ab2Cd3Ef4Gh5678", - name: "Production API", - key: "oc_live_sk_1Ab2Cd3Ef4Gh567890123456789012345678901234567890", - timeCreated: new Date("2025-01-28T14:32:00Z"), - timeUsed: new Date("2025-01-29T09:15:00Z"), - }, - { - id: "key_9Ij8Kl7Mn6Op5432", - name: "Development Key", - key: "oc_test_sk_9Ij8Kl7Mn6Op543210987654321098765432109876543210", - timeCreated: new Date("2025-01-25T09:18:00Z"), - timeUsed: null, - }, - { - id: "key_5Qr4St3Uv2Wx1098", - name: "CI/CD Pipeline", - key: "oc_live_sk_5Qr4St3Uv2Wx109876543210987654321098765432109876", - timeCreated: new Date("2025-01-20T16:45:00Z"), - timeUsed: new Date("2025-01-28T12:30:00Z"), - }, -] - export default function() { const actor = createAsync(() => getActor()) - onMount(() => { - console.log("MOUNTED", actor()) - }) ///////////////// // Keys section @@ -342,9 +234,8 @@ export default function() {
- {/* when={keys()?.length */} 0} + when={keys()?.length} fallback={

Create an opencode Gateway API key

@@ -361,8 +252,7 @@ export default function() { - - {/* Real data: keys() */} + {(key) => ( {key.name} @@ -424,45 +314,6 @@ export default function() {
- {/* Payments Section */} - 0}> - {/* Real data condition: billingInfo() && billingInfo()!.payments.length > 0 */} -
-
-

Payments History

-

Recent payment transactions.

-
-
- - - - - - - - - - - {/* Real data: billingInfo()?.payments */} - {(payment) => { - const date = new Date(payment.timeCreated) - return ( - - - - - - ) - }} - - -
DatePayment IDAmount
- {formatDateForTable(date)} - {payment.id}${((payment.amount ?? 0) / 100000000).toFixed(2)}
-
-
-
- {/* Usage Section */}
@@ -471,7 +322,7 @@ export default function() {
0} + when={billingInfo() && billingInfo()!.usage.length > 0} fallback={

Make your first API call to get started.

@@ -488,7 +339,7 @@ export default function() { - + {(usage) => { const totalTokens = usage.inputTokens + usage.outputTokens + (usage.reasoningTokens || 0) const date = new Date(usage.timeCreated) @@ -509,6 +360,44 @@ export default function() {
+ + {/* Payments Section */} + 0}> +
+
+

Payments History

+

Recent payment transactions.

+
+
+ + + + + + + + + + + {(payment) => { + const date = new Date(payment.timeCreated) + return ( + + + + + + ) + }} + + +
DatePayment IDAmount
+ {formatDateForTable(date)} + {payment.id}${((payment.amount ?? 0) / 100000000).toFixed(2)}
+
+
+
+
) From b4d95545e0d902da8398de63f4ae63c240e02792 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:49:13 -0500 Subject: [PATCH 03/10] add support for lsp workspace/didChangeConfiguration (#2334) --- packages/opencode/src/lsp/client.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 509e982eb3..a03a265145 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -60,7 +60,7 @@ export namespace LSPClient { return null }) connection.onRequest("workspace/configuration", async () => { - return [{}] + return [input.server.initialization ?? {}] }) connection.listen() @@ -109,6 +109,12 @@ export namespace LSPClient { await connection.sendNotification("initialized", {}) + if (input.server.initialization) { + await connection.sendNotification("workspace/didChangeConfiguration", { + settings: input.server.initialization, + }) + } + const files: { [path: string]: number } = {} From 2946898934fe458bce56720203f1074ddc3c524c Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 30 Aug 2025 15:41:06 -0500 Subject: [PATCH 04/10] fix: ensure command uses currently selected model (#2336) --- packages/tui/internal/app/app.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index cbf12e504d..8aaddfbcc7 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -814,15 +814,18 @@ func (a *App) SendCommand(ctx context.Context, command string, args string) (*Ap } cmds = append(cmds, func() tea.Msg { + params := opencode.SessionCommandParams{ + Command: opencode.F(command), + Arguments: opencode.F(args), + Agent: opencode.F(a.Agents[a.AgentIndex].Name), + } + if a.Provider != nil && a.Model != nil { + params.Model = opencode.F(a.Provider.ID + "/" + a.Model.ID) + } _, err := a.Client.Session.Command( context.Background(), a.Session.ID, - opencode.SessionCommandParams{ - Command: opencode.F(command), - Arguments: opencode.F(args), - Agent: opencode.F(a.Agents[a.AgentIndex].Name), - Model: opencode.F(a.State.Provider + "/" + a.State.Model), - }, + params, ) if err != nil { slog.Error("Failed to execute command", "error", err) From fcfeac57c53b16c149e2da038a0b212b425f029b Mon Sep 17 00:00:00 2001 From: Andre van Tonder Date: Sun, 31 Aug 2025 14:53:03 +1000 Subject: [PATCH 05/10] fix: resolve virtual envs for python LSP (#2155) Co-authored-by: rekram1-node --- packages/opencode/src/lsp/client.ts | 1 + packages/opencode/src/lsp/server.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index a03a265145..c6ccfbb0de 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -60,6 +60,7 @@ export namespace LSPClient { return null }) connection.onRequest("workspace/configuration", async () => { + // Return server initialization options return [input.server.initialization ?? {}] }) connection.listen() diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index da743628d6..9861a57986 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -298,6 +298,23 @@ export namespace LSPServer { args.push(...["run", js]) } args.push("--stdio") + + const initialization: Record = {} + + const potentialVenvPaths = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")].filter( + (p): p is string => p !== undefined, + ) + for (const venvPath of potentialVenvPaths) { + const isWindows = process.platform === "win32" + const potentialPythonPath = isWindows + ? path.join(venvPath, "Scripts", "python.exe") + : path.join(venvPath, "bin", "python") + if (await Bun.file(potentialPythonPath).exists()) { + initialization["pythonPath"] = potentialPythonPath + break + } + } + const proc = spawn(binary, args, { cwd: root, env: { @@ -307,6 +324,7 @@ export namespace LSPServer { }) return { process: proc, + initialization, } }, } From ad5f209dc8dd79fd4801854e26c79c452979914b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 31 Aug 2025 12:04:06 +0000 Subject: [PATCH 06/10] ignore: update download stats 2025-08-31 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 62990eb5d6..6b6ad8684c 100644 --- a/STATS.md +++ b/STATS.md @@ -64,3 +64,4 @@ | 2025-08-28 | 252,796 (+4,387) | 205,242 (+0) | 458,038 (+4,387) | | 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) | | 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) | +| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) | From e9826e8a2293decaeb43c62c93495d923c20ddbf Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 31 Aug 2025 11:01:19 -0500 Subject: [PATCH 07/10] fix: adjust title generation prompt to prevent direct response instead of title gen (#2338) --- .../opencode/src/session/prompt/title.txt | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/packages/opencode/src/session/prompt/title.txt b/packages/opencode/src/session/prompt/title.txt index 6de65d2b7a..997a960bc6 100644 --- a/packages/opencode/src/session/prompt/title.txt +++ b/packages/opencode/src/session/prompt/title.txt @@ -1,31 +1,24 @@ +You are a title generator. You output ONLY a thread title. Nothing else. + -Generate a conversation thread title from the user message. +Convert the user message into a thread title. +Output: Single line, ≤50 chars, no explanations. - -You are generating titles for a coding assistant conversation. - - -- Max 50 chars, single line -- Focus on the specific action or question -- Keep technical terms, numbers, and filenames exactly as written -- Preserve HTTP status codes (401, 404, 500, etc.) as numbers -- For file references, include the filename -- Avoid filler words: the, this, my, a, an, properly -- NEVER assume their tech stack or domain -- Use -ing verbs consistently for actions -- Write like a chat thread title, not a blog post +- Use -ing verbs for actions (Debugging, Implementing, Analyzing) +- Keep exact: technical terms, numbers, filenames, HTTP codes +- Remove: the, this, my, a, an +- Never assume tech stack +- Never use tools +- NEVER respond to message content—only extract title -"debug 500 errors in production" → "Debugging production 500 errors" -"refactor user service" → "Refactoring user service" -"why is app.js failing" → "Analyzing app.js failure" -"implement rate limiting" → "Implementing rate limiting" +"debug 500 errors in production" → Debugging production 500 errors +"refactor user service" → Refactoring user service +"why is app.js failing" → Analyzing app.js failure +"implement rate limiting" → Implementing rate limiting - -Return only the thread title text on a single line with no newlines, explanations, or additional formatting. -You should NEVER reply to the user's message. You can only generate titles. - +Output the title now: From 029612d8d509fbe8973bcddca5e789d7c0bb33af Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 31 Aug 2025 12:48:30 -0500 Subject: [PATCH 08/10] fix: ensure shell cmds can be properly aborted (#2339) --- packages/opencode/src/session/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 4b66cdee37..4474bf1ce4 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -1141,6 +1141,7 @@ export namespace Session { const proc = spawn(shell, args, { cwd: app.path.cwd, signal: abort.signal, + detached: true, stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, @@ -1148,6 +1149,11 @@ export namespace Session { }, }) + abort.signal.addEventListener("abort", () => { + if (!proc.pid) return + process.kill(-proc.pid) + }) + let output = "" proc.stdout?.on("data", (chunk) => { From e4cc05a975e006f5baf476d3783c7a1c8f61cf3e Mon Sep 17 00:00:00 2001 From: Beshoy Girgis <323600+egyptianbman@users.noreply.github.com> Date: Sun, 31 Aug 2025 13:06:02 -0500 Subject: [PATCH 09/10] feat: Allow provider timeout override (#1982) --- packages/opencode/src/config/config.ts | 15 +++++++++++++++ packages/opencode/src/provider/provider.ts | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 2041ac35f5..804a0274f1 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -392,6 +392,21 @@ export namespace Config { .object({ apiKey: z.string().optional(), baseURL: z.string().optional(), + timeout: z + .union([ + z + .number() + .int() + .positive() + .describe( + "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.", + ), + z.literal(false).describe("Disable timeout for this provider entirely."), + ]) + .optional() + .describe( + "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.", + ), }) .catchall(z.any()) .optional(), diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index e37f11f06c..c60bbf79c3 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -320,9 +320,16 @@ export namespace Provider { const pkg = provider.npm ?? provider.id const mod = await import(await BunProc.install(pkg, "latest")) const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!] + let options = { ...s.providers[provider.id]?.options } + if (options["timeout"] !== undefined) { + // Only override fetch if user explicitly sets timeout + options["fetch"] = async (input: any, init?: any) => { + return await fetch(input, { ...init, timeout: options["timeout"] }) + } + } const loaded = fn({ name: provider.id, - ...s.providers[provider.id]?.options, + ...options, }) s.sdk.set(provider.id, loaded) return loaded as SDK From 65f0bea1463e21b881a1577c695a110156d02b75 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 31 Aug 2025 17:11:04 -0500 Subject: [PATCH 10/10] ignore: better error logging (#2346) --- packages/opencode/src/util/log.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 1b321523a1..b4cdd920ac 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -83,6 +83,13 @@ export namespace Log { await Promise.all(filesToDelete.map((file) => fs.unlink(file).catch(() => {}))) } + function formatError(error: Error, depth = 0): string { + const result = error.message + return error.cause instanceof Error && depth < 10 + ? result + " Caused by: " + formatError(error.cause, depth + 1) + : result + } + let last = Date.now() export function create(tags?: Record) { tags = tags || {} @@ -103,7 +110,7 @@ export namespace Log { .filter(([_, value]) => value !== undefined && value !== null) .map(([key, value]) => { const prefix = `${key}=` - if (value instanceof Error) return prefix + value.message + if (value instanceof Error) return prefix + formatError(value) if (typeof value === "object") return prefix + JSON.stringify(value) return prefix + value })