From 272349b8da4d33f29b9249e55d7bd4055659ba60 Mon Sep 17 00:00:00 2001 From: "Jared A. Scheel" Date: Tue, 4 Nov 2025 15:40:29 -0600 Subject: [PATCH 001/218] Add support for `uv format` in formatters (#3916) --- packages/opencode/src/format/formatter.ts | 15 +++++++++++++++ packages/web/src/content/docs/formatters.mdx | 1 + 2 files changed, 16 insertions(+) diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index d2a9eee83..baa39906f 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -177,6 +177,21 @@ export const ruff: Info = { }, } +export const uvformat: Info = { + name: "uv format", + command: ["uv", "format", "--", "$FILE"], + extensions: [".py", ".pyi"], + async enabled() { + if (await ruff.enabled()) return false + if (Bun.which("uv") !== null) { + const proc = Bun.spawn(["uv", "format", "--help"], { stderr: "pipe", stdout: "pipe" }) + const code = await proc.exited + return code === 0 + } + return false + }, +} + export const rubocop: Info = { name: "rubocop", command: ["rubocop", "--autocorrect", "$FILE"], diff --git a/packages/web/src/content/docs/formatters.mdx b/packages/web/src/content/docs/formatters.mdx index 8853ca334..4b46a4f63 100644 --- a/packages/web/src/content/docs/formatters.mdx +++ b/packages/web/src/content/docs/formatters.mdx @@ -21,6 +21,7 @@ OpenCode comes with several built-in formatters for popular languages and framew | clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file | | ktlint | .kt, .kts | `ktlint` command available | | ruff | .py, .pyi | `ruff` command available with config | +| uv | .py, .pyi | `uv` command available | | rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available | | standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available | | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | From 678ca757c97ed7992ad905fd7c9add4282a484e6 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 4 Nov 2025 16:08:31 -0600 Subject: [PATCH 002/218] fix: permissions not responding to esc --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index f37be32f7..ccd12819a 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -120,6 +120,7 @@ export function Session() { if (evt.name === "return") return "once" if (evt.name === "a") return "always" if (evt.name === "d") return "reject" + if (evt.name === "escape") return "reject" return }) if (response) { From 71b04ffa99a3215316083dcf4d5e15afb3193958 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 4 Nov 2025 14:33:38 -0500 Subject: [PATCH 003/218] add command bar option to interrupt session --- .../src/cli/cmd/tui/component/prompt/index.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 785eb7e4b..cf497407a 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -156,10 +156,11 @@ export function Prompt(props: PromptProps) { title: "Interrupt session", value: "session.interrupt", keybind: "session_interrupt", + disabled: status() !== "working", category: "Session", - disabled: true, onSelect: (dialog) => { if (!props.sessionID) return + if (autocomplete.visible) return sdk.client.session.abort({ path: { id: props.sessionID, @@ -602,16 +603,6 @@ export function Prompt(props: PromptProps) { ) input.cursorOffset = input.plainText.length } - if (!autocomplete.visible) { - if (keybind.match("session_interrupt", e) && props.sessionID) { - sdk.client.session.abort({ - path: { - id: props.sessionID, - }, - }) - return - } - } }} onSubmit={submit} onPaste={async (event: PasteEvent) => { From 8d6a03cc898c0b982b7a05419d41a07e8db579f8 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 4 Nov 2025 16:51:38 -0500 Subject: [PATCH 004/218] zen: custom reload amount --- .../console/app/src/routes/stripe/webhook.ts | 265 +++++++++--------- .../[id]/billing/billing-section.module.css | 51 ++++ .../[id]/billing/billing-section.tsx | 187 ++++++++---- .../[id]/billing/monthly-limit-section.tsx | 32 +-- .../[id]/billing/reload-section.module.css | 202 +++++++++++++ .../workspace/[id]/billing/reload-section.tsx | 162 +++++++++-- .../app/src/routes/workspace/[id]/index.tsx | 42 +-- .../app/src/routes/workspace/common.tsx | 33 ++- .../app/src/routes/zen/util/handler.ts | 6 +- packages/console/core/src/billing.ts | 61 ++-- 10 files changed, 767 insertions(+), 274 deletions(-) diff --git a/packages/console/app/src/routes/stripe/webhook.ts b/packages/console/app/src/routes/stripe/webhook.ts index cc44f8674..d8d857255 100644 --- a/packages/console/app/src/routes/stripe/webhook.ts +++ b/packages/console/app/src/routes/stripe/webhook.ts @@ -13,146 +13,157 @@ export async function POST(input: APIEvent) { input.request.headers.get("stripe-signature")!, Resource.STRIPE_WEBHOOK_SECRET.value, ) - console.log(body.type, JSON.stringify(body, null, 2)) - if (body.type === "customer.updated") { - // check default payment method changed - const prevInvoiceSettings = body.data.previous_attributes?.invoice_settings ?? {} - if (!("default_payment_method" in prevInvoiceSettings)) return - const customerID = body.data.object.id - const paymentMethodID = body.data.object.invoice_settings.default_payment_method as string + return (async () => { + if (body.type === "customer.updated") { + // check default payment method changed + const prevInvoiceSettings = body.data.previous_attributes?.invoice_settings ?? {} + if (!("default_payment_method" in prevInvoiceSettings)) return "ignored" - if (!customerID) throw new Error("Customer ID not found") - if (!paymentMethodID) throw new Error("Payment method ID not found") + const customerID = body.data.object.id + const paymentMethodID = body.data.object.invoice_settings.default_payment_method as string - const paymentMethod = await Billing.stripe().paymentMethods.retrieve(paymentMethodID) - await Database.use(async (tx) => { - await tx - .update(BillingTable) - .set({ - paymentMethodID, - paymentMethodLast4: paymentMethod.card?.last4 ?? null, - paymentMethodType: paymentMethod.type, - }) - .where(eq(BillingTable.customerID, customerID)) - }) - } - if (body.type === "checkout.session.completed") { - const workspaceID = body.data.object.metadata?.workspaceID - const customerID = body.data.object.customer as string - const paymentID = body.data.object.payment_intent as string - const invoiceID = body.data.object.invoice as string - const amount = body.data.object.amount_total + if (!customerID) throw new Error("Customer ID not found") + if (!paymentMethodID) throw new Error("Payment method ID not found") - if (!workspaceID) throw new Error("Workspace ID not found") - if (!customerID) throw new Error("Customer ID not found") - if (!amount) throw new Error("Amount not found") - if (!paymentID) throw new Error("Payment ID not found") - if (!invoiceID) throw new Error("Invoice ID not found") - - await Actor.provide("system", { workspaceID }, async () => { - const customer = await Billing.get() - if (customer?.customerID && customer.customerID !== customerID) - throw new Error("Customer ID mismatch") - - // set customer metadata - if (!customer?.customerID) { - await Billing.stripe().customers.update(customerID, { - metadata: { - workspaceID, - }, - }) - } - - // get payment method for the payment intent - const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, { - expand: ["payment_method"], - }) - const paymentMethod = paymentIntent.payment_method - if (!paymentMethod || typeof paymentMethod === "string") - throw new Error("Payment method not expanded") - - const oldBillingInfo = await Database.use((tx) => - tx - .select({ - customerID: BillingTable.customerID, - }) - .from(BillingTable) - .where(eq(BillingTable.workspaceID, workspaceID)) - .then((rows) => rows[0]), - ) - - await Database.transaction(async (tx) => { + const paymentMethod = await Billing.stripe().paymentMethods.retrieve(paymentMethodID) + await Database.use(async (tx) => { await tx .update(BillingTable) .set({ - balance: sql`${BillingTable.balance} + ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`, - customerID, - paymentMethodID: paymentMethod.id, + paymentMethodID, paymentMethodLast4: paymentMethod.card?.last4 ?? null, paymentMethodType: paymentMethod.type, - // enable reload if first time enabling billing - ...(oldBillingInfo?.customerID - ? {} - : { - reload: true, - reloadError: null, - timeReloadError: null, - }), }) - .where(eq(BillingTable.workspaceID, workspaceID)) - await tx.insert(PaymentTable).values({ - workspaceID, - id: Identifier.create("payment"), - amount: centsToMicroCents(Billing.CHARGE_AMOUNT), - paymentID, - invoiceID, - customerID, + .where(eq(BillingTable.customerID, customerID)) + }) + } + if (body.type === "checkout.session.completed") { + const workspaceID = body.data.object.metadata?.workspaceID + const amountInCents = + body.data.object.metadata?.amount && parseInt(body.data.object.metadata?.amount) + const customerID = body.data.object.customer as string + const paymentID = body.data.object.payment_intent as string + const invoiceID = body.data.object.invoice as string + + if (!workspaceID) throw new Error("Workspace ID not found") + if (!customerID) throw new Error("Customer ID not found") + if (!amountInCents) throw new Error("Amount not found") + if (!paymentID) throw new Error("Payment ID not found") + if (!invoiceID) throw new Error("Invoice ID not found") + + await Actor.provide("system", { workspaceID }, async () => { + const customer = await Billing.get() + if (customer?.customerID && customer.customerID !== customerID) + throw new Error("Customer ID mismatch") + + // set customer metadata + if (!customer?.customerID) { + await Billing.stripe().customers.update(customerID, { + metadata: { + workspaceID, + }, + }) + } + + // get payment method for the payment intent + const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, { + expand: ["payment_method"], + }) + const paymentMethod = paymentIntent.payment_method + if (!paymentMethod || typeof paymentMethod === "string") + throw new Error("Payment method not expanded") + + await Database.transaction(async (tx) => { + await tx + .update(BillingTable) + .set({ + balance: sql`${BillingTable.balance} + ${centsToMicroCents(amountInCents)}`, + customerID, + paymentMethodID: paymentMethod.id, + paymentMethodLast4: paymentMethod.card?.last4 ?? null, + paymentMethodType: paymentMethod.type, + // enable reload if first time enabling billing + ...(customer?.customerID + ? {} + : { + reload: true, + reloadError: null, + timeReloadError: null, + }), + }) + .where(eq(BillingTable.workspaceID, workspaceID)) + await tx.insert(PaymentTable).values({ + workspaceID, + id: Identifier.create("payment"), + amount: centsToMicroCents(amountInCents), + paymentID, + invoiceID, + customerID, + }) }) }) + } + if (body.type === "charge.refunded") { + const customerID = body.data.object.customer as string + const paymentIntentID = body.data.object.payment_intent as string + if (!customerID) throw new Error("Customer ID not found") + if (!paymentIntentID) throw new Error("Payment ID not found") + + const workspaceID = await Database.use((tx) => + tx + .select({ + workspaceID: BillingTable.workspaceID, + }) + .from(BillingTable) + .where(eq(BillingTable.customerID, customerID)) + .then((rows) => rows[0]?.workspaceID), + ) + if (!workspaceID) throw new Error("Workspace ID not found") + + const amount = await Database.use((tx) => + tx + .select({ + amount: PaymentTable.amount, + }) + .from(PaymentTable) + .where( + and( + eq(PaymentTable.paymentID, paymentIntentID), + eq(PaymentTable.workspaceID, workspaceID), + ), + ) + .then((rows) => rows[0]?.amount), + ) + if (!amount) throw new Error("Payment not found") + + await Database.transaction(async (tx) => { + await tx + .update(PaymentTable) + .set({ + timeRefunded: new Date(body.created * 1000), + }) + .where( + and( + eq(PaymentTable.paymentID, paymentIntentID), + eq(PaymentTable.workspaceID, workspaceID), + ), + ) + + await tx + .update(BillingTable) + .set({ + balance: sql`${BillingTable.balance} - ${amount}`, + }) + .where(eq(BillingTable.workspaceID, workspaceID)) + }) + } + })() + .then((message) => { + return Response.json({ message: message ?? "done" }, { status: 200 }) }) - } - if (body.type === "charge.refunded") { - const customerID = body.data.object.customer as string - const paymentIntentID = body.data.object.payment_intent as string - if (!customerID) throw new Error("Customer ID not found") - if (!paymentIntentID) throw new Error("Payment ID not found") - - const workspaceID = await Database.use((tx) => - tx - .select({ - workspaceID: BillingTable.workspaceID, - }) - .from(BillingTable) - .where(eq(BillingTable.customerID, customerID)) - .then((rows) => rows[0]?.workspaceID), - ) - if (!workspaceID) throw new Error("Workspace ID not found") - - await Database.transaction(async (tx) => { - await tx - .update(PaymentTable) - .set({ - timeRefunded: new Date(body.created * 1000), - }) - .where( - and( - eq(PaymentTable.paymentID, paymentIntentID), - eq(PaymentTable.workspaceID, workspaceID), - ), - ) - - await tx - .update(BillingTable) - .set({ - balance: sql`${BillingTable.balance} - ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`, - }) - .where(eq(BillingTable.workspaceID, workspaceID)) + .catch((error: any) => { + return Response.json({ message: error.message }, { status: 500 }) }) - } - - console.log("finished handling") - - return Response.json("ok", { status: 200 }) } diff --git a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css index e0a80ef74..aef008a48 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.module.css @@ -71,6 +71,57 @@ flex: 1; } + [data-slot="add-balance-form-container"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + } + + [data-slot="add-balance-form"] { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--space-3); + + label { + font-size: var(--font-size-sm); + font-weight: 500; + color: var(--color-text-muted); + white-space: nowrap; + } + + input[data-component="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); + line-height: 1.5; + + &:focus { + outline: none; + border-color: var(--color-accent); + box-shadow: 0 0 0 3px var(--color-accent-alpha); + } + + &::placeholder { + color: var(--color-text-disabled); + } + } + + [data-slot="form-actions"] { + display: flex; + gap: var(--space-2); + } + } + + [data-slot="form-error"] { + color: var(--color-danger); + font-size: var(--font-size-sm); + line-height: 1.4; + } + [data-slot="credit-card"] { padding: var(--space-2) var(--space-4); background-color: var(--color-bg-surface); diff --git a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx index c0723136b..9e51bbe10 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/billing-section.tsx @@ -1,24 +1,80 @@ -import { action, useParams, useAction, createAsync, useSubmission } from "@solidjs/router" -import { createMemo, Match, Show, Switch } from "solid-js" +import { action, useParams, useAction, createAsync, useSubmission, json } from "@solidjs/router" +import { createMemo, Match, Show, Switch, createEffect } from "solid-js" +import { createStore } from "solid-js/store" import { Billing } from "@opencode-ai/console-core/billing.js" import { withActor } from "~/context/auth.withActor" import { IconCreditCard, IconStripe } from "~/component/icon" import styles from "./billing-section.module.css" -import { createCheckoutUrl, queryBillingInfo } from "../../common" +import { createCheckoutUrl, formatBalance, queryBillingInfo } from "../../common" const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => { "use server" - return withActor(() => Billing.generateSessionUrl({ returnUrl }), workspaceID) + return json( + await withActor( + () => + Billing.generateSessionUrl({ returnUrl }) + .then((data) => ({ error: undefined, data })) + .catch((e) => ({ + error: e.message as string, + data: undefined, + })), + workspaceID, + ), + { revalidate: queryBillingInfo.key }, + ) }, "sessionUrl") export function BillingSection() { const params = useParams() // ORIGINAL CODE - COMMENTED OUT FOR TESTING - const balanceInfo = createAsync(() => queryBillingInfo(params.id)) - const createCheckoutUrlAction = useAction(createCheckoutUrl) - const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl) - const createSessionUrlAction = useAction(createSessionUrl) - const createSessionUrlSubmission = useSubmission(createSessionUrl) + const billingInfo = createAsync(() => queryBillingInfo(params.id)) + const checkoutAction = useAction(createCheckoutUrl) + const checkoutSubmission = useSubmission(createCheckoutUrl) + const sessionAction = useAction(createSessionUrl) + const sessionSubmission = useSubmission(createSessionUrl) + const [store, setStore] = createStore({ + showAddBalanceForm: false, + addBalanceAmount: "", + checkoutRedirecting: false, + sessionRedirecting: false, + }) + const balance = createMemo(() => formatBalance(billingInfo()?.balance ?? 0)) + + async function onClickCheckout() { + const amount = parseInt(store.addBalanceAmount) + const baseUrl = window.location.href + + const checkout = await checkoutAction(params.id, amount, baseUrl, baseUrl) + if (checkout && checkout.data) { + setStore("checkoutRedirecting", true) + window.location.href = checkout.data + } + } + + async function onClickSession() { + const baseUrl = window.location.href + const sessionUrl = await sessionAction(params.id, baseUrl) + if (sessionUrl && sessionUrl.data) { + setStore("sessionRedirecting", true) + window.location.href = sessionUrl.data + } + } + + function showAddBalanceForm() { + while (true) { + checkoutSubmission.clear() + if (!checkoutSubmission.result) break + } + setStore({ + showAddBalanceForm: true, + addBalanceAmount: billingInfo()!.reloadAmount.toString(), + }) + } + + function hideAddBalanceForm() { + setStore("showAddBalanceForm", false) + checkoutSubmission.clear() + } // DUMMY DATA FOR TESTING - UNCOMMENT ONE OF THE SCENARIOS BELOW @@ -72,10 +128,6 @@ export function BillingSection() { // timeReloadError: null as Date | null // }) - const balanceAmount = createMemo(() => { - return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2) - }) - return (
@@ -88,81 +140,110 @@ export function BillingSection() {
- - ${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()} - + ${balance()} Current Balance
- +
- + +
+
+ + {(err: any) =>
{err()}
} +
+
+ } > - {createCheckoutUrlSubmission.pending ? "Loading..." : "Add Balance"} - + +
}> - +
- + ----} > •••• - {balanceInfo()?.paymentMethodLast4} + {billingInfo()?.paymentMethodLast4} - + Linked to Stripe
- + diff --git a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx index dbeda115c..b28b072d5 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx @@ -1,16 +1,10 @@ -import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router" +import { json, action, useParams, createAsync, useSubmission } from "@solidjs/router" import { createEffect, Show } from "solid-js" import { createStore } from "solid-js/store" import { withActor } from "~/context/auth.withActor" import { Billing } from "@opencode-ai/console-core/billing.js" import styles from "./monthly-limit-section.module.css" - -const getBillingInfo = query(async (workspaceID: string) => { - "use server" - return withActor(async () => { - return await Billing.get() - }, workspaceID) -}, "billing.get") +import { queryBillingInfo } from "../../common" const setMonthlyLimit = action(async (form: FormData) => { "use server" @@ -28,7 +22,7 @@ const setMonthlyLimit = action(async (form: FormData) => { .catch((e) => ({ error: e.message as string })), workspaceID, ), - { revalidate: getBillingInfo.key }, + { revalidate: queryBillingInfo.key }, ) }, "billing.setMonthlyLimit") @@ -36,7 +30,7 @@ export function MonthlyLimitSection() { const params = useParams() const submission = useSubmission(setMonthlyLimit) const [store, setStore] = createStore({ show: false }) - const balanceInfo = createAsync(() => getBillingInfo(params.id)) + const billingInfo = createAsync(() => queryBillingInfo(params.id)) let input: HTMLInputElement @@ -73,8 +67,8 @@ export function MonthlyLimitSection() {
- {balanceInfo()?.monthlyLimit ? $ : null} - {balanceInfo()?.monthlyLimit ?? "-"} + {billingInfo()?.monthlyLimit ? $ : null} + {billingInfo()?.monthlyLimit ?? "-"}
- No spending limit set.

}> + No spending limit set.

} + >

- Current usage for {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $ + Current usage for{" "} + {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $ {(() => { - const dateLastUsed = balanceInfo()?.timeMonthlyUsageUpdated + const dateLastUsed = billingInfo()?.timeMonthlyUsageUpdated if (!dateLastUsed) return "0" const current = new Date().toLocaleDateString("en-US", { @@ -128,7 +126,7 @@ export function MonthlyLimitSection() { timeZone: "UTC", }) if (current !== lastUsed) return "0" - return ((balanceInfo()?.monthlyUsage ?? 0) / 100000000).toFixed(2) + return ((billingInfo()?.monthlyUsage ?? 0) / 100000000).toFixed(2) })()} .

diff --git a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.module.css b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.module.css index 08fb8524b..11ab789b2 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.module.css @@ -34,6 +34,206 @@ } } + [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); + margin-top: var(--space-4); + + [data-slot="form-field"] { + display: flex; + flex-direction: column; + gap: var(--space-2); + + label { + display: flex; + flex-direction: column; + gap: var(--space-2); + } + + [data-slot="field-label"] { + font-size: var(--font-size-sm); + font-weight: 500; + color: var(--color-text-muted); + } + + [data-slot="toggle-container"] { + display: flex; + align-items: center; + } + + input[data-component="input"] { + flex: 1; + 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="input-row"] { + display: flex; + flex-direction: row; + gap: var(--space-3); + + @media (max-width: 40rem) { + flex-direction: column; + gap: var(--space-2); + } + } + + [data-slot="input-field"] { + display: flex; + flex-direction: column; + gap: var(--space-1); + flex: 1; + + p { + line-height: 1.2; + margin: 0; + color: var(--color-text-muted); + font-size: var(--font-size-sm); + } + + input[data-component="input"] { + flex: 1; + 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); + line-height: 1.5; + min-width: 0; + + &:focus { + outline: none; + border-color: var(--color-accent); + box-shadow: 0 0 0 3px var(--color-accent-alpha); + } + + &::placeholder { + color: var(--color-text-disabled); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + background-color: var(--color-bg-surface); + } + } + + [data-slot="field-with-connector"] { + display: flex; + align-items: center; + gap: var(--space-2); + + [data-slot="field-connector"] { + font-size: var(--font-size-sm); + color: var(--color-text-muted); + white-space: nowrap; + } + + input[data-component="input"] { + flex: 1; + min-width: 80px; + } + } + } + + [data-slot="form-actions"] { + display: flex; + gap: var(--space-2); + margin-top: var(--space-1); + } + + [data-slot="form-error"] { + color: var(--color-danger); + font-size: var(--font-size-sm); + line-height: 1.4; + margin-top: calc(var(--space-1) * -1); + } + + [data-slot="model-toggle-label"] { + position: relative; + display: inline-block; + width: 2.5rem; + height: 1.5rem; + cursor: pointer; + + input { + opacity: 0; + width: 0; + height: 0; + } + + span { + position: absolute; + inset: 0; + background-color: #ccc; + border: 1px solid #bbb; + border-radius: 1.5rem; + transition: all 0.3s ease; + cursor: pointer; + + &::before { + content: ""; + position: absolute; + top: 50%; + left: 0.125rem; + width: 1.25rem; + height: 1.25rem; + background-color: white; + border: 1px solid #ddd; + border-radius: 50%; + transform: translateY(-50%); + transition: all 0.3s ease; + } + } + + input:checked + span { + background-color: #21ad0e; + border-color: #148605; + + &::before { + transform: translateX(1rem) translateY(-50%); + } + } + + &:hover span { + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); + } + + input:checked:hover + span { + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.3); + } + + &:has(input:disabled) { + cursor: not-allowed; + } + + input:disabled + span { + opacity: 0.5; + cursor: not-allowed; + } + } + } + [data-slot="reload-error"] { display: flex; align-items: center; @@ -54,6 +254,8 @@ gap: var(--space-2); margin: 0; flex-shrink: 0; + padding: 0; + border: none; } } } diff --git a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx index 6be6ddf31..57267a95e 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx @@ -1,17 +1,19 @@ -import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router" -import { Show } from "solid-js" +import { json, action, useParams, createAsync, useSubmission } from "@solidjs/router" +import { createEffect, Show } from "solid-js" +import { createStore } from "solid-js/store" import { withActor } from "~/context/auth.withActor" import { Billing } from "@opencode-ai/console-core/billing.js" import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js" import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js" import styles from "./reload-section.module.css" +import { queryBillingInfo } from "../../common" const reload = action(async (form: FormData) => { "use server" const workspaceID = form.get("workspaceID")?.toString() if (!workspaceID) return { error: "Workspace ID is required" } return json(await withActor(() => Billing.reload(), workspaceID), { - revalidate: getBillingInfo.key, + revalidate: queryBillingInfo.key, }) }, "billing.reload") @@ -20,12 +22,27 @@ const setReload = action(async (form: FormData) => { const workspaceID = form.get("workspaceID")?.toString() if (!workspaceID) return { error: "Workspace ID is required" } const reloadValue = form.get("reload")?.toString() === "true" + const amountStr = form.get("reloadAmount")?.toString() + const triggerStr = form.get("reloadTrigger")?.toString() + + const reloadAmount = amountStr && amountStr.trim() !== "" ? parseInt(amountStr) : null + const reloadTrigger = triggerStr && triggerStr.trim() !== "" ? parseInt(triggerStr) : null + + if (reloadValue) { + if (reloadAmount === null || reloadAmount < Billing.RELOAD_AMOUNT_MIN) + return { error: `Reload amount must be at least $${Billing.RELOAD_AMOUNT_MIN}` } + if (reloadTrigger === null || reloadTrigger < Billing.RELOAD_TRIGGER_MIN) + return { error: `Balance trigger must be at least $${Billing.RELOAD_TRIGGER_MIN}` } + } + return json( await Database.use((tx) => tx .update(BillingTable) .set({ reload: reloadValue, + ...(reloadAmount !== null ? { reloadAmount } : {}), + ...(reloadTrigger !== null ? { reloadTrigger } : {}), ...(reloadValue ? { reloadError: null, @@ -35,22 +52,47 @@ const setReload = action(async (form: FormData) => { }) .where(eq(BillingTable.workspaceID, workspaceID)), ), - { revalidate: getBillingInfo.key }, + { revalidate: queryBillingInfo.key }, ) }, "billing.setReload") -const getBillingInfo = query(async (workspaceID: string) => { - "use server" - return withActor(async () => { - return await Billing.get() - }, workspaceID) -}, "billing.get") - export function ReloadSection() { const params = useParams() - const balanceInfo = createAsync(() => getBillingInfo(params.id)) + const billingInfo = createAsync(() => queryBillingInfo(params.id)) const setReloadSubmission = useSubmission(setReload) const reloadSubmission = useSubmission(reload) + const [store, setStore] = createStore({ + show: false, + reload: false, + reloadAmount: "", + reloadTrigger: "", + }) + + createEffect(() => { + if ( + !setReloadSubmission.pending && + setReloadSubmission.result && + !(setReloadSubmission.result as any).error + ) { + setStore("show", false) + } + }) + + function show() { + while (true) { + setReloadSubmission.clear() + if (!setReloadSubmission.result) break + } + const info = billingInfo()! + setStore("show", true) + setStore("reload", info.reload ? true : true) + setStore("reloadAmount", info.reloadAmount.toString()) + setStore("reloadTrigger", info.reloadTrigger.toString()) + } + + function hide() { + setStore("show", false) + } return (
@@ -58,43 +100,101 @@ export function ReloadSection() {

Auto Reload

Auto reload is disabled. Enable to automatically reload when balance is low.

+

+ Auto reload is disabled. Enable to automatically reload when balance is low. +

} >

- We'll automatically reload $20 (+$1.23 processing fee) when it reaches{" "} - $5. + Auto reload is enabled. We'll reload ${billingInfo()?.reloadAmount}{" "} + (+$1.23 processing fee) when balance reaches ${billingInfo()?.reloadTrigger}.

-
- - - -
+
+ +
+
+ +
+ +
+
+

Reload $

+ setStore("reloadAmount", e.currentTarget.value)} + placeholder={billingInfo()?.reloadAmount.toString()} + disabled={!store.reload} + /> +
+
+

When balance reaches $

+ setStore("reloadTrigger", e.currentTarget.value)} + placeholder={billingInfo()?.reloadTrigger.toString()} + disabled={!store.reload} + /> +
+
+ + + {(err: any) =>
{err()}
} +
+ +
+ + +
+
+
- +

Reload failed at{" "} - {balanceInfo()?.timeReloadError!.toLocaleString("en-US", { + {billingInfo()?.timeReloadError!.toLocaleString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", second: "2-digit", })} - . Reason: {balanceInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment + . Reason: {billingInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment method and try again.

diff --git a/packages/console/app/src/routes/workspace/[id]/index.tsx b/packages/console/app/src/routes/workspace/[id]/index.tsx index 8f7678f21..2e7f7d64b 100644 --- a/packages/console/app/src/routes/workspace/[id]/index.tsx +++ b/packages/console/app/src/routes/workspace/[id]/index.tsx @@ -1,22 +1,32 @@ +import { Show, createMemo } from "solid-js" +import { createStore } from "solid-js/store" +import { createAsync, useParams, useAction, useSubmission } from "@solidjs/router" import { NewUserSection } from "./new-user-section" import { UsageSection } from "./usage-section" import { ModelSection } from "./model-section" import { ProviderSection } from "./provider-section" import { IconLogo } from "~/component/icon" -import { createAsync, useParams, useAction, useSubmission } from "@solidjs/router" -import { querySessionInfo, queryBillingInfo, createCheckoutUrl } from "../common" -import { Show, createMemo } from "solid-js" +import { querySessionInfo, queryBillingInfo, createCheckoutUrl, formatBalance } from "../common" export default function () { const params = useParams() const userInfo = createAsync(() => querySessionInfo(params.id)) const billingInfo = createAsync(() => queryBillingInfo(params.id)) - const createCheckoutUrlAction = useAction(createCheckoutUrl) - const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl) - - const balanceAmount = createMemo(() => { - return ((billingInfo()?.balance ?? 0) / 100000000).toFixed(2) + const checkoutAction = useAction(createCheckoutUrl) + const checkoutSubmission = useSubmission(createCheckoutUrl) + const [store, setStore] = createStore({ + checkoutRedirecting: false, }) + const balance = createMemo(() => formatBalance(billingInfo()?.balance ?? 0)) + + async function onClickCheckout() { + const baseUrl = window.location.href + const checkout = await checkoutAction(params.id, billingInfo()!.reloadAmount, baseUrl, baseUrl) + if (checkout && checkout.data) { + setStore("checkoutRedirecting", true) + window.location.href = checkout.data + } + } return (
@@ -38,21 +48,17 @@ export default function () { } > - Current balance ${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()} + Current balance ${balance()} diff --git a/packages/console/app/src/routes/workspace/common.tsx b/packages/console/app/src/routes/workspace/common.tsx index 69bfebe97..5b638192c 100644 --- a/packages/console/app/src/routes/workspace/common.tsx +++ b/packages/console/app/src/routes/workspace/common.tsx @@ -1,6 +1,6 @@ import { Resource } from "@opencode-ai/console-resource" import { Actor } from "@opencode-ai/console-core/actor.js" -import { action, query } from "@solidjs/router" +import { action, json, query } from "@solidjs/router" import { withActor } from "~/context/auth.withActor" import { Billing } from "@opencode-ai/console-core/billing.js" import { User } from "@opencode-ai/console-core/user.js" @@ -34,6 +34,11 @@ export function formatDateUTC(date: Date) { return date.toLocaleDateString("en-US", options) } +export function formatBalance(amount: number) { + const balance = ((amount ?? 0) / 100000000).toFixed(2) + return balance === "-0.00" ? "0.00" : balance +} + export async function getLastSeenWorkspaceID() { "use server" return withActor(async () => { @@ -71,14 +76,34 @@ export const querySessionInfo = query(async (workspaceID: string) => { }, "session.get") export const createCheckoutUrl = action( - async (workspaceID: string, successUrl: string, cancelUrl: string) => { + async (workspaceID: string, amount: number, successUrl: string, cancelUrl: string) => { "use server" - return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID) + return json( + await withActor( + () => + Billing.generateCheckoutUrl({ amount, successUrl, cancelUrl }) + .then((data) => ({ error: undefined, data })) + .catch((e) => ({ + error: e.message as string, + data: undefined, + })), + workspaceID, + ), + ) }, "checkoutUrl", ) export const queryBillingInfo = query(async (workspaceID: string) => { "use server" - return withActor(() => Billing.get(), workspaceID) + return withActor(async () => { + const billing = await Billing.get() + return { + ...billing, + reloadAmount: billing.reloadAmount ?? Billing.RELOAD_AMOUNT, + reloadAmountMin: Billing.RELOAD_AMOUNT_MIN, + reloadTrigger: billing.reloadTrigger ?? Billing.RELOAD_TRIGGER, + reloadTriggerMin: Billing.RELOAD_TRIGGER_MIN, + } + }, workspaceID) }, "billing.get") diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index 0d46e8580..deab7ded2 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -281,6 +281,7 @@ export async function handler( monthlyLimit: BillingTable.monthlyLimit, monthlyUsage: BillingTable.monthlyUsage, timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated, + reloadTrigger: BillingTable.reloadTrigger, }, user: { id: UserTable.id, @@ -532,7 +533,10 @@ export async function handler( and( eq(BillingTable.workspaceID, authInfo.workspaceID), eq(BillingTable.reload, true), - lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)), + lt( + BillingTable.balance, + centsToMicroCents((authInfo.billing.reloadTrigger ?? Billing.RELOAD_TRIGGER) * 100), + ), or( isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`), diff --git a/packages/console/core/src/billing.ts b/packages/console/core/src/billing.ts index 70bf1bc36..348718146 100644 --- a/packages/console/core/src/billing.ts +++ b/packages/console/core/src/billing.ts @@ -10,13 +10,12 @@ import { centsToMicroCents } from "./util/price" import { User } from "./user" export namespace Billing { - export const CHARGE_NAME = "opencode credits" - export const CHARGE_FEE_NAME = "processing fee" - export const CHARGE_AMOUNT = 2000 // $20 - export const CHARGE_AMOUNT_DOLLAR = 20 - export const CHARGE_FEE = 123 // Stripe fee 4.4% + $0.30 - export const CHARGE_THRESHOLD_DOLLAR = 5 - export const CHARGE_THRESHOLD = 500 // $5 + export const ITEM_CREDIT_NAME = "opencode credits" + export const ITEM_FEE_NAME = "processing fee" + export const RELOAD_AMOUNT = 20 + export const RELOAD_AMOUNT_MIN = 10 + export const RELOAD_TRIGGER = 5 + export const RELOAD_TRIGGER_MIN = 5 export const stripe = () => new Stripe(Resource.STRIPE_SECRET_KEY.value, { apiVersion: "2025-03-31.basil", @@ -33,6 +32,8 @@ export namespace Billing { paymentMethodLast4: BillingTable.paymentMethodLast4, balance: BillingTable.balance, reload: BillingTable.reload, + reloadAmount: BillingTable.reloadAmount, + reloadTrigger: BillingTable.reloadTrigger, monthlyLimit: BillingTable.monthlyLimit, monthlyUsage: BillingTable.monthlyUsage, timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated, @@ -67,17 +68,28 @@ export namespace Billing { ) } + export const calculateFeeInCents = (x: number) => { + // math: x = total - (total * 0.044 + 0.30) + // math: x = total * (1-0.044) - 0.30 + // math: (x + 0.30) / 0.956 = total + return Math.round(((x + 30) / 0.956) * 0.044 + 30) + } + export const reload = async () => { - const { customerID, paymentMethodID } = await Database.use((tx) => + const billing = await Database.use((tx) => tx .select({ customerID: BillingTable.customerID, paymentMethodID: BillingTable.paymentMethodID, + reloadAmount: BillingTable.reloadAmount, }) .from(BillingTable) .where(eq(BillingTable.workspaceID, Actor.workspace())) .then((rows) => rows[0]), ) + const customerID = billing.customerID + const paymentMethodID = billing.paymentMethodID + const amountInCents = (billing.reloadAmount ?? Billing.RELOAD_AMOUNT) * 100 const paymentID = Identifier.create("payment") let invoice try { @@ -89,18 +101,18 @@ export namespace Billing { currency: "usd", }) await Billing.stripe().invoiceItems.create({ - amount: Billing.CHARGE_AMOUNT, + amount: amountInCents, currency: "usd", customer: customerID!, - description: CHARGE_NAME, invoice: draft.id!, + description: ITEM_CREDIT_NAME, }) await Billing.stripe().invoiceItems.create({ - amount: Billing.CHARGE_FEE, + amount: calculateFeeInCents(amountInCents), currency: "usd", customer: customerID!, - description: CHARGE_FEE_NAME, invoice: draft.id!, + description: ITEM_FEE_NAME, }) await Billing.stripe().invoices.finalizeInvoice(draft.id!) invoice = await Billing.stripe().invoices.pay(draft.id!, { @@ -128,7 +140,7 @@ export namespace Billing { await tx .update(BillingTable) .set({ - balance: sql`${BillingTable.balance} + ${centsToMicroCents(CHARGE_AMOUNT)}`, + balance: sql`${BillingTable.balance} + ${centsToMicroCents(amountInCents)}`, reloadError: null, timeReloadError: null, }) @@ -136,7 +148,7 @@ export namespace Billing { await tx.insert(PaymentTable).values({ workspaceID: Actor.workspace(), id: paymentID, - amount: centsToMicroCents(CHARGE_AMOUNT), + amount: centsToMicroCents(amountInCents), invoiceID: invoice.id!, paymentID: invoice.payments?.data[0].payment.payment_intent as string, customerID, @@ -159,13 +171,19 @@ export namespace Billing { z.object({ successUrl: z.string(), cancelUrl: z.string(), + amount: z.number().optional(), }), async (input) => { const user = Actor.assert("user") - const { successUrl, cancelUrl } = input + const { successUrl, cancelUrl, amount } = input + + if (amount !== undefined && amount < Billing.RELOAD_AMOUNT_MIN) { + throw new Error(`Amount must be at least $${Billing.RELOAD_AMOUNT_MIN}`) + } const email = await User.getAuthEmail(user.properties.userID) const customer = await Billing.get() + const amountInCents = (amount ?? customer.reloadAmount ?? Billing.RELOAD_AMOUNT) * 100 const session = await Billing.stripe().checkout.sessions.create({ mode: "payment", billing_address_collection: "required", @@ -173,20 +191,16 @@ export namespace Billing { { price_data: { currency: "usd", - product_data: { - name: CHARGE_NAME, - }, - unit_amount: CHARGE_AMOUNT, + product_data: { name: ITEM_CREDIT_NAME }, + unit_amount: amountInCents, }, quantity: 1, }, { price_data: { currency: "usd", - product_data: { - name: CHARGE_FEE_NAME, - }, - unit_amount: CHARGE_FEE, + product_data: { name: ITEM_FEE_NAME }, + unit_amount: calculateFeeInCents(amountInCents), }, quantity: 1, }, @@ -218,6 +232,7 @@ export namespace Billing { }, metadata: { workspaceID: Actor.workspace(), + amount: amountInCents.toString(), }, success_url: successUrl, cancel_url: cancelUrl, From 9fb49ab87b4fbf3890ef6db80602abbbe517f176 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 4 Nov 2025 17:14:31 -0500 Subject: [PATCH 005/218] wip: zen --- .../src/routes/workspace/[id]/billing/reload-section.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx index 57267a95e..50d00ef30 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx @@ -182,8 +182,8 @@ export function ReloadSection() {
-
- + +

Reload failed at{" "} @@ -204,8 +204,8 @@ export function ReloadSection() {

- -
+
+
) } From 16e2bded5b9c47ac6e05c7623ae0800921874f0e Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 4 Nov 2025 17:24:18 -0500 Subject: [PATCH 006/218] wip: zen --- .../routes/workspace/[id]/billing/monthly-limit-section.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx index b28b072d5..e6461ac83 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx @@ -62,7 +62,7 @@ export function MonthlyLimitSection() {

Monthly Limit

-

Set a monthly spending limit for your account.

+

Set a monthly usage limit for your account.

@@ -106,7 +106,7 @@ export function MonthlyLimitSection() {
No spending limit set.

} + fallback={

No usage limit set.

} >

Current usage for{" "} From ee9aa24a554f356529d3dcb32b702be8adf555b1 Mon Sep 17 00:00:00 2001 From: Jay V Date: Tue, 4 Nov 2025 17:27:13 -0500 Subject: [PATCH 007/218] ignore: update meta description to use proper OpenCode capitalization --- packages/console/app/src/app.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/console/app/src/app.tsx b/packages/console/app/src/app.tsx index bc3961214..7976f6b3b 100644 --- a/packages/console/app/src/app.tsx +++ b/packages/console/app/src/app.tsx @@ -12,7 +12,10 @@ export default function App() { root={(props) => ( opencode - + {props.children} )} From d3e080894ca9eb6432fb93d85d2e74d230921e14 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 4 Nov 2025 17:54:06 -0500 Subject: [PATCH 008/218] wip: zen --- packages/console/app/src/routes/workspace/[id]/model-section.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/console/app/src/routes/workspace/[id]/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx index 964f7dacb..223d69fc8 100644 --- a/packages/console/app/src/routes/workspace/[id]/model-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx @@ -31,6 +31,7 @@ const getModelsInfo = query(async (workspaceID: string) => { return { all: Object.entries(ZenData.list().models) .filter(([id, _model]) => !["claude-3-5-haiku", "minimax-m2"].includes(id)) + .filter(([id, _model]) => !id.startsWith("an-")) .sort(([_idA, modelA], [_idB, modelB]) => modelA.name.localeCompare(modelB.name)) .map(([id, model]) => ({ id, name: model.name })), disabled: await Model.listDisabled(), From 01b9148c04d9812c43843b867c1a9ded8f186a68 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 4 Nov 2025 17:29:27 -0600 Subject: [PATCH 009/218] fix: image reading error, also add error toast for event bus --- packages/opencode/src/cli/cmd/tui/app.tsx | 21 +++++++ packages/opencode/src/session/prompt.ts | 76 +++++++++++++++-------- 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index e57ef8cd6..1fba51cb9 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -367,6 +367,27 @@ function App() { } }) + event.on(SessionApi.Event.Error.type, (evt) => { + const error = evt.properties.error + const message = (() => { + if (!error) return "An error occured" + + if (typeof error === "object") { + const data = error.data + if ("message" in data && typeof data.message === "string") { + return data.message + } + } + return String(error) + })() + + toast.show({ + variant: "error", + message, + duration: 5000, + }) + }) + return ( - t.execute(args, { - sessionID: input.sessionID, - abort: new AbortController().signal, - agent: input.agent!, - messageID: info.id, - extra: { bypassCwdCheck: true }, - metadata: async () => {}, - }), - ) - return [ + + const pieces: MessageV2.Part[] = [ { id: Identifier.ascending("part"), messageID: info.id, @@ -754,21 +746,55 @@ export namespace SessionPrompt { synthetic: true, text: `Called the Read tool with the following input: ${JSON.stringify(args)}`, }, - { - id: Identifier.ascending("part"), - messageID: info.id, - sessionID: input.sessionID, - type: "text", - synthetic: true, - text: result.output, - }, - { - ...part, - id: part.id ?? Identifier.ascending("part"), - messageID: info.id, - sessionID: input.sessionID, - }, ] + + await ReadTool.init() + .then(async (t) => { + const result = await t.execute(args, { + sessionID: input.sessionID, + abort: new AbortController().signal, + agent: input.agent!, + messageID: info.id, + extra: { bypassCwdCheck: true }, + metadata: async () => {}, + }) + pieces.push( + { + id: Identifier.ascending("part"), + messageID: info.id, + sessionID: input.sessionID, + type: "text", + synthetic: true, + text: result.output, + }, + { + ...part, + id: part.id ?? Identifier.ascending("part"), + messageID: info.id, + sessionID: input.sessionID, + }, + ) + }) + .catch((error) => { + log.error("failed to read file", { error }) + const message = error instanceof Error ? error.message : error.toString() + Bus.publish(Session.Event.Error, { + sessionID: input.sessionID, + error: new NamedError.Unknown({ + message, + }).toObject(), + }) + pieces.push({ + id: Identifier.ascending("part"), + messageID: info.id, + sessionID: input.sessionID, + type: "text", + synthetic: true, + text: `Read tool failed to read ${filepath} with the following error: ${message}`, + }) + }) + + return pieces } if (part.mime === "application/x-directory") { From ebca25462edcf535b055e00f3476187646c9f8cb Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 4 Nov 2025 18:45:39 -0500 Subject: [PATCH 010/218] tui: fix session abort when autocomplete is visible --- packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index cf497407a..349026e99 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -161,6 +161,7 @@ export function Prompt(props: PromptProps) { onSelect: (dialog) => { if (!props.sessionID) return if (autocomplete.visible) return + if (!input.focused) return sdk.client.session.abort({ path: { id: props.sessionID, From f51bd91af49fcc3339c11a86a463fc7d7e2489b2 Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 5 Nov 2025 00:12:19 +0000 Subject: [PATCH 011/218] release: v1.0.24 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index e18ad9507..53ed347dc 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -150,7 +150,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -166,7 +166,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.23", + "version": "1.0.24", "bin": { "opencode": "./bin/opencode", }, @@ -243,7 +243,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -263,7 +263,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.23", + "version": "1.0.24", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -274,7 +274,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -287,7 +287,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -317,7 +317,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 7e06d8248..a367a6796 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.23" + "version": "1.0.24" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index c1ab23476..738c41e70 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.23", + "version": "1.0.24", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 0c6e02886..b014e441d 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.23", + "version": "1.0.24", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 30e772b6b..3355d754e 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.23", + "version": "1.0.24", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 341884453..d74dd95b5 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.23", + "version": "1.0.24", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index eeb842ca3..a001b92c5 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.23", + "version": "1.0.24", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 3e4f5c02c..841f972b9 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.23", + "version": "1.0.24", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 0ad4afe63..ec0ca9ac8 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.23", + "version": "1.0.24", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index d4de9ca17..8e4b5b6a3 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.23", + "version": "1.0.24", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index f9392ef37..9de763e60 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.23", + "version": "1.0.24", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 585a249b0..6f5dd1e97 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.23", + "version": "1.0.24", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 2de696b3e..63d2b8e97 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.23", + "version": "1.0.24", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 6a8ed497a..ec4902ea4 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.23", + "version": "1.0.24", "publisher": "sst-dev", "repository": { "type": "git", From 04546c0873b6c379b9a4c92b7301249eea009b0b Mon Sep 17 00:00:00 2001 From: Kyle F Butts Date: Tue, 4 Nov 2025 18:18:33 -0600 Subject: [PATCH 012/218] Add support for `R formatter` in formatters (#3918) Co-authored-by: Aiden Cline --- packages/opencode/src/bun/index.ts | 5 ++- packages/opencode/src/format/formatter.ts | 44 +++++++++++++++++++- packages/opencode/src/format/index.ts | 1 + packages/web/src/content/docs/formatters.mdx | 1 + 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/bun/index.ts b/packages/opencode/src/bun/index.ts index 5f1847275..2a8b48eff 100644 --- a/packages/opencode/src/bun/index.ts +++ b/packages/opencode/src/bun/index.ts @@ -8,7 +8,10 @@ import { readableStreamToText } from "bun" export namespace BunProc { const log = Log.create({ service: "bun" }) - export async function run(cmd: string[], options?: Bun.SpawnOptions.OptionsObject) { + export async function run( + cmd: string[], + options?: Bun.SpawnOptions.OptionsObject, + ) { log.info("running", { cmd: [which(), ...cmd], ...options, diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index baa39906f..a8b8e9de5 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -1,3 +1,4 @@ +import { readableStreamToText } from "bun" import { BunProc } from "../bun" import { Instance } from "../project/instance" import { Filesystem } from "../util/filesystem" @@ -131,7 +132,21 @@ export const zig: Info = { export const clang: Info = { name: "clang-format", command: ["clang-format", "-i", "$FILE"], - extensions: [".c", ".cc", ".cpp", ".cxx", ".c++", ".h", ".hh", ".hpp", ".hxx", ".h++", ".ino", ".C", ".H"], + extensions: [ + ".c", + ".cc", + ".cpp", + ".cxx", + ".c++", + ".h", + ".hh", + ".hpp", + ".hxx", + ".h++", + ".ino", + ".C", + ".H", + ], async enabled() { const items = await Filesystem.findUp(".clang-format", Instance.directory, Instance.worktree) return items.length > 0 @@ -177,6 +192,33 @@ export const ruff: Info = { }, } +export const rlang: Info = { + name: "air", + command: ["air", "format", "$FILE"], + extensions: [".R"], + async enabled() { + const airPath = Bun.which("air") + if (airPath == null) return false + + try { + const proc = Bun.spawn(["air", "--help"], { + stdout: "pipe", + stderr: "pipe", + }) + await proc.exited + const output = await readableStreamToText(proc.stdout) + + // Check for "Air: An R language server and formatter" + const firstLine = output.split("\n")[0] + const hasR = firstLine.includes("R language") + const hasFormatter = firstLine.includes("formatter") + return hasR && hasFormatter + } catch (error) { + return false + } + }, +} + export const uvformat: Info = { name: "uv format", command: ["uv", "format", "--", "$FILE"], diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts index b42940934..e307496c4 100644 --- a/packages/opencode/src/format/index.ts +++ b/packages/opencode/src/format/index.ts @@ -69,6 +69,7 @@ export namespace Format { log.info("checking", { name: item.name, ext }) if (!item.extensions.includes(ext)) continue if (!(await isEnabled(item))) continue + log.info("enabled", { name: item.name, ext }) result.push(item) } return result diff --git a/packages/web/src/content/docs/formatters.mdx b/packages/web/src/content/docs/formatters.mdx index 4b46a4f63..9fc41a53d 100644 --- a/packages/web/src/content/docs/formatters.mdx +++ b/packages/web/src/content/docs/formatters.mdx @@ -25,6 +25,7 @@ OpenCode comes with several built-in formatters for popular languages and framew | rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available | | standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available | | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | +| air | .R | `air` command available | So if your project has `prettier` in your `package.json`, OpenCode will automatically use it. From 234db24f1f995d680d2e33002f13b03b1eacba79 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 4 Nov 2025 20:46:01 -0500 Subject: [PATCH 013/218] tui: fix command validation to prevent invalid commands from being executed --- .../src/cli/cmd/tui/component/prompt/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 349026e99..d66b8e0a3 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -28,6 +28,7 @@ import { useExit } from "../../context/exit" import { Clipboard } from "../../util/clipboard" import type { FilePart } from "@opencode-ai/sdk" import { TuiEvent } from "../../event" +import { iife } from "@/util/iife" export type PromptProps = { sessionID?: string @@ -363,8 +364,15 @@ export function Prompt(props: PromptProps) { }, }) setStore("mode", "normal") - } else if (inputText.startsWith("/")) { - const [command, ...args] = inputText.split(" ") + } else if ( + inputText.startsWith("/") && + iife(() => { + const command = inputText.split(" ")[0].slice(1) + console.log(command) + return sync.data.command.some((x) => x.name === command) + }) + ) { + let [command, ...args] = inputText.split(" ") sdk.client.session.command({ path: { id: sessionID, From 3b1ab444fdbae7f62e5e0d8d226a4a9b63396d5f Mon Sep 17 00:00:00 2001 From: Err Date: Tue, 4 Nov 2025 22:47:05 -0600 Subject: [PATCH 014/218] feat: add Clojure syntax highlighting support (#3912) Co-authored-by: opencode-agent[bot] Co-authored-by: rekram1-node --- packages/opencode/parsers-config.ts | 9 +++++++++ packages/opencode/src/lsp/language.ts | 3 +++ 2 files changed, 12 insertions(+) diff --git a/packages/opencode/parsers-config.ts b/packages/opencode/parsers-config.ts index cfa00454b..ef1495245 100644 --- a/packages/opencode/parsers-config.ts +++ b/packages/opencode/parsers-config.ts @@ -203,5 +203,14 @@ export default { ], }, }, + { + filetype: "clojure", + wasm: "https://github.com/sogaiu/tree-sitter-clojure/releases/download/v0.0.13/tree-sitter-clojure.wasm", + queries: { + highlights: [ + "https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/clojure/highlights.scm", + ], + }, + }, ], } diff --git a/packages/opencode/src/lsp/language.ts b/packages/opencode/src/lsp/language.ts index b7bcd8e91..7980f05e8 100644 --- a/packages/opencode/src/lsp/language.ts +++ b/packages/opencode/src/lsp/language.ts @@ -4,6 +4,9 @@ export const LANGUAGE_EXTENSIONS: Record = { ".bib": "bibtex", ".bibtex": "bibtex", ".clj": "clojure", + ".cljs": "clojure", + ".cljc": "clojure", + ".edn": "clojure", ".coffee": "coffeescript", ".c": "c", ".cpp": "cpp", From b90c0b5facae4b8690e1d1ca3efe8e8e4e7ec5f2 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Tue, 4 Nov 2025 21:02:45 -0800 Subject: [PATCH 015/218] feat(tui): add /export and /copy commands (#3883) Signed-off-by: Christian Stewart --- .../cmd/tui/component/prompt/autocomplete.tsx | 10 ++ .../src/cli/cmd/tui/routes/session/index.tsx | 102 ++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index a4c1f33d9..172ae8a02 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -243,6 +243,16 @@ export function Autocomplete(props: { description: "rename session", onSelect: () => command.trigger("session.rename"), }, + { + display: "/copy", + description: "copy session transcript to clipboard", + onSelect: () => command.trigger("session.copy"), + }, + { + display: "/export", + description: "export session transcript to file", + onSelect: () => command.trigger("session.export"), + }, { display: "/timeline", description: "jump to message", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index ccd12819a..9fa844b7c 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -65,6 +65,9 @@ import parsers from "../../../../../../parsers-config.ts" import { Clipboard } from "../../util/clipboard" import { Toast, useToast } from "../../ui/toast" import { useKV } from "../../context/kv.tsx" +import { Editor } from "../../util/editor" +import { Global } from "@/global" +import fs from "fs/promises" addDefaultParsers(parsers.parsers) @@ -446,6 +449,105 @@ export function Session() { dialog.clear() }, }, + { + title: "Copy session transcript", + value: "session.copy", + keybind: "session_copy", + category: "Session", + onSelect: async (dialog) => { + try { + // Format session transcript as markdown + const sessionData = session() + const sessionMessages = messages() + + let transcript = `# ${sessionData.title}\n\n` + transcript += `**Session ID:** ${sessionData.id}\n` + transcript += `**Created:** ${new Date(sessionData.time.created).toLocaleString()}\n` + transcript += `**Updated:** ${new Date(sessionData.time.updated).toLocaleString()}\n\n` + transcript += `---\n\n` + + for (const msg of sessionMessages) { + const parts = sync.data.part[msg.id] ?? [] + const role = msg.role === "user" ? "User" : "Assistant" + transcript += `## ${role}\n\n` + + for (const part of parts) { + if (part.type === "text" && !part.synthetic) { + transcript += `${part.text}\n\n` + } else if (part.type === "tool") { + transcript += `\`\`\`\nTool: ${part.tool}\n\`\`\`\n\n` + } + } + + transcript += `---\n\n` + } + + // Copy to clipboard + await Clipboard.copy(transcript) + toast.show({ message: "Session transcript copied to clipboard!", variant: "success" }) + } catch (error) { + toast.show({ message: "Failed to copy session transcript", variant: "error" }) + } + dialog.clear() + }, + }, + { + title: "Export session transcript to file", + value: "session.export", + keybind: "session_export", + category: "Session", + onSelect: async (dialog) => { + try { + // Format session transcript as markdown + const sessionData = session() + const sessionMessages = messages() + + let transcript = `# ${sessionData.title}\n\n` + transcript += `**Session ID:** ${sessionData.id}\n` + transcript += `**Created:** ${new Date(sessionData.time.created).toLocaleString()}\n` + transcript += `**Updated:** ${new Date(sessionData.time.updated).toLocaleString()}\n\n` + transcript += `---\n\n` + + for (const msg of sessionMessages) { + const parts = sync.data.part[msg.id] ?? [] + const role = msg.role === "user" ? "User" : "Assistant" + transcript += `## ${role}\n\n` + + for (const part of parts) { + if (part.type === "text" && !part.synthetic) { + transcript += `${part.text}\n\n` + } else if (part.type === "tool") { + transcript += `\`\`\`\nTool: ${part.tool}\n\`\`\`\n\n` + } + } + + transcript += `---\n\n` + } + + // Save to file in data directory + const exportDir = path.join(Global.Path.data, "exports") + await fs.mkdir(exportDir, { recursive: true }) + + const timestamp = new Date().toISOString().replace(/[:.]/g, "-") + const filename = `session-${sessionData.id.slice(0, 8)}-${timestamp}.md` + const filepath = path.join(exportDir, filename) + + await Bun.write(filepath, transcript) + + // Open with EDITOR if available + const result = await Editor.open({ value: transcript, renderer }) + if (result !== undefined) { + // User edited the file, save the changes + await Bun.write(filepath, result) + } + + toast.show({ message: `Session exported to ${filename}`, variant: "success" }) + } catch (error) { + toast.show({ message: "Failed to export session", variant: "error" }) + } + dialog.clear() + }, + }, { title: "Next child session", value: "session.child.next", From 3ebec2435a4c9dd9b5080fbb1d2591a8836305b8 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 4 Nov 2025 23:37:09 -0600 Subject: [PATCH 016/218] allow @ agents to work even if not first thing in prompt --- .../src/cli/cmd/tui/component/prompt/autocomplete.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 172ae8a02..aea10bcaf 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -164,7 +164,6 @@ export function Autocomplete(props: { ) const agents = createMemo(() => { - if (store.index !== 0) return [] const agents = sync.data.agent return agents .filter((agent) => !agent.builtIn && agent.mode !== "primary") @@ -395,7 +394,9 @@ export function Autocomplete(props: { return } // Check if a space was typed after the trigger character - const currentText = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1) + const currentText = props + .input() + .getTextRange(store.index + 1, props.input().cursorOffset + 1) if (currentText.includes(" ")) { hide() } From 1e0596bc46fcd130257174e6691c74484391f905 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:50:48 -0600 Subject: [PATCH 017/218] ACP: update package, fix slash command bug (#3906) --- bun.lock | 4 +- packages/opencode/package.json | 4 +- packages/opencode/src/acp/agent.ts | 86 +++++++++++++++++++--------- packages/opencode/src/acp/session.ts | 7 ++- packages/opencode/src/acp/types.ts | 1 + packages/opencode/src/mcp/index.ts | 20 ++++++- 6 files changed, 87 insertions(+), 35 deletions(-) diff --git a/bun.lock b/bun.lock index 53ed347dc..866ce4472 100644 --- a/bun.lock +++ b/bun.lock @@ -173,7 +173,7 @@ "dependencies": { "@actions/core": "1.11.1", "@actions/github": "6.0.1", - "@agentclientprotocol/sdk": "0.4.9", + "@agentclientprotocol/sdk": "0.5.1", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", @@ -401,7 +401,7 @@ "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], - "@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.4.9", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-ExwH828LaTGoTTjxuw49l+fwOLA+Yx0+qkWn1TcHMOsY5mVI9CUfkj7ZDhv2klgZ7mJeT+lxX/Dn/KINv1AkNQ=="], + "@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="], "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@2.2.10", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-icLGO7Q0NinnHIPgT+y1QjHVwH4HwV+brWbvM+FfCG2Afpa89PyKa3Ret91kGjZpBgM/xnj1B7K5eM+rRlsXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 841f972b9..d9fe076f9 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -43,7 +43,7 @@ "dependencies": { "@actions/core": "1.11.1", "@actions/github": "6.0.1", - "@agentclientprotocol/sdk": "0.4.9", + "@agentclientprotocol/sdk": "0.5.1", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", @@ -57,8 +57,8 @@ "@opentui/core": "0.1.33", "@opentui/solid": "0.1.33", "@parcel/watcher": "2.5.1", - "@solid-primitives/event-bus": "1.1.2", "@pierre/precision-diffs": "catalog:", + "@solid-primitives/event-bus": "1.1.2", "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 1eae36e66..47bac4e57 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -1,9 +1,12 @@ import { + RequestError, type Agent as ACPAgent, type AgentSideConnection, type AuthenticateRequest, + type AuthMethod, type CancelNotification, type InitializeRequest, + type InitializeResponse, type LoadSessionRequest, type NewSessionRequest, type PermissionOption, @@ -33,6 +36,7 @@ import type { Config } from "@/config/config" import { MCP } from "@/mcp" import { Todo } from "@/session/todo" import { z } from "zod" +import { LoadAPIKeyError } from "ai" export namespace ACP { const log = Log.create({ service: "acp-agent" }) @@ -302,9 +306,26 @@ export namespace ACP { }) } - async initialize(params: InitializeRequest) { + async initialize(params: InitializeRequest): Promise { log.info("initialize", { protocolVersion: params.protocolVersion }) + const authMethod: AuthMethod = { + description: "Run `opencode auth login` in the terminal", + name: "Login with opencode", + id: "opencode-login", + } + + // If client supports terminal-auth capability, use that instead. + if (params.clientCapabilities?._meta?.["terminal-auth"] === true) { + authMethod._meta = { + "terminal-auth": { + command: "opencode", + args: ["auth", "login"], + label: "OpenCode Login", + }, + } + } + return { protocolVersion: 1, agentCapabilities: { @@ -325,10 +346,9 @@ export namespace ACP { id: "opencode-login", }, ], - _meta: { - opencode: { - version: Installation.VERSION, - }, + agentInfo: { + name: "OpenCode", + version: Installation.VERSION, }, } } @@ -338,21 +358,31 @@ export namespace ACP { } async newSession(params: NewSessionRequest) { - const model = await defaultModel(this.config) - const session = await this.sessionManager.create(params.cwd, params.mcpServers, model) + try { + const model = await defaultModel(this.config) + const session = await this.sessionManager.create(params.cwd, params.mcpServers, model) - log.info("creating_session", { mcpServers: params.mcpServers.length }) - const load = await this.loadSession({ - cwd: params.cwd, - mcpServers: params.mcpServers, - sessionId: session.id, - }) + log.info("creating_session", { mcpServers: params.mcpServers.length }) + const load = await this.loadSession({ + cwd: params.cwd, + mcpServers: params.mcpServers, + sessionId: session.id, + }) - return { - sessionId: session.id, - models: load.models, - modes: load.modes, - _meta: {}, + return { + sessionId: session.id, + models: load.models, + modes: load.modes, + _meta: {}, + } + } catch (e) { + const error = MessageV2.fromError(e, { + providerID: this.config.defaultModel?.providerID ?? "unknown", + }) + if (LoadAPIKeyError.isInstance(error)) { + throw RequestError.authRequired() + } + throw e } } @@ -387,16 +417,6 @@ export namespace ACP { description: "compact the session", }) - setTimeout(() => { - this.connection.sessionUpdate({ - sessionId, - update: { - sessionUpdate: "available_commands_update", - availableCommands, - }, - }) - }, 0) - const availableModes = (await Agents.list()) .filter((agent) => agent.mode !== "subagent") .map((agent) => ({ @@ -437,6 +457,16 @@ export namespace ACP { }), ) + setTimeout(() => { + this.connection.sessionUpdate({ + sessionId, + update: { + sessionUpdate: "available_commands_update", + availableCommands, + }, + }) + }, 0) + return { sessionId, models: { diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts index 5d45ee283..d3ab73d21 100644 --- a/packages/opencode/src/acp/session.ts +++ b/packages/opencode/src/acp/session.ts @@ -6,13 +6,18 @@ import type { ACPSessionState } from "./types" export class ACPSessionManager { private sessions = new Map() - async create(cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"]): Promise { + async create( + cwd: string, + mcpServers: McpServer[], + model?: ACPSessionState["model"], + ): Promise { const session = await Session.create({ title: `ACP Session ${crypto.randomUUID()}` }) const sessionId = session.id const resolvedModel = model ?? (await Provider.defaultModel()) const state: ACPSessionState = { id: sessionId, + parentId: session.parentID, cwd, mcpServers, createdAt: new Date(), diff --git a/packages/opencode/src/acp/types.ts b/packages/opencode/src/acp/types.ts index 56308cb76..119b335ce 100644 --- a/packages/opencode/src/acp/types.ts +++ b/packages/opencode/src/acp/types.ts @@ -2,6 +2,7 @@ import type { McpServer } from "@agentclientprotocol/sdk" export interface ACPSessionState { id: string + parentId?: string cwd: string mcpServers: McpServer[] createdAt: Date diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index d6de4a59b..fbc887030 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -77,7 +77,15 @@ export namespace MCP { } }, async (state) => { - await Promise.all(Object.values(state.clients).map((client) => client.close())) + await Promise.all( + Object.values(state.clients).map((client) => + client.close().catch((error) => { + log.error("Failed to close MCP client", { + error, + }) + }), + ), + ) }, ) @@ -201,7 +209,15 @@ export namespace MCP { const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch(() => {}) if (!result) { - await mcpClient.close() + await mcpClient.close().catch((error) => { + log.error("Failed to close MCP client", { + error, + }) + }) + status = { + status: "failed", + error: "Failed to get tools", + } return { mcpClient: undefined, status: { From 7269c2316d1165b30fc8778e63320a9fc594176a Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 5 Nov 2025 07:00:07 +0000 Subject: [PATCH 018/218] release: v1.0.25 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index 866ce4472..679f837ec 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -150,7 +150,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -166,7 +166,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.24", + "version": "1.0.25", "bin": { "opencode": "./bin/opencode", }, @@ -243,7 +243,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -263,7 +263,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.24", + "version": "1.0.25", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -274,7 +274,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -287,7 +287,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -317,7 +317,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index a367a6796..63eee203b 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.24" + "version": "1.0.25" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 738c41e70..e3d04c2fc 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.24", + "version": "1.0.25", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index b014e441d..bbef6422d 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.24", + "version": "1.0.25", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 3355d754e..c15f8580a 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.24", + "version": "1.0.25", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index d74dd95b5..8eb2786dc 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.24", + "version": "1.0.25", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index a001b92c5..803911f54 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.24", + "version": "1.0.25", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d9fe076f9..ea3e721d5 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.24", + "version": "1.0.25", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index ec0ca9ac8..5935ff8a2 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.24", + "version": "1.0.25", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 8e4b5b6a3..5aa94bf30 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.24", + "version": "1.0.25", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 9de763e60..0805f4b4a 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.24", + "version": "1.0.25", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 6f5dd1e97..83af3a269 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.24", + "version": "1.0.25", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 63d2b8e97..898482903 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.24", + "version": "1.0.25", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index ec4902ea4..96fc935bb 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.24", + "version": "1.0.25", "publisher": "sst-dev", "repository": { "type": "git", From 2db76fc6dd03b58e372675275dd6d48663c593c5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 5 Nov 2025 12:04:41 +0000 Subject: [PATCH 019/218] ignore: update download stats 2025-11-05 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 27c8d2836..28a0e3da9 100644 --- a/STATS.md +++ b/STATS.md @@ -130,3 +130,4 @@ | 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) | | 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) | | 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) | +| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) | From 03f7f182605d178aa6e3b3533d76eb0a6361940f Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 5 Nov 2025 09:36:38 -0600 Subject: [PATCH 020/218] ci: adjust auto label --- .github/workflows/auto-label-tui.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-label-tui.yml b/.github/workflows/auto-label-tui.yml index 834562e17..73cccf928 100644 --- a/.github/workflows/auto-label-tui.yml +++ b/.github/workflows/auto-label-tui.yml @@ -20,8 +20,8 @@ jobs: const title = issue.title; const description = issue.body || ''; - // Check for web/desktop keywords - const webPattern = /\b(web|desktop)\b/i; + // Check for "opencode web" or desktop keywords + const webPattern = /(opencode web|\bdesktop\b)/i; const isWebRelated = webPattern.test(title) || webPattern.test(description); // Check for version patterns like v1.0.x or 1.0.x From c9dfe6d9649c637729d8d4d1cdb0791105c7c98b Mon Sep 17 00:00:00 2001 From: Matthew Fitzpatrick Date: Wed, 5 Nov 2025 07:47:11 -0800 Subject: [PATCH 021/218] docs: include "limit" example (#3925) Co-authored-by: opencode-agent[bot] Co-authored-by: rekram1-node --- packages/web/src/content/docs/providers.mdx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/web/src/content/docs/providers.mdx b/packages/web/src/content/docs/providers.mdx index 41e6308f7..6409acee1 100644 --- a/packages/web/src/content/docs/providers.mdx +++ b/packages/web/src/content/docs/providers.mdx @@ -482,7 +482,6 @@ To use Google Vertex AI with OpenCode: 4. Run the `/models` command to select a model like _Kimi-K2-Instruct_ or _GLM-4.6_. - --- ### LM Studio @@ -935,9 +934,9 @@ You can use any OpenAI-compatible provider with opencode. Most modern AI provide ##### Example -Here's an example setting the `apiKey` and `headers` options. +Here's an example setting the `apiKey`, `headers`, and model `limit` options. -```json title="opencode.json" {9,11} +```json title="opencode.json" {9,11,17-20} { "$schema": "https://opencode.ai/config.json", "provider": { @@ -953,7 +952,11 @@ Here's an example setting the `apiKey` and `headers` options. }, "models": { "my-model-name": { - "name": "My Model Display Name" + "name": "My Model Display Name", + "limit": { + "context": 200000, + "output": 65536 + } } } } @@ -961,7 +964,14 @@ Here's an example setting the `apiKey` and `headers` options. } ``` -We are setting the `apiKey` using the `env` variable syntax, [learn more](/docs/config#env-vars). +Configuration details: + +- **apiKey**: Set using `env` variable syntax, [learn more](/docs/config#env-vars). +- **headers**: Custom headers sent with each request. +- **limit.context**: Maximum input tokens the model accepts. +- **limit.output**: Maximum tokens the model can generate. + +The `limit` fields allow OpenCode to understand how much context you have left. Standard providers pull these from models.dev automatically. --- From 77c65b18b521aad07aa306952b9e0a4172bf2765 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 5 Nov 2025 10:13:04 -0600 Subject: [PATCH 022/218] tweak: normalize escape keybind --- packages/opencode/src/util/keybind.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/opencode/src/util/keybind.ts b/packages/opencode/src/util/keybind.ts index 96619416f..5beaf9aab 100644 --- a/packages/opencode/src/util/keybind.ts +++ b/packages/opencode/src/util/keybind.ts @@ -64,6 +64,9 @@ export namespace Keybind { case "leader": info.leader = true break + case "esc": + info.name = "escape" + break default: info.name = part break From af7b9e77d1f68b66e4ec3c1dd79efccf6bb53b07 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 5 Nov 2025 10:14:08 -0600 Subject: [PATCH 023/218] fix: eu-west-2 aws bedrock issue --- packages/opencode/src/provider/provider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 3be091df8..2ed072e51 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -118,6 +118,7 @@ export namespace Provider { case "eu": { const regionRequiresPrefix = [ "eu-west-1", + "eu-west-2", "eu-west-3", "eu-north-1", "eu-central-1", From 53998a2fedc55262aca8b049eacbed3d710a46e5 Mon Sep 17 00:00:00 2001 From: Filip <34747899+neriousy@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:26:28 +0100 Subject: [PATCH 024/218] chore: remove unused patch tool from registry (to avoid accidental inclusions of it) (#3938) --- packages/opencode/src/tool/registry.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 4ea70f289..c4d54597d 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -3,7 +3,6 @@ import { EditTool } from "./edit" import { GlobTool } from "./glob" import { GrepTool } from "./grep" import { ListTool } from "./ls" -import { PatchTool } from "./patch" import { ReadTool } from "./read" import { TaskTool } from "./task" import { TodoWriteTool, TodoReadTool } from "./todo" @@ -82,7 +81,6 @@ export namespace ToolRegistry { GlobTool, GrepTool, ListTool, - PatchTool, ReadTool, WriteTool, TodoWriteTool, @@ -113,11 +111,9 @@ export namespace ToolRegistry { agent: Agent.Info, ): Promise> { const result: Record = {} - result["patch"] = false if (agent.permission.edit === "deny") { result["edit"] = false - result["patch"] = false result["write"] = false } if (agent.permission.bash["*"] === "deny" && Object.keys(agent.permission.bash).length === 1) { From ee8b81269bdd40dde0987e7796ae0ebcd8ebc3ab Mon Sep 17 00:00:00 2001 From: monke-yo <94365786+monke-yo@users.noreply.github.com> Date: Wed, 5 Nov 2025 23:01:01 +0530 Subject: [PATCH 025/218] feat: add --attach flag to opencode run (#3889) --- packages/opencode/src/cli/cmd/run.ts | 407 +++++++++++++------------- packages/web/src/content/docs/cli.mdx | 12 + 2 files changed, 222 insertions(+), 197 deletions(-) diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index a979f8baa..39b0a55fd 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -1,21 +1,14 @@ import type { Argv } from "yargs" import path from "path" -import { Bus } from "../../bus" -import { Provider } from "../../provider/provider" -import { Session } from "../../session" import { UI } from "../ui" import { cmd } from "./cmd" import { Flag } from "../../flag/flag" -import { Config } from "../../config/config" import { bootstrap } from "../bootstrap" -import { MessageV2 } from "../../session/message-v2" -import { Identifier } from "../../id/id" -import { Agent } from "../../agent/agent" import { Command } from "../../command" -import { SessionPrompt } from "../../session/prompt" import { EOL } from "os" -import { Permission } from "@/permission" import { select } from "@clack/prompts" +import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk" +import { Server } from "../../server/server" const TOOL: Record = { todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD], @@ -84,11 +77,19 @@ export const RunCommand = cmd({ type: "string", describe: "title for the session (uses truncated prompt if no value provided)", }) + .option("attach", { + type: "string", + describe: "attach to a running opencode server (e.g., http://localhost:4096)", + }) + .option("port", { + type: "number", + describe: "port for the local server (defaults to random port if no value provided)", + }) }, handler: async (args) => { let message = args.message.join(" ") - let fileParts: any[] = [] + const fileParts: any[] = [] if (args.file) { const files = Array.isArray(args.file) ? args.file : [args.file] @@ -124,79 +125,8 @@ export const RunCommand = cmd({ process.exit(1) } - await bootstrap(process.cwd(), async () => { - if (args.command) { - const exists = await Command.get(args.command) - if (!exists) { - UI.error(`Command "${args.command}" not found`) - process.exit(1) - } - } - const session = await (async () => { - if (args.continue) { - const it = Session.list() - try { - for await (const s of it) { - if (s.parentID === undefined) { - return s - } - } - return - } finally { - await it.return() - } - } - - if (args.session) return Session.get(args.session) - - const title = (() => { - if (args.title !== undefined) { - if (args.title === "") { - return message.slice(0, 50) + (message.length > 50 ? "..." : "") - } - return args.title - } - return undefined - })() - - return Session.create({ - title, - }) - })() - - if (!session) { - UI.error("Session not found") - process.exit(1) - } - - const cfg = await Config.get() - if (cfg.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) { - try { - await Session.share(session.id) - UI.println(UI.Style.TEXT_INFO_BOLD + "~ https://opencode.ai/s/" + session.id.slice(-8)) - } catch (error) { - if (error instanceof Error && error.message.includes("disabled")) { - UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) - } else { - throw error - } - } - } - - const agent = await (async () => { - if (args.agent) return Agent.get(args.agent) - const build = Agent.get("build") - if (build) return build - return Agent.list().then((x) => x[0]) - })() - - const { providerID, modelID } = await (async () => { - if (args.model) return Provider.parseModel(args.model) - if (agent.model) return agent.model - return await Provider.defaultModel() - })() - - function printEvent(color: string, type: string, title: string) { + const execute = async (sdk: OpencodeClient, sessionID: string) => { + const printEvent = (color: string, type: string, title: string) => { UI.println( color + `|`, UI.Style.TEXT_NORMAL + UI.Style.TEXT_DIM + ` ${type.padEnd(7, " ")}`, @@ -205,135 +135,218 @@ export const RunCommand = cmd({ ) } - function outputJsonEvent(type: string, data: any) { + const outputJsonEvent = (type: string, data: any) => { if (args.format === "json") { - const jsonEvent = { - type, - timestamp: Date.now(), - sessionID: session?.id, - ...data, - } - process.stdout.write(JSON.stringify(jsonEvent) + EOL) + process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL) return true } return false } - const messageID = Identifier.ascending("message") - - Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => { - if (evt.properties.part.sessionID !== session.id) return - if (evt.properties.part.messageID === messageID) return - const part = evt.properties.part - - if (part.type === "tool" && part.state.status === "completed") { - if (outputJsonEvent("tool_use", { part })) return - const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] - const title = - part.state.title || - (Object.keys(part.state.input).length > 0 - ? JSON.stringify(part.state.input) - : "Unknown") - - printEvent(color, tool, title) - - if (part.tool === "bash" && part.state.output && part.state.output.trim()) { - UI.println() - UI.println(part.state.output) - } - } - - if (part.type === "step-start") { - if (outputJsonEvent("step_start", { part })) return - } - - if (part.type === "step-finish") { - if (outputJsonEvent("step_finish", { part })) return - } - - if (part.type === "text") { - const text = part.text - const isPiped = !process.stdout.isTTY - - if (part.time?.end) { - if (outputJsonEvent("text", { part })) return - if (!isPiped) UI.println() - process.stdout.write((isPiped ? text : UI.markdown(text)) + EOL) - if (!isPiped) UI.println() - } - } - }) - + const events = await sdk.event.subscribe() let errorMsg: string | undefined - Bus.subscribe(Session.Event.Error, async (evt) => { - const { sessionID, error } = evt.properties - if (sessionID !== session.id || !error) return - let err = String(error.name) - if ("data" in error && error.data && "message" in error.data) { - err = error.data.message + const eventProcessor = (async () => { + for await (const event of events.stream) { + if (event.type === "message.part.updated") { + const part = event.properties.part + if (part.sessionID !== sessionID) continue + + if (part.type === "tool" && part.state.status === "completed") { + if (outputJsonEvent("tool_use", { part })) continue + const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] + const title = + part.state.title || + (Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown") + printEvent(color, tool, title) + if (part.tool === "bash" && part.state.output?.trim()) { + UI.println() + UI.println(part.state.output) + } + } + + if (part.type === "step-start") { + if (outputJsonEvent("step_start", { part })) continue + } + + if (part.type === "step-finish") { + if (outputJsonEvent("step_finish", { part })) continue + } + + if (part.type === "text" && part.time?.end) { + if (outputJsonEvent("text", { part })) continue + const isPiped = !process.stdout.isTTY + if (!isPiped) UI.println() + process.stdout.write((isPiped ? part.text : UI.markdown(part.text)) + EOL) + if (!isPiped) UI.println() + } + } + + if (event.type === "session.error") { + const props = event.properties + if (props.sessionID !== sessionID || !props.error) continue + let err = String(props.error.name) + if ("data" in props.error && props.error.data && "message" in props.error.data) { + err = String(props.error.data.message) + } + errorMsg = errorMsg ? errorMsg + EOL + err : err + if (outputJsonEvent("error", { error: props.error })) continue + UI.error(err) + } + + if (event.type === "session.idle" && event.properties.sessionID === sessionID) { + break + } + + if (event.type === "permission.updated") { + const permission = event.properties + if (permission.sessionID !== sessionID) continue + const result = await select({ + message: `Permission required to run: ${permission.title}`, + options: [ + { value: "once", label: "Allow once" }, + { value: "always", label: "Always allow" }, + { value: "reject", label: "Reject" }, + ], + initialValue: "once", + }).catch(() => "reject") + const response = (result.toString().includes("cancel") ? "reject" : result) as + | "once" + | "always" + | "reject" + await sdk.postSessionIdPermissionsPermissionId({ + path: { id: sessionID, permissionID: permission.id }, + body: { response }, + }) + } } - errorMsg = errorMsg ? errorMsg + EOL + err : err + })() - if (outputJsonEvent("error", { error })) return - UI.error(err) - }) - - Bus.subscribe(Permission.Event.Updated, async (evt) => { - const permission = evt.properties - const message = `Permission required to run: ${permission.title}` - - const result = await select({ - message, - options: [ - { value: "once", label: "Allow once" }, - { value: "always", label: "Always allow" }, - { value: "reject", label: "Reject" }, - ], - initialValue: "once", - }).catch(() => "reject") - const response = (result.toString().includes("cancel") ? "reject" : result) as - | "once" - | "always" - | "reject" - - Permission.respond({ - sessionID: session.id, - permissionID: permission.id, - response, - }) - }) - - await (async () => { - if (args.command) { - return await SessionPrompt.command({ - messageID, - sessionID: session.id, - agent: agent.name, - model: providerID + "/" + modelID, + if (args.command) { + await sdk.session.command({ + path: { id: sessionID }, + body: { + agent: args.agent || "build", + model: args.model, command: args.command, arguments: message, - }) - } - return await SessionPrompt.prompt({ - sessionID: session.id, - messageID, - model: { - providerID, - modelID, }, - agent: agent.name, - parts: [ - ...fileParts, - { - id: Identifier.ascending("part"), - type: "text", - text: message, - }, - ], }) - })() + } else { + const modelParam = args.model + ? (() => { + const [providerID, modelID] = args.model.split("/") + return { providerID, modelID } + })() + : undefined + await sdk.session.prompt({ + path: { id: sessionID }, + body: { + agent: args.agent || "build", + model: modelParam, + parts: [...fileParts, { type: "text", text: message }], + }, + }) + } + + await eventProcessor if (errorMsg) process.exit(1) + } + + if (args.attach) { + const sdk = createOpencodeClient({ baseUrl: args.attach }) + + const sessionID = await (async () => { + if (args.continue) { + const result = await sdk.session.list() + return result.data?.find((s) => !s.parentID)?.id + } + if (args.session) return args.session + + const title = + args.title !== undefined + ? args.title === "" + ? message.slice(0, 50) + (message.length > 50 ? "..." : "") + : args.title + : undefined + + const result = await sdk.session.create({ body: title ? { title } : {} }) + return result.data?.id + })() + + if (!sessionID) { + UI.error("Session not found") + process.exit(1) + } + + const cfgResult = await sdk.config.get() + if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { + if (error instanceof Error && error.message.includes("disabled")) { + UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) + } + return { error } + }) + if (!shareResult.error) { + UI.println(UI.Style.TEXT_INFO_BOLD + "~ https://opencode.ai/s/" + sessionID.slice(-8)) + } + } + + return await execute(sdk, sessionID) + } + + await bootstrap(process.cwd(), async () => { + const server = Server.listen({ port: args.port ?? 0, hostname: "127.0.0.1" }) + const sdk = createOpencodeClient({ baseUrl: `http://${server.hostname}:${server.port}` }) + + if (args.command) { + const exists = await Command.get(args.command) + if (!exists) { + server.stop() + UI.error(`Command "${args.command}" not found`) + process.exit(1) + } + } + + const sessionID = await (async () => { + if (args.continue) { + const result = await sdk.session.list() + return result.data?.find((s) => !s.parentID)?.id + } + if (args.session) return args.session + + const title = + args.title !== undefined + ? args.title === "" + ? message.slice(0, 50) + (message.length > 50 ? "..." : "") + : args.title + : undefined + + const result = await sdk.session.create({ body: title ? { title } : {} }) + return result.data?.id + })() + + if (!sessionID) { + server.stop() + UI.error("Session not found") + process.exit(1) + } + + const cfgResult = await sdk.config.get() + if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { + if (error instanceof Error && error.message.includes("disabled")) { + UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) + } + return { error } + }) + if (!shareResult.error) { + UI.println(UI.Style.TEXT_INFO_BOLD + "~ https://opencode.ai/s/" + sessionID.slice(-8)) + } + } + + await execute(sdk, sessionID) + server.stop() }) }, }) diff --git a/packages/web/src/content/docs/cli.mdx b/packages/web/src/content/docs/cli.mdx index 1312442d6..083db369b 100644 --- a/packages/web/src/content/docs/cli.mdx +++ b/packages/web/src/content/docs/cli.mdx @@ -184,6 +184,16 @@ This is useful for scripting, automation, or when you want a quick answer withou opencode run Explain the use of context in Go ``` +You can also attach to a running `opencode serve` instance to avoid MCP server cold boot times on every run: + +```bash +# Start a headless server in one terminal +opencode serve + +# In another terminal, run commands that attach to it +opencode run --attach http://localhost:4096 "Explain async/await in JavaScript" +``` + #### Flags | Flag | Short | Description | @@ -197,6 +207,8 @@ opencode run Explain the use of context in Go | `--file` | `-f` | File(s) to attach to message | | `--format` | | Format: default (formatted) or json (raw JSON events) | | `--title` | | Title for the session (uses truncated prompt if no value provided) | +| `--attach` | | Attach to a running opencode server (e.g., http://localhost:4096) | +| `--port` | | Port for the local server (defaults to random port) | --- From 37e564139fc1b46a66f916280a3baf3e3e399bf2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 12:32:13 -0500 Subject: [PATCH 026/218] tui: lower paste summary threshold to trigger on shorter content --- packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index d66b8e0a3..c3ae1407d 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -650,7 +650,10 @@ export function Prompt(props: PromptProps) { } catch {} const lineCount = (pastedContent.match(/\n/g)?.length ?? 0) + 1 - if (lineCount >= 5 && !sync.data.config.experimental?.disable_paste_summary) { + if ( + (lineCount >= 3 || pastedContent.length > 150) && + !sync.data.config.experimental?.disable_paste_summary + ) { event.preventDefault() const currentOffset = input.visualCursor.offset const virtualText = `[Pasted ~${lineCount} lines]` From 69a499f80786b2656121cee4469f39df2e6c40e7 Mon Sep 17 00:00:00 2001 From: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:33:30 +0100 Subject: [PATCH 027/218] fix(tui): restructure Sidebar component to be scrollable (#3946) --- .../cli/cmd/tui/routes/session/sidebar.tsx | 258 +++++++++--------- 1 file changed, 130 insertions(+), 128 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index c63297db2..2f1451a5f 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -40,136 +40,138 @@ export function Sidebar(props: { sessionID: string }) { return ( - - - - {session().title} - - - {session().share!.url} + + + + + {session().title} + + + {session().share!.url} + + + + + Context + + {context()?.tokens ?? 0} tokens + {context()?.percentage ?? 0}% used + {cost()} spent + + 0}> + + + MCP + + + {([key, item]) => ( + + + • + + + {key}{" "} + + + Connected + + {(val) => {val().error}} + + Disabled in configuration + + + + + )} + + + + 0}> + + + LSP + + + {(item) => ( + + + • + + + {item.id} {item.root} + + + )} + + + + + + + Modified Files + + + {(item) => { + const file = createMemo(() => { + const splits = item.file.split(path.sep).filter(Boolean) + const last = splits.at(-1)! + const rest = splits.slice(0, -1).join(path.sep) + return Locale.truncateMiddle(rest, 30 - last.length) + "/" + last + }) + return ( + + + {file()} + + + + +{item.additions} + + + -{item.deletions} + + + + ) + }} + + + + 0}> + + + Todo + + + {(todo) => ( + + [{todo.status === "completed" ? "✓" : " "}] {todo.content} + + )} + + - - - Context - - {context()?.tokens ?? 0} tokens - {context()?.percentage ?? 0}% used - {cost()} spent - - 0}> - - - MCP - - - {([key, item]) => ( - - - • - - - {key}{" "} - - - Connected - - {(val) => {val().error}} - - Disabled in configuration - - - - - )} - - - - 0}> - - - LSP - - - {(item) => ( - - - • - - - {item.id} {item.root} - - - )} - - - - - - - Modified Files - - - {(item) => { - const file = createMemo(() => { - const splits = item.file.split(path.sep).filter(Boolean) - const last = splits.at(-1)! - const rest = splits.slice(0, -1).join(path.sep) - return Locale.truncateMiddle(rest, 30 - last.length) + "/" + last - }) - return ( - - - {file()} - - - - +{item.additions} - - - -{item.deletions} - - - - ) - }} - - - - 0}> - - - Todo - - - {(todo) => ( - - [{todo.status === "completed" ? "✓" : " "}] {todo.content} - - )} - - - - + ) } From d525fbf82940442d8d47265b7d7d0a9af8c282bc Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:55:31 -0600 Subject: [PATCH 028/218] feat(desktop): session router, interrupt agent, visual cleanup --- bun.lock | 27 +- package.json | 2 +- packages/desktop/package.json | 1 + .../src/components/message-progress.tsx | 11 +- .../desktop/src/components/prompt-input.tsx | 223 +++-- packages/desktop/src/context/local.tsx | 176 +--- packages/desktop/src/context/session.tsx | 213 +++++ packages/desktop/src/context/sync.tsx | 14 +- packages/desktop/src/index.tsx | 10 +- packages/desktop/src/pages/index.tsx | 857 ------------------ packages/desktop/src/pages/layout.tsx | 75 ++ packages/desktop/src/pages/session-layout.tsx | 12 + packages/desktop/src/pages/session.tsx | 693 ++++++++++++++ packages/ui/src/components/code.tsx | 5 +- packages/ui/src/components/diff.tsx | 1 + packages/ui/src/components/icon.tsx | 1 + packages/ui/src/components/input.tsx | 23 +- packages/ui/src/components/list.tsx | 8 +- packages/ui/src/components/select-dialog.tsx | 22 +- packages/ui/src/components/select.css | 12 +- packages/ui/src/components/select.tsx | 18 +- 21 files changed, 1259 insertions(+), 1145 deletions(-) create mode 100644 packages/desktop/src/context/session.tsx delete mode 100644 packages/desktop/src/pages/index.tsx create mode 100644 packages/desktop/src/pages/layout.tsx create mode 100644 packages/desktop/src/pages/session-layout.tsx create mode 100644 packages/desktop/src/pages/session.tsx diff --git a/bun.lock b/bun.lock index 679f837ec..af3e4f2d4 100644 --- a/bun.lock +++ b/bun.lock @@ -121,6 +121,7 @@ "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", + "@solid-primitives/storage": "4.3.3", "@solidjs/meta": "catalog:", "@solidjs/router": "0.15.3", "@thisbeyond/solid-dnd": "0.7.5", @@ -364,7 +365,7 @@ "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.4.1", + "@pierre/precision-diffs": "0.4.2", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "@tsconfig/bun": "1.0.9", @@ -1033,7 +1034,7 @@ "@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="], - "@pierre/precision-diffs": ["@pierre/precision-diffs@0.4.1", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-AoozHakINGyNJFgbYc/1PlDK0yunrAxbtXEMBe9fdu8RLkNjVtYRTLw7EF2mM/YuVoVRjj2HT/2VJ4a2rMyDOA=="], + "@pierre/precision-diffs": ["@pierre/precision-diffs@0.4.2", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/transformers": "3.14.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.14.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-C6LbruH24BCp4awI47D5iMtVaZZD6GkzqBoDw+Sfu7DB3hC9y/rZr1C2BD7AUzAKwByTfFnh16Zp11ipfPqLKw=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], @@ -1309,6 +1310,8 @@ "@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw=="], + "@solid-primitives/storage": ["@solid-primitives/storage@4.3.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "@tauri-apps/plugin-store": "*", "solid-js": "^1.6.12" }, "optionalPeers": ["@tauri-apps/plugin-store"] }, "sha512-ACbNwMZ1s8VAvld6EUXkDkX/US3IhtlPLxg6+B2s9MwNUugwdd51I98LPEaHrdLpqPmyzqgoJe0TxEFlf3Dqrw=="], + "@solid-primitives/trigger": ["@solid-primitives/trigger@1.2.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-IWoptVc0SWYgmpBPpCMehS5b07+tpFcvw15tOQ3QbXedSYn6KP8zCjPkHNzMxcOvOicTneleeZDP7lqmz+PQ6g=="], "@solid-primitives/utils": ["@solid-primitives/utils@6.3.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ=="], @@ -3725,11 +3728,11 @@ "@parcel/watcher-wasm/napi-wasm": ["napi-wasm@1.1.3", "", { "bundled": true }, "sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg=="], - "@pierre/precision-diffs/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="], + "@pierre/precision-diffs/@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], - "@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="], + "@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/types": "3.14.0" } }, "sha512-i67zQnY9wLMMnKasonVW1L9fKneSLZDj1ePsA4o0AZWU4uUobmJY9baRDa36z+a9/g0aG76/2tybQvm4hrwxIQ=="], - "@pierre/precision-diffs/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="], + "@pierre/precision-diffs/shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], @@ -4327,19 +4330,19 @@ "@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@pierre/precision-diffs/@shikijs/core/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], + "@pierre/precision-diffs/@shikijs/core/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], - "@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], + "@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], - "@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="], + "@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], - "@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="], + "@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], - "@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="], + "@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], - "@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="], + "@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], - "@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], + "@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], "@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], diff --git a/package.json b/package.json index 551258967..de6c62a5f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@tsconfig/bun": "1.0.9", "@cloudflare/workers-types": "4.20251008.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.4.1", + "@pierre/precision-diffs": "0.4.2", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "diff": "8.0.2", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 8eb2786dc..094e71b05 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -31,6 +31,7 @@ "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", + "@solid-primitives/storage": "4.3.3", "@solidjs/meta": "catalog:", "@solidjs/router": "0.15.3", "@thisbeyond/solid-dnd": "0.7.5", diff --git a/packages/desktop/src/components/message-progress.tsx b/packages/desktop/src/components/message-progress.tsx index c0037f57c..a9be2ae5e 100644 --- a/packages/desktop/src/components/message-progress.tsx +++ b/packages/desktop/src/components/message-progress.tsx @@ -70,9 +70,8 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa const lastPart = createMemo(() => resolvedParts().slice(-1)?.at(0)) const rawStatus = createMemo(() => { - const defaultStatus = "Working..." const last = lastPart() - if (!last) return defaultStatus + if (!last) return undefined if (last.type === "tool") { switch (last.tool) { @@ -102,7 +101,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa } else if (last.type === "text") { return "Gathering thoughts..." } - return defaultStatus + return undefined }) const [status, setStatus] = createSignal(rawStatus()) @@ -111,11 +110,11 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa createEffect(() => { const newStatus = rawStatus() - if (newStatus === status()) return + if (newStatus === status() || !newStatus) return const timeSinceLastChange = Date.now() - lastStatusChange - if (timeSinceLastChange >= 1000) { + if (timeSinceLastChange >= 1500) { setStatus(newStatus) lastStatusChange = Date.now() if (statusTimeout) { @@ -145,7 +144,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa {/* )} */} {/* */}

- {status()} + {status() ?? "Considering next steps..."}
0}>
void class?: string ref?: (el: HTMLDivElement) => void } export const PromptInput: Component = (props) => { + const navigate = useNavigate() + const sdk = useSDK() + const sync = useSync() const local = useLocal() + const session = useSession() let editorRef!: HTMLDivElement - const defaultParts = [{ type: "text", content: "", start: 0, end: 0 } as const] const [store, setStore] = createStore<{ - contentParts: ContentPart[] popoverIsOpen: boolean }>({ - contentParts: defaultParts, popoverIsOpen: false, }) - const isEmpty = createMemo(() => isEqual(store.contentParts, defaultParts)) + createEffect(() => { + session.id + editorRef.focus() + }) + const isFocused = createFocusSignal(() => editorRef) const handlePaste = (event: ClipboardEvent) => { @@ -71,14 +61,16 @@ export const PromptInput: Component = (props) => { } }) + const handleFileSelect = (path: string | undefined) => { + if (!path) return + addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 }) + setStore("popoverIsOpen", false) + } + const { flat, active, onInput, onKeyDown, refetch } = useFilteredList({ items: local.file.searchFilesAndDirectories, key: (x) => x, - onSelect: (path) => { - if (!path) return - addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 }) - setStore("popoverIsOpen", false) - }, + onSelect: handleFileSelect, }) createEffect(() => { @@ -88,10 +80,10 @@ export const PromptInput: Component = (props) => { createEffect( on( - () => store.contentParts, + () => session.prompt.current(), (currentParts) => { const domParts = parseFromDOM() - if (isEqual(currentParts, domParts)) return + if (isPromptEqual(currentParts, domParts)) return const selection = window.getSelection() let cursorPosition: number | null = null @@ -122,8 +114,18 @@ export const PromptInput: Component = (props) => { ), ) - const parseFromDOM = (): ContentPart[] => { - const newParts: ContentPart[] = [] + createEffect( + on( + () => session.prompt.cursor(), + (cursor) => { + if (cursor === undefined) return + queueMicrotask(() => setCursorPosition(editorRef, cursor)) + }, + ), + ) + + const parseFromDOM = (): Prompt => { + const newParts: Prompt = [] let position = 0 editorRef.childNodes.forEach((node) => { if (node.nodeType === Node.TEXT_NODE) { @@ -150,7 +152,7 @@ export const PromptInput: Component = (props) => { } } }) - if (newParts.length === 0) newParts.push(...defaultParts) + if (newParts.length === 0) newParts.push(...DEFAULT_PROMPT) return newParts } @@ -167,12 +169,15 @@ export const PromptInput: Component = (props) => { setStore("popoverIsOpen", false) } - setStore("contentParts", rawParts) + session.prompt.set(rawParts, cursorPosition) } const addPart = (part: ContentPart) => { const cursorPosition = getCursorPosition(editorRef) - const rawText = store.contentParts.map((p) => p.content).join("") + const rawText = session.prompt + .current() + .map((p) => p.content) + .join("") const textBeforeCursor = rawText.substring(0, cursorPosition) const atMatch = textBeforeCursor.match(/@(\S*)$/) @@ -198,7 +203,7 @@ export const PromptInput: Component = (props) => { parts: nextParts, inserted, cursorPositionAfter, - } = store.contentParts.reduce( + } = session.prompt.current().reduce( (acc, item) => { if (acc.inserted) { acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length }) @@ -257,7 +262,7 @@ export const PromptInput: Component = (props) => { ) if (!inserted) { - const baseParts = store.contentParts.filter((item) => !(item.type === "text" && item.content === "")) + const baseParts = session.prompt.current().filter((item) => !(item.type === "text" && item.content === "")) const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0) const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex } if (part.type === "text") { @@ -270,20 +275,27 @@ export const PromptInput: Component = (props) => { end: appendedAcc.runningIndex + part.content.length, }) } - const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : defaultParts - setStore("contentParts", next) - setStore("popoverIsOpen", false) + const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : DEFAULT_PROMPT const nextCursor = rawText.length + part.content.length + session.prompt.set(next, nextCursor) + setStore("popoverIsOpen", false) queueMicrotask(() => setCursorPosition(editorRef, nextCursor)) return } - setStore("contentParts", nextParts) + session.prompt.set(nextParts, cursorPositionAfter) setStore("popoverIsOpen", false) queueMicrotask(() => setCursorPosition(editorRef, cursorPositionAfter)) } + const abort = () => + sdk.client.session.abort({ + path: { + id: session.id!, + }, + }) + const handleKeyDown = (event: KeyboardEvent) => { if (store.popoverIsOpen && (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter")) { onKeyDown(event) @@ -293,14 +305,101 @@ export const PromptInput: Component = (props) => { if (event.key === "Enter" && !event.shiftKey) { handleSubmit(event) } + if (event.key === "Escape") { + if (store.popoverIsOpen) { + setStore("popoverIsOpen", false) + } else if (session.working()) { + abort() + } + } } - const handleSubmit = (event: Event) => { + const handleSubmit = async (event: Event) => { event.preventDefault() - if (store.contentParts.length > 0) { - props.onSubmit([...store.contentParts]) - setStore("contentParts", defaultParts) + const text = session.prompt + .current() + .map((part) => part.content) + .join("") + if (text.trim().length === 0) { + if (session.working()) abort() + return } + + let existing = session.info() + if (!existing) { + const created = await sdk.client.session.create() + existing = created.data ?? undefined + } + if (!existing) return + + navigate(`/session/${existing.id}`) + if (!session.id) { + // session.layout.setOpenedTabs( + // session.layout.copyTabs("", session.id) + } + session.layout.setActiveTab(undefined) + const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + + const attachments = session.prompt.current().filter((part) => part.type === "file") + + // const activeFile = local.context.active() + // if (activeFile) { + // registerAttachment( + // activeFile.path, + // activeFile.selection, + // activeFile.name ?? formatAttachmentLabel(activeFile.path, activeFile.selection), + // ) + // } + + // for (const contextFile of local.context.all()) { + // registerAttachment( + // contextFile.path, + // contextFile.selection, + // formatAttachmentLabel(contextFile.path, contextFile.selection), + // ) + // } + + const attachmentParts = attachments.map((attachment) => { + const absolute = toAbsolutePath(attachment.path) + const query = attachment.selection + ? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}` + : "" + return { + type: "file" as const, + mime: "text/plain", + url: `file://${absolute}${query}`, + filename: getFilename(attachment.path), + source: { + type: "file" as const, + text: { + value: attachment.content, + start: attachment.start, + end: attachment.end, + }, + path: absolute, + }, + } + }) + + session.prompt.set(DEFAULT_PROMPT, 0) + + await sdk.client.session.prompt({ + path: { id: existing.id }, + body: { + agent: local.agent.current()!.name, + model: { + modelID: local.model.current()!.id, + providerID: local.model.current()!.provider.id, + }, + parts: [ + { + type: "text", + text, + }, + ...attachmentParts, + ], + }, + }) } return ( @@ -310,11 +409,12 @@ export const PromptInput: Component = (props) => { 0} fallback={
No matching files
}> {(i) => ( -
handleFileSelect(i)} >
@@ -326,7 +426,7 @@ export const PromptInput: Component = (props) => {
-
+ )}
@@ -354,7 +454,7 @@ export const PromptInput: Component = (props) => { "[&>[data-type=file]]:text-icon-info-active": true, }} /> - +
Plan and build anything
@@ -419,29 +519,18 @@ export const PromptInput: Component = (props) => { )}
- + ) } -function isEqual(arrA: ContentPart[], arrB: ContentPart[]): boolean { - if (arrA.length !== arrB.length) return false - for (let i = 0; i < arrA.length; i++) { - const partA = arrA[i] - const partB = arrB[i] - if (partA.type !== partB.type) return false - if (partA.type === "text" && partA.content !== (partB as TextPart).content) { - return false - } - if (partA.type === "file" && partA.path !== (partB as FileAttachmentPart).path) { - return false - } - } - return true -} - function getCursorPosition(parent: HTMLElement): number { const selection = window.getSelection() if (!selection || selection.rangeCount === 0) return 0 diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index 8fc05c452..cef6c5555 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -195,18 +195,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const file = (() => { const [store, setStore] = createStore<{ node: Record - // opened: string[] - // active?: string }>({ node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])), - // opened: [], }) - // const active = createMemo(() => { - // if (!store.active) return undefined - // return store.node[store.active] - // }) - // const opened = createMemo(() => store.opened.map((x) => store.node[x])) const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path))) const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b))) @@ -247,18 +239,18 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ return false } - const resetNode = (path: string) => { - setStore("node", path, { - loaded: undefined, - pinned: undefined, - content: undefined, - selection: undefined, - scrollTop: undefined, - folded: undefined, - view: undefined, - selectedChange: undefined, - }) - } + // const resetNode = (path: string) => { + // setStore("node", path, { + // loaded: undefined, + // pinned: undefined, + // content: undefined, + // selection: undefined, + // scrollTop: undefined, + // folded: undefined, + // view: undefined, + // selectedChange: undefined, + // }) + // } const relative = (path: string) => path.replace(sync.data.path.directory + "/", "") @@ -333,31 +325,21 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ sdk.event.listen((e) => { const event = e.details switch (event.type) { - case "message.part.updated": - const part = event.properties.part - if (part.type === "tool" && part.state.status === "completed") { - switch (part.tool) { - case "read": - break - case "edit": - // load(part.state.input["filePath"] as string) - break - default: - break - } - } - break case "file.watcher.updated": - // setTimeout(sync.load.changes, 1000) - // const relativePath = relative(event.properties.file) - // if (relativePath.startsWith(".git/")) return - // load(relativePath) + const relativePath = relative(event.properties.file) + if (relativePath.startsWith(".git/")) return + load(relativePath) break } }) return { - node: (path: string) => store.node[path], + node: async (path: string) => { + if (!store.node[path]) { + await init(path) + } + return store.node[path] + }, update: (path: string, node: LocalFile) => setStore("node", path, reconcile(node)), open, load, @@ -417,121 +399,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ searchFiles, searchFilesAndDirectories, relative, - // active, - // opened, - // close(path: string) { - // setStore("opened", (opened) => opened.filter((x) => x !== path)) - // if (store.active === path) { - // const index = store.opened.findIndex((f) => f === path) - // const previous = store.opened[Math.max(0, index - 1)] - // setStore("active", previous) - // } - // resetNode(path) - // }, - // move(path: string, to: number) { - // const index = store.opened.findIndex((f) => f === path) - // if (index === -1) return - // setStore( - // "opened", - // produce((opened) => { - // opened.splice(to, 0, opened.splice(index, 1)[0]) - // }), - // ) - // setStore("node", path, "pinned", true) - // }, - } - })() - - const session = (() => { - const [store, setStore] = createStore<{ - active?: string - tabs: Record< - string, - { - active?: string - opened: string[] - } - > - }>({ - tabs: { - "": { - opened: [], - }, - }, - }) - - const active = createMemo(() => { - if (!store.active) return undefined - return sync.session.get(store.active) - }) - - createEffect(() => { - if (!store.active) return - sync.session.sync(store.active) - - if (!store.tabs[store.active]) { - setStore("tabs", store.active, { - opened: [], - }) - } - }) - - const tabs = createMemo(() => store.tabs[store.active ?? ""]) - - return { - active, - setActive(sessionId: string | undefined) { - setStore("active", sessionId) - }, - clearActive() { - setStore("active", undefined) - }, - tabs, - copyTabs(from: string, to: string) { - setStore("tabs", to, { - opened: store.tabs[from]?.opened ?? [], - }) - }, - setActiveTab(tab: string | undefined) { - setStore("tabs", store.active ?? "", "active", tab) - }, - async open(tab: string) { - if (tab !== "chat") { - await file.open(tab) - } - if (!tabs()?.opened?.includes(tab)) { - setStore("tabs", store.active ?? "", "opened", [...(tabs()?.opened ?? []), tab]) - } - setStore("tabs", store.active ?? "", "active", tab) - }, - close(tab: string) { - batch(() => { - if (!tabs()) return - setStore("tabs", store.active ?? "", { - active: tabs()!.active, - opened: tabs()!.opened.filter((x) => x !== tab), - }) - if (tabs()!.active === tab) { - const index = tabs()!.opened.findIndex((f) => f === tab) - const previous = tabs()!.opened[Math.max(0, index - 1)] - setStore("tabs", store.active ?? "", "active", previous) - } - }) - }, - move(tab: string, to: number) { - if (!tabs()) return - const index = tabs()!.opened.findIndex((f) => f === tab) - if (index === -1) return - setStore( - "tabs", - store.active ?? "", - "opened", - produce((opened) => { - opened.splice(to, 0, opened.splice(index, 1)[0]) - }), - ) - // setStore("node", path, "pinned", true) - }, } })() @@ -593,7 +460,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ model, agent, file, - session, context, } return result diff --git a/packages/desktop/src/context/session.tsx b/packages/desktop/src/context/session.tsx new file mode 100644 index 000000000..77ab3bc25 --- /dev/null +++ b/packages/desktop/src/context/session.tsx @@ -0,0 +1,213 @@ +import { createStore, produce } from "solid-js/store" +import { createSimpleContext } from "./helper" +import { batch, createEffect, createMemo } from "solid-js" +import { useSync } from "./sync" +import { makePersisted } from "@solid-primitives/storage" +import { TextSelection, useLocal } from "./local" +import { pipe, sumBy } from "remeda" +import { AssistantMessage } from "@opencode-ai/sdk" + +export const { use: useSession, provider: SessionProvider } = createSimpleContext({ + name: "Session", + init: (props: { sessionId?: string }) => { + const sync = useSync() + const local = useLocal() + + const [store, setStore] = makePersisted( + createStore<{ + prompt: Prompt + cursorPosition?: number + messageId?: string + tabs: { + active?: string + opened: string[] + } + }>({ + prompt: [{ type: "text", content: "", start: 0, end: 0 }], + tabs: { + opened: [], + }, + }), + { + name: props.sessionId ?? "new-session", + }, + ) + + createEffect(() => { + if (!props.sessionId) return + sync.session.sync(props.sessionId) + }) + + const info = createMemo(() => (props.sessionId ? sync.session.get(props.sessionId) : undefined)) + const messages = createMemo(() => (props.sessionId ? (sync.data.message[props.sessionId] ?? []) : [])) + const userMessages = createMemo(() => + messages() + .filter((m) => m.role === "user") + .sort((a, b) => b.id.localeCompare(a.id)), + ) + const lastUserMessage = createMemo(() => { + return userMessages()?.at(0) + }) + const activeMessage = createMemo(() => { + if (!store.messageId) return lastUserMessage() + return userMessages()?.find((m) => m.id === store.messageId) + }) + const working = createMemo(() => { + if (!props.sessionId) return false + const last = lastUserMessage() + if (!last) return false + const assistantMessages = sync.data.message[props.sessionId]?.filter( + (m) => m.role === "assistant" && m.parentID == last?.id, + ) as AssistantMessage[] + const error = assistantMessages?.find((m) => m?.error)?.error + return !last?.summary?.body && !error + }) + + const cost = createMemo(() => { + const total = pipe( + messages(), + sumBy((x) => (x.role === "assistant" ? x.cost : 0)), + ) + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(total) + }) + + const last = createMemo( + () => messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage, + ) + const model = createMemo(() => + last() ? sync.data.provider.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined, + ) + + const tokens = createMemo(() => { + if (!last()) return + const tokens = last().tokens + return tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write + }) + + const context = createMemo(() => { + const total = tokens() + const limit = model()?.limit.context + if (!total || !limit) return 0 + return Math.round((total / limit) * 100) + }) + + return { + id: props.sessionId, + info, + working, + prompt: { + current: createMemo(() => store.prompt), + cursor: createMemo(() => store.cursorPosition), + dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)), + set(prompt: Prompt, cursorPosition?: number) { + batch(() => { + setStore("prompt", prompt) + if (cursorPosition !== undefined) setStore("cursorPosition", cursorPosition) + }) + }, + }, + messages: { + all: messages, + user: userMessages, + last: lastUserMessage, + active: activeMessage, + setActive(id: string | undefined) { + setStore("messageId", id) + }, + }, + usage: { + tokens, + cost, + context, + }, + layout: { + tabs: store.tabs, + setActiveTab(tab: string | undefined) { + setStore("tabs", "active", tab) + }, + setOpenedTabs(tabs: string[]) { + setStore("tabs", "opened", tabs) + }, + async openTab(tab: string) { + if (tab === "chat") { + setStore("tabs", "active", undefined) + return + } + if (tab.startsWith("file://")) { + await local.file.open(tab.replace("file://", "")) + } + if (!store.tabs.opened.includes(tab)) { + setStore("tabs", "opened", [...store.tabs.opened, tab]) + } + setStore("tabs", "active", tab) + }, + closeTab(tab: string) { + batch(() => { + setStore( + "tabs", + "opened", + store.tabs.opened.filter((x) => x !== tab), + ) + if (store.tabs.active === tab) { + const index = store.tabs.opened.findIndex((f) => f === tab) + const previous = store.tabs.opened[Math.max(0, index - 1)] + setStore("tabs", "active", previous) + } + }) + }, + moveTab(tab: string, to: number) { + const index = store.tabs.opened.findIndex((f) => f === tab) + if (index === -1) return + setStore( + "tabs", + "opened", + produce((opened) => { + opened.splice(to, 0, opened.splice(index, 1)[0]) + }), + ) + // setStore("node", path, "pinned", true) + }, + }, + } + }, +}) + +interface PartBase { + content: string + start: number + end: number +} + +export interface TextPart extends PartBase { + type: "text" +} + +export interface FileAttachmentPart extends PartBase { + type: "file" + path: string + selection?: TextSelection +} + +export type ContentPart = TextPart | FileAttachmentPart +export type Prompt = ContentPart[] + +export const DEFAULT_PROMPT: Prompt = [{ type: "text", content: "", start: 0, end: 0 }] + +export function isPromptEqual(promptA: Prompt, promptB: Prompt): boolean { + if (promptA.length !== promptB.length) return false + for (let i = 0; i < promptA.length; i++) { + const partA = promptA[i] + const partB = promptB[i] + if (partA.type !== partB.type) return false + if (partA.type === "text" && partA.content !== (partB as TextPart).content) { + return false + } + if (partA.type === "file" && partA.path !== (partB as FileAttachmentPart).path) { + return false + } + } + return true +} diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index 06fc05677..c60206b0b 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -1,16 +1,4 @@ -import type { - Message, - Agent, - Provider, - Session, - Part, - Config, - Path, - File, - FileNode, - Project, - Command, -} from "@opencode-ai/sdk" +import type { Message, Agent, Provider, Session, Part, Config, Path, File, FileNode, Project } from "@opencode-ai/sdk" import { createStore, produce, reconcile } from "solid-js/store" import { createMemo } from "solid-js" import { Binary } from "@/utils/binary" diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 9fe5da2f6..de9994af6 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -7,7 +7,9 @@ import { Fonts, ShikiProvider, MarkedProvider } from "@opencode-ai/ui" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" import { LocalProvider } from "./context/local" -import Home from "@/pages" +import Layout from "@/pages/layout" +import SessionLayout from "@/pages/session-layout" +import Session from "@/pages/session" const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" @@ -32,8 +34,10 @@ render( - - + + + + diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx deleted file mode 100644 index 5f04c3dbe..000000000 --- a/packages/desktop/src/pages/index.tsx +++ /dev/null @@ -1,857 +0,0 @@ -import { - Button, - List, - SelectDialog, - Tooltip, - IconButton, - Tabs, - Icon, - Accordion, - Diff, - Collapsible, - DiffChanges, - Message, - Typewriter, - Card, - Code, -} from "@opencode-ai/ui" -import { FileIcon } from "@/ui" -import FileTree from "@/components/file-tree" -import { MessageProgress } from "@/components/message-progress" -import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js" -import { useLocal, type LocalFile } from "@/context/local" -import { createStore } from "solid-js/store" -import { getDirectory, getFilename } from "@/utils" -import { ContentPart, PromptInput } from "@/components/prompt-input" -import { DateTime } from "luxon" -import { - DragDropProvider, - DragDropSensors, - DragOverlay, - SortableProvider, - closestCenter, - createSortable, - useDragDropContext, -} from "@thisbeyond/solid-dnd" -import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" -import type { JSX } from "solid-js" -import { useSync } from "@/context/sync" -import { useSDK } from "@/context/sdk" -import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" -import { Markdown } from "@opencode-ai/ui" -import { Spinner } from "@/components/spinner" - -export default function Page() { - const local = useLocal() - const sync = useSync() - const sdk = useSDK() - const [store, setStore] = createStore({ - clickTimer: undefined as number | undefined, - fileSelectOpen: false, - }) - let inputRef!: HTMLDivElement - let messageScrollElement!: HTMLDivElement - const [activeItem, setActiveItem] = createSignal(undefined) - - const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" - - onMount(() => { - document.addEventListener("keydown", handleKeyDown) - }) - - onCleanup(() => { - document.removeEventListener("keydown", handleKeyDown) - }) - - const handleKeyDown = (event: KeyboardEvent) => { - if (event.getModifierState(MOD) && event.shiftKey && event.key.toLowerCase() === "p") { - event.preventDefault() - return - } - if (event.getModifierState(MOD) && event.key.toLowerCase() === "p") { - event.preventDefault() - setStore("fileSelectOpen", true) - return - } - - const focused = document.activeElement === inputRef - if (focused) { - if (event.key === "Escape") { - inputRef?.blur() - } - return - } - - // if (local.file.active()) { - // const active = local.file.active()! - // if (event.key === "Enter" && active.selection) { - // local.context.add({ - // type: "file", - // path: active.path, - // selection: { ...active.selection }, - // }) - // return - // } - // - // if (event.getModifierState(MOD)) { - // if (event.key.toLowerCase() === "a") { - // return - // } - // if (event.key.toLowerCase() === "c") { - // return - // } - // } - // } - - if (event.key.length === 1 && event.key !== "Unidentified" && !(event.ctrlKey || event.metaKey)) { - inputRef?.focus() - } - } - - const resetClickTimer = () => { - if (!store.clickTimer) return - clearTimeout(store.clickTimer) - setStore("clickTimer", undefined) - } - - const startClickTimer = () => { - const newClickTimer = setTimeout(() => { - setStore("clickTimer", undefined) - }, 300) - setStore("clickTimer", newClickTimer as unknown as number) - } - - const handleFileClick = async (file: LocalFile) => { - if (store.clickTimer) { - resetClickTimer() - local.file.update(file.path, { ...file, pinned: true }) - } else { - local.file.open(file.path) - startClickTimer() - } - } - - // const navigateChange = (dir: 1 | -1) => { - // const active = local.file.active() - // if (!active) return - // const current = local.file.changeIndex(active.path) - // const next = current === undefined ? (dir === 1 ? 0 : -1) : current + dir - // local.file.setChangeIndex(active.path, next) - // } - - const handleTabChange = (path: string) => { - local.session.setActiveTab(path) - if (path === "chat") return - local.session.open(path) - } - - const handleTabClose = (file: LocalFile) => { - local.session.close(file.path) - } - - const handleDragStart = (event: unknown) => { - const id = getDraggableId(event) - if (!id) return - setActiveItem(id) - } - - const handleDragOver = (event: DragEvent) => { - const { draggable, droppable } = event - if (draggable && droppable) { - const currentFiles = local.session.tabs()?.opened.map((file) => file) - const fromIndex = currentFiles?.indexOf(draggable.id.toString()) - const toIndex = currentFiles?.indexOf(droppable.id.toString()) - if (fromIndex !== toIndex && toIndex !== undefined) { - local.session.move(draggable.id.toString(), toIndex) - } - } - } - - const handleDragEnd = () => { - setActiveItem(undefined) - } - - // const scrollDiffItem = (element: HTMLElement) => { - // element.scrollIntoView({ block: "start", behavior: "instant" }) - // } - - const handleDiffTriggerClick = (event: MouseEvent) => { - // disabling scroll to diff for now - return - // const target = event.currentTarget as HTMLElement - // queueMicrotask(() => { - // if (target.getAttribute("aria-expanded") !== "true") return - // const item = target.closest('[data-slot="accordion-item"]') as HTMLElement | null - // if (!item) return - // scrollDiffItem(item) - // }) - } - - const handlePromptSubmit = async (parts: ContentPart[]) => { - const existingSession = local.session.active() - let session = existingSession - if (!session) { - const created = await sdk.client.session.create() - session = created.data ?? undefined - } - if (!session) return - - local.session.setActive(session.id) - if (!existingSession) { - local.session.copyTabs("", session.id) - } - local.session.setActiveTab(undefined) - const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) - - const text = parts.map((part) => part.content).join("") - const attachments = parts.filter((part) => part.type === "file") - - // const activeFile = local.context.active() - // if (activeFile) { - // registerAttachment( - // activeFile.path, - // activeFile.selection, - // activeFile.name ?? formatAttachmentLabel(activeFile.path, activeFile.selection), - // ) - // } - - // for (const contextFile of local.context.all()) { - // registerAttachment( - // contextFile.path, - // contextFile.selection, - // formatAttachmentLabel(contextFile.path, contextFile.selection), - // ) - // } - - const attachmentParts = attachments.map((attachment) => { - const absolute = toAbsolutePath(attachment.path) - const query = attachment.selection - ? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}` - : "" - return { - type: "file" as const, - mime: "text/plain", - url: `file://${absolute}${query}`, - filename: getFilename(attachment.path), - source: { - type: "file" as const, - text: { - value: attachment.content, - start: attachment.start, - end: attachment.end, - }, - path: absolute, - }, - } - }) - - await sdk.client.session.prompt({ - path: { id: session.id }, - body: { - agent: local.agent.current()!.name, - model: { - modelID: local.model.current()!.id, - providerID: local.model.current()!.provider.id, - }, - parts: [ - { - type: "text", - text, - }, - ...attachmentParts, - ], - }, - }) - } - - const handleNewSession = () => { - local.session.setActive(undefined) - inputRef?.focus() - } - - const TabVisual = (props: { file: LocalFile }): JSX.Element => { - return ( -
- - - {props.file.name} - - -
- ) - } - - const SortableTab = (props: { - file: LocalFile - onTabClick: (file: LocalFile) => void - onTabClose: (file: LocalFile) => void - }): JSX.Element => { - const sortable = createSortable(props.file.path) - - return ( - // @ts-ignore -
- -
- props.onTabClick(props.file)} - > - - props.onTabClose(props.file)} - /> - -
-
-
- ) - } - - const ConstrainDragYAxis = (): JSX.Element => { - const context = useDragDropContext() - if (!context) return <> - const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context - const transformer: Transformer = { - id: "constrain-y-axis", - order: 100, - callback: (transform) => ({ ...transform, y: 0 }), - } - onDragStart((event) => { - const id = getDraggableId(event) - if (!id) return - addTransformer("draggables", id, transformer) - }) - onDragEnd((event) => { - const id = getDraggableId(event) - if (!id) return - removeTransformer("draggables", id, transformer.id) - }) - return <> - } - - const getDraggableId = (event: unknown): string | undefined => { - if (typeof event !== "object" || event === null) return undefined - if (!("draggable" in event)) return undefined - const draggable = (event as { draggable?: { id?: unknown } }).draggable - if (!draggable) return undefined - return typeof draggable.id === "string" ? draggable.id : undefined - } - - return ( -
- -
-
-
- {getFilename(sync.data.path.directory)} -
-
- - x.id} - current={local.session.active()} - onSelect={(s) => local.session.setActive(s?.id)} - onHover={(s) => (!!s ? sync.session.sync(s?.id) : undefined)} - > - {(session) => { - const diffs = createMemo(() => session.summary?.diffs ?? []) - const filesChanged = createMemo(() => diffs().length) - const updated = DateTime.fromMillis(session.time.updated) - return ( - -
-
- - {session.title} - - - {Math.abs(updated.diffNow().as("seconds")) < 60 - ? "Now" - : updated - .toRelative({ style: "short", unit: ["days", "hours", "minutes"] }) - ?.replace(" ago", "") - ?.replace(/ days?/, "d") - ?.replace(" min.", "m") - ?.replace(" hr.", "h")} - -
-
- {`${filesChanged() || "No"} file${filesChanged() !== 1 ? "s" : ""} changed`} - -
-
-
- ) - }} -
-
-
-
- - - - -
- - -
Chat
- {/* */} - {/* */} - {/*
{local.session.context() ?? 0}%
*/} - {/*
*/} -
- {/* Review */} - - - {(file) => ( - - )} - - -
- setStore("fileSelectOpen", true)} - /> -
-
-
- -
- -
New session
-
- -
- {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
-
-
- -
- Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
-
-
- } - > - {(session) => { - const [store, setStore] = createStore<{ - messageId?: string - }>() - - const messages = createMemo(() => sync.data.message[session().id] ?? []) - const userMessages = createMemo(() => - messages() - .filter((m) => m.role === "user") - .sort((a, b) => b.id.localeCompare(a.id)), - ) - const lastUserMessage = createMemo(() => { - return userMessages()?.at(0) - }) - const activeMessage = createMemo(() => { - if (!store.messageId) return lastUserMessage() - return userMessages()?.find((m) => m.id === store.messageId) - }) - - return ( -
-
- 1}> - - -
- - {(message) => { - const isActive = createMemo(() => activeMessage()?.id === message.id) - const [titled, setTitled] = createSignal(!!message.summary?.title) - const assistantMessages = createMemo(() => { - return sync.data.message[session().id]?.filter( - (m) => m.role === "assistant" && m.parentID == message.id, - ) as AssistantMessageType[] - }) - const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) - const [expanded, setExpanded] = createSignal(false) - const parts = createMemo(() => sync.data.part[message.id]) - const title = createMemo(() => message.summary?.title) - const summary = createMemo(() => message.summary?.body) - const diffs = createMemo(() => message.summary?.diffs ?? []) - const hasToolPart = createMemo(() => - assistantMessages() - ?.flatMap((m) => sync.data.part[m.id]) - .some((p) => p?.type === "tool"), - ) - const working = createMemo(() => !summary() && !error()) - - // allowing time for the animations to finish - createEffect(() => { - title() - setTimeout(() => setTitled(!!title()), 10_000) - }) - createEffect(() => { - const complete = !!summary() || !!error() - setTimeout(() => setCompleted(complete), 1200) - }) - - return ( - -
- {/* Title */} -
-
- - } - > -

- {title()} -

-
-
-
-
- -
- {/* Summary */} - -
-
-

- - Summary - Response - -

- - {(summary) => ( - *]:fade-up-text": !diffs().length }} - text={summary()} - /> - )} - -
- - - {(diff) => ( - - - -
-
- -
- - - {getDirectory(diff.file)}‎ - - - - {getFilename(diff.file)} - -
-
-
- - -
-
-
-
- - - -
- )} -
-
-
-
- - - {error()?.data?.message as string} - - - {/* Response */} -
- - - - - - - -
-
- - Hide details - Show details - -
- -
-
- -
- - {(assistantMessage) => { - const parts = createMemo( - () => sync.data.part[assistantMessage.id], - ) - return - }} - - - - {error()?.data?.message as string} - - -
-
-
-
-
-
-
-
- ) - }} -
-
-
-
- ) - }} - -
- - {/* */} - - {(file) => ( - - {(() => { - { - /* const view = local.file.view(file) */ - } - { - /* const showRaw = view === "raw" || !file.content?.diff */ - } - { - /* const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "") */ - } - const node = local.file.node(file) - return ( - - ) - })()} - - )} - - - - {(() => { - const id = activeItem() - if (!id) return null - const draggedFile = local.file.node(id) - if (!draggedFile) return null - return ( -
- -
- ) - })()} -
- -
- { - inputRef = el - }} - onSubmit={handlePromptSubmit} - /> -
- - } - > -
    - - {(path) => ( -
  • - -
  • - )} -
    -
- -
- - - - x} - onOpenChange={(open) => setStore("fileSelectOpen", open)} - onSelect={(x) => (x ? local.session.open(x) : undefined)} - > - {(i) => ( -
-
- -
- - {getDirectory(i)} - - {getFilename(i)} -
-
-
-
- )} -
-
- - ) -} diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx new file mode 100644 index 000000000..afec9ee3c --- /dev/null +++ b/packages/desktop/src/pages/layout.tsx @@ -0,0 +1,75 @@ +import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" +import { createMemo, ParentProps } from "solid-js" +import { getFilename } from "@/utils" +import { DateTime } from "luxon" +import { useSync } from "@/context/sync" +import { VList } from "virtua/solid" +import { A, useParams } from "@solidjs/router" + +export default function Layout(props: ParentProps) { + const params = useParams() + const sync = useSync() + return ( +
+ +
+
+
+ {getFilename(sync.data.path.directory)} +
+ +
+
{props.children}
+
+
+ ) +} diff --git a/packages/desktop/src/pages/session-layout.tsx b/packages/desktop/src/pages/session-layout.tsx new file mode 100644 index 000000000..9a24608f0 --- /dev/null +++ b/packages/desktop/src/pages/session-layout.tsx @@ -0,0 +1,12 @@ +import { Show, type ParentProps } from "solid-js" +import { SessionProvider } from "@/context/session" +import { useParams } from "@solidjs/router" + +export default function Layout(props: ParentProps) { + const params = useParams() + return ( + + {props.children} + + ) +} diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx new file mode 100644 index 000000000..9c633f4f7 --- /dev/null +++ b/packages/desktop/src/pages/session.tsx @@ -0,0 +1,693 @@ +import { + SelectDialog, + IconButton, + Tabs, + Icon, + Accordion, + Diff, + Collapsible, + DiffChanges, + Message, + Typewriter, + Card, + Code, + Tooltip, + ProgressCircle, +} from "@opencode-ai/ui" +import { FileIcon } from "@/ui" +import { MessageProgress } from "@/components/message-progress" +import { + For, + onCleanup, + onMount, + Show, + Match, + Switch, + createSignal, + createEffect, + createMemo, + createResource, +} from "solid-js" +import { useLocal, type LocalFile } from "@/context/local" +import { createStore } from "solid-js/store" +import { getDirectory, getFilename } from "@/utils" +import { PromptInput } from "@/components/prompt-input" +import { DateTime } from "luxon" +import { + DragDropProvider, + DragDropSensors, + DragOverlay, + SortableProvider, + closestCenter, + createSortable, + useDragDropContext, +} from "@thisbeyond/solid-dnd" +import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" +import type { JSX } from "solid-js" +import { useSync } from "@/context/sync" +import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" +import { Markdown } from "@opencode-ai/ui" +import { Spinner } from "@/components/spinner" +import { useSession } from "@/context/session" + +export default function Page() { + const local = useLocal() + const sync = useSync() + const session = useSession() + const [store, setStore] = createStore({ + clickTimer: undefined as number | undefined, + fileSelectOpen: false, + activeDraggable: undefined as string | undefined, + }) + let inputRef!: HTMLDivElement + let messageScrollElement!: HTMLDivElement + + const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" + + onMount(() => { + document.addEventListener("keydown", handleKeyDown) + }) + + onCleanup(() => { + document.removeEventListener("keydown", handleKeyDown) + }) + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.getModifierState(MOD) && event.shiftKey && event.key.toLowerCase() === "p") { + event.preventDefault() + return + } + if (event.getModifierState(MOD) && event.key.toLowerCase() === "p") { + event.preventDefault() + setStore("fileSelectOpen", true) + return + } + + const focused = document.activeElement === inputRef + if (focused) { + if (event.key === "Escape") { + inputRef?.blur() + } + return + } + + // if (local.file.active()) { + // const active = local.file.active()! + // if (event.key === "Enter" && active.selection) { + // local.context.add({ + // type: "file", + // path: active.path, + // selection: { ...active.selection }, + // }) + // return + // } + // + // if (event.getModifierState(MOD)) { + // if (event.key.toLowerCase() === "a") { + // return + // } + // if (event.key.toLowerCase() === "c") { + // return + // } + // } + // } + + if (event.key.length === 1 && event.key !== "Unidentified" && !(event.ctrlKey || event.metaKey)) { + inputRef?.focus() + } + } + + const resetClickTimer = () => { + if (!store.clickTimer) return + clearTimeout(store.clickTimer) + setStore("clickTimer", undefined) + } + + const startClickTimer = () => { + const newClickTimer = setTimeout(() => { + setStore("clickTimer", undefined) + }, 300) + setStore("clickTimer", newClickTimer as unknown as number) + } + + const handleTabClick = async (tab: string) => { + if (store.clickTimer) { + resetClickTimer() + // local.file.update(file.path, { ...file, pinned: true }) + } else { + if (tab.startsWith("file://")) { + local.file.open(tab.replace("file://", "")) + } + startClickTimer() + } + } + + const handleDragStart = (event: unknown) => { + const id = getDraggableId(event) + if (!id) return + setStore("activeDraggable", id) + } + + const handleDragOver = (event: DragEvent) => { + const { draggable, droppable } = event + if (draggable && droppable) { + const currentTabs = session.layout.tabs.opened + const fromIndex = currentTabs?.indexOf(draggable.id.toString()) + const toIndex = currentTabs?.indexOf(droppable.id.toString()) + if (fromIndex !== toIndex && toIndex !== undefined) { + session.layout.moveTab(draggable.id.toString(), toIndex) + } + } + } + + const handleDragEnd = () => { + setStore("activeDraggable", undefined) + } + + const FileVisual = (props: { file: LocalFile }): JSX.Element => { + return ( +
+ + + {props.file.name} + + +
+ ) + } + + const SortableTab = (props: { + tab: string + onTabClick: (tab: string) => void + onTabClose: (tab: string) => void + }): JSX.Element => { + const sortable = createSortable(props.tab) + + const [file] = createResource( + () => props.tab, + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + + return ( + // @ts-ignore +
+
+ props.onTabClick(props.tab)}> + + {(f) => } + + props.onTabClose(props.tab)} + /> + +
+
+ ) + } + + const ConstrainDragYAxis = (): JSX.Element => { + const context = useDragDropContext() + if (!context) return <> + const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context + const transformer: Transformer = { + id: "constrain-y-axis", + order: 100, + callback: (transform) => ({ ...transform, y: 0 }), + } + onDragStart((event) => { + const id = getDraggableId(event) + if (!id) return + addTransformer("draggables", id, transformer) + }) + onDragEnd((event) => { + const id = getDraggableId(event) + if (!id) return + removeTransformer("draggables", id, transformer.id) + }) + return <> + } + + const getDraggableId = (event: unknown): string | undefined => { + if (typeof event !== "object" || event === null) return undefined + if (!("draggable" in event)) return undefined + const draggable = (event as { draggable?: { id?: unknown } }).draggable + if (!draggable) return undefined + return typeof draggable.id === "string" ? draggable.id : undefined + } + + return ( +
+ + + + +
+ + +
Chat
+ + +
{session.usage.context() ?? 0}%
+
+
+ {/* Review */} + + + {(tab) => } + + +
+ setStore("fileSelectOpen", true)} + /> +
+
+
+ +
+ +
New session
+
+ +
+ {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)} +
+
+
+ +
+ Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} + +
+
+
+ } + > + {(_) => { + return ( +
+
+ 1}> + + +
+ + {(message) => { + const isActive = createMemo(() => session.messages.active()?.id === message.id) + const [titled, setTitled] = createSignal(!!message.summary?.title) + const assistantMessages = createMemo(() => { + if (!session.id) return [] + return sync.data.message[session.id]?.filter( + (m) => m.role === "assistant" && m.parentID == message.id, + ) as AssistantMessageType[] + }) + const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) + const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) + const [detailsExpanded, setDetailsExpanded] = createSignal(false) + const parts = createMemo(() => sync.data.part[message.id]) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => sync.data.part[m.id]) + .some((p) => p?.type === "tool"), + ) + const working = createMemo(() => !message.summary?.body && !error()) + + // allowing time for the animations to finish + createEffect(() => { + const title = message.summary?.title + setTimeout(() => setTitled(!!title), 10_000) + }) + createEffect(() => { + const summary = message.summary?.body + const complete = !!summary || !!error() + setTimeout(() => setCompleted(complete), 1200) + }) + + return ( + +
+ {/* Title */} +
+
+ + } + > +

+ {message.summary?.title} +

+
+
+
+
+ +
+ {/* Summary */} + +
+
+

+ + Summary + Response + +

+ + {(summary) => ( + *]:fade-up-text": !message.summary?.diffs?.length, + }} + text={summary()} + /> + )} + +
+ + + {(diff) => ( + + + +
+
+ +
+ + + {getDirectory(diff.file)}‎ + + + + {getFilename(diff.file)} + +
+
+
+ + +
+
+
+
+ + + +
+ )} +
+
+
+
+ + + {error()?.data?.message as string} + + + {/* Response */} +
+ + + + + + + +
+
+ + Hide details + Show details + +
+ +
+
+ +
+ + {(assistantMessage) => { + const parts = createMemo(() => sync.data.part[assistantMessage.id]) + return + }} + + + + {error()?.data?.message as string} + + +
+
+
+
+
+
+
+
+ ) + }} +
+
+
+
+ ) + }} + +
+ + {/* */} + + {(tab) => { + const [file] = createResource( + () => tab, + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + return ( + + + + {(f) => ( + + )} + + + + ) + }} + + + + + {(draggedFile) => { + const [file] = createResource( + () => draggedFile(), + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + return ( +
+ {(f) => } +
+ ) + }} +
+
+ +
+ { + inputRef = el + }} + /> +
+ + }> +
    + + {(path) => ( +
  • + +
  • + )} +
    +
+ + + + x} + onOpenChange={(open) => setStore("fileSelectOpen", open)} + onSelect={(x) => (x ? session.layout.openTab("file://" + x) : undefined)} + > + {(i) => ( +
+
+ +
+ + {getDirectory(i)} + + {getFilename(i)} +
+
+
+
+ )} +
+
+ + ) +} diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx index f1c7efada..a31649788 100644 --- a/packages/ui/src/components/code.tsx +++ b/packages/ui/src/components/code.tsx @@ -11,20 +11,21 @@ export type CodeProps = FileOptions & { export function Code(props: CodeProps) { let container!: HTMLDivElement const [local, others] = splitProps(props, ["file", "class", "classList", "annotations"]) - const file = () => local.file createEffect(() => { const instance = new File({ theme: { dark: "oc-1-dark", light: "oc-1-light" }, // or any Shiki theme overflow: "wrap", // or 'scroll' themeType: "system", // 'system', 'light', or 'dark' + disableFileHeader: true, disableLineNumbers: false, // optional // lang: 'typescript', // optional - auto-detected from filename if not provided ...others, }) + container.innerHTML = "" instance.render({ - file: file(), + file: local.file, lineAnnotations: local.annotations, containerWrapper: container, }) diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index f3ca74a88..6297a6422 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -154,6 +154,7 @@ export function Diff(props: DiffProps) { ...others, }) + container.innerHTML = "" instance.render({ oldFile: local.before, newFile: local.after, diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index a2e127290..617997201 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -150,6 +150,7 @@ const newIcons = { "code-lines": ``, "square-arrow-top-right": ``, "circle-ban-sign": ``, + stop: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/input.tsx b/packages/ui/src/components/input.tsx index 509e242c9..55e84c3af 100644 --- a/packages/ui/src/components/input.tsx +++ b/packages/ui/src/components/input.tsx @@ -2,22 +2,37 @@ import { TextField as Kobalte } from "@kobalte/core/text-field" import { Show, splitProps } from "solid-js" import type { ComponentProps } from "solid-js" -export interface InputProps extends ComponentProps { +export interface InputProps + extends ComponentProps, + Pick, "value" | "onChange" | "onKeyDown"> { label?: string hideLabel?: boolean description?: string } export function Input(props: InputProps) { - const [local, others] = splitProps(props, ["class", "label", "hideLabel", "description", "placeholder"]) + const [local, others] = splitProps(props, [ + "class", + "label", + "hideLabel", + "description", + "value", + "onChange", + "onKeyDown", + ]) return ( - + {local.label} - + {local.description} diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx index aaba61fdf..766979ae6 100644 --- a/packages/ui/src/components/list.tsx +++ b/packages/ui/src/components/list.tsx @@ -62,7 +62,13 @@ export function List(props: ListProps) { }) return ( - + {(item) => ( - - {(session) => { - const updated = createMemo(() => DateTime.fromMillis(session.time.updated)) - return ( - - -
+
+ + + ) + }} + + + + + +
{props.children}
From 306f45f04a3cb282e2d88082d9afe222eb397573 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 18:04:31 -0500 Subject: [PATCH 046/218] add opencode import command to restore sessions from JSON exports --- packages/opencode/src/cli/cmd/import.ts | 51 +++++++++++++++++++++++++ packages/opencode/src/index.ts | 2 + 2 files changed, 53 insertions(+) create mode 100644 packages/opencode/src/cli/cmd/import.ts diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts new file mode 100644 index 000000000..eeb2e6885 --- /dev/null +++ b/packages/opencode/src/cli/cmd/import.ts @@ -0,0 +1,51 @@ +import type { Argv } from "yargs" +import { Session } from "../../session" +import { cmd } from "./cmd" +import { bootstrap } from "../bootstrap" +import { UI } from "../ui" +import { Storage } from "../../storage/storage" +import { Instance } from "../../project/instance" +import { EOL } from "os" + +export const ImportCommand = cmd({ + command: "import ", + describe: "import session data from JSON file", + builder: (yargs: Argv) => { + return yargs.positional("file", { + describe: "path to JSON file to import", + type: "string", + demandOption: true, + }) + }, + handler: async (args) => { + await bootstrap(process.cwd(), async () => { + const file = Bun.file(args.file as string) + const exists = await file.exists() + if (!exists) { + UI.error(`File not found: ${args.file}`) + process.exit(1) + } + + const exportData = (await file.json()) as { + info: Session.Info + messages: Array<{ + info: any + parts: any[] + }> + } + + await Storage.write(["session", Instance.project.id, exportData.info.id], exportData.info) + + for (const msg of exportData.messages) { + await Storage.write(["message", exportData.info.id, msg.info.id], msg.info) + + for (const part of msg.parts) { + await Storage.write(["part", msg.info.id, part.id], part) + } + } + + process.stdout.write(`Imported session: ${exportData.info.id}`) + process.stdout.write(EOL) + }) + }, +}) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 7fd7aeb10..015806997 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -17,6 +17,7 @@ import { StatsCommand } from "./cli/cmd/stats" import { McpCommand } from "./cli/cmd/mcp" import { GithubCommand } from "./cli/cmd/github" import { ExportCommand } from "./cli/cmd/export" +import { ImportCommand } from "./cli/cmd/import" import { AttachCommand } from "./cli/cmd/tui/attach" import { TuiThreadCommand } from "./cli/cmd/tui/thread" import { TuiSpawnCommand } from "./cli/cmd/tui/spawn" @@ -87,6 +88,7 @@ const cli = yargs(hideBin(process.argv)) .command(ModelsCommand) .command(StatsCommand) .command(ExportCommand) + .command(ImportCommand) .command(GithubCommand) .fail((msg) => { if ( From e316050bf573c657e4b38da0a8ac9230a9880a08 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 18:27:19 -0500 Subject: [PATCH 047/218] temporarily remove bun strip ansi due to bug --- packages/opencode/src/cli/cmd/import.ts | 16 +++++++--------- .../src/cli/cmd/tui/routes/session/index.tsx | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts index eeb2e6885..afebadc3d 100644 --- a/packages/opencode/src/cli/cmd/import.ts +++ b/packages/opencode/src/cli/cmd/import.ts @@ -2,7 +2,6 @@ import type { Argv } from "yargs" import { Session } from "../../session" import { cmd } from "./cmd" import { bootstrap } from "../bootstrap" -import { UI } from "../ui" import { Storage } from "../../storage/storage" import { Instance } from "../../project/instance" import { EOL } from "os" @@ -19,20 +18,19 @@ export const ImportCommand = cmd({ }, handler: async (args) => { await bootstrap(process.cwd(), async () => { - const file = Bun.file(args.file as string) - const exists = await file.exists() - if (!exists) { - UI.error(`File not found: ${args.file}`) - process.exit(1) - } - - const exportData = (await file.json()) as { + const file = Bun.file(args.file) + const exportData = (await file.json().catch(() => {})) as { info: Session.Info messages: Array<{ info: any parts: any[] }> } + if (!exportData) { + process.stdout.write(`File not found: ${args.file}`) + process.stdout.write(EOL) + return + } await Storage.write(["session", Instance.project.id, exportData.info.id], exportData.info) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 0adde4c89..1968987d0 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1171,7 +1171,7 @@ ToolRegistry.register({ name: "bash", container: "block", render(props) { - const output = createMemo(() => Bun.stripANSI(props.metadata.output?.trim() ?? "")) + const output = createMemo(() => props.metadata.output?.trim() ?? "") const { theme } = useTheme() return ( <> From c9ae89a38b3330456c1349c40b757248eba00c43 Mon Sep 17 00:00:00 2001 From: opencode Date: Wed, 5 Nov 2025 23:32:22 +0000 Subject: [PATCH 048/218] release: v1.0.30 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index ea7e22cd0..1706cd770 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.29", + "version": "1.0.30", "bin": { "opencode": "./bin/opencode", }, @@ -244,7 +244,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -264,7 +264,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.29", + "version": "1.0.30", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -275,7 +275,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -288,7 +288,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -318,7 +318,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 29f64f0c4..3b12a6864 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.29" + "version": "1.0.30" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 1c5a400ee..4c9b67923 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.29", + "version": "1.0.30", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index e9ab76ac8..b07210926 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.29", + "version": "1.0.30", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index cb4d10dfd..7bd6061fe 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.29", + "version": "1.0.30", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 77a5d643d..edadbc9a2 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.29", + "version": "1.0.30", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index bc3dc0c36..ce3939105 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.29", + "version": "1.0.30", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 3a7d7016d..cf4c87a23 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.29", + "version": "1.0.30", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 556090347..a8531f0ce 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.29", + "version": "1.0.30", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index e875c4af5..64c2c8ed6 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.29", + "version": "1.0.30", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 93efe207e..d7ed96b04 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.29", + "version": "1.0.30", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 2329f858b..96a3aa433 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.29", + "version": "1.0.30", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 9978202f1..577249797 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.29", + "version": "1.0.30", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index f0ef9ad72..a53147d89 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.29", + "version": "1.0.30", "publisher": "sst-dev", "repository": { "type": "git", From 2a9b6a85dee84d974c3808488b5b4ea3eb93abed Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 18:35:59 -0500 Subject: [PATCH 049/218] core: ensure export command output can be piped without UI interference --- packages/opencode/src/cli/cmd/export.ts | 18 ++++++++++++++---- packages/opencode/src/util/log.ts | 21 ++++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/cli/cmd/export.ts b/packages/opencode/src/cli/cmd/export.ts index 82952f1f6..4e040abd1 100644 --- a/packages/opencode/src/cli/cmd/export.ts +++ b/packages/opencode/src/cli/cmd/export.ts @@ -18,10 +18,13 @@ export const ExportCommand = cmd({ handler: async (args) => { await bootstrap(process.cwd(), async () => { let sessionID = args.sessionID + process.stderr.write(`Exporting session: ${sessionID ?? "latest"}`) if (!sessionID) { UI.empty() - prompts.intro("Export session") + prompts.intro("Export session", { + output: process.stderr, + }) const sessions = [] for await (const session of Session.list()) { @@ -29,8 +32,12 @@ export const ExportCommand = cmd({ } if (sessions.length === 0) { - prompts.log.error("No sessions found") - prompts.outro("Done") + prompts.log.error("No sessions found", { + output: process.stderr, + }) + prompts.outro("Done", { + output: process.stderr, + }) return } @@ -44,6 +51,7 @@ export const ExportCommand = cmd({ value: session.id, hint: `${new Date(session.time.updated).toLocaleString()} • ${session.id.slice(-8)}`, })), + output: process.stderr, }) if (prompts.isCancel(selectedSession)) { @@ -52,7 +60,9 @@ export const ExportCommand = cmd({ sessionID = selectedSession as string - prompts.outro("Exporting session...") + prompts.outro("Exporting session...", { + output: process.stderr, + }) } try { diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 463069562..6b31952fe 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -4,7 +4,9 @@ import { Global } from "../global" import z from "zod" export namespace Log { - export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" }) + export const Level = z + .enum(["DEBUG", "INFO", "WARN", "ERROR"]) + .meta({ ref: "LogLevel", description: "Log level" }) export type Level = z.infer const levelPriority: Record = { @@ -50,6 +52,7 @@ export namespace Log { export function file() { return logpath } + let write: (msg: string) => void export async function init(options: Options) { if (options.level) level = options.level @@ -62,7 +65,7 @@ export namespace Log { const logfile = Bun.file(logpath) await fs.truncate(logpath).catch(() => {}) const writer = logfile.writer() - process.stderr.write = (msg) => { + write = (msg) => { writer.write(msg) writer.flush() return true @@ -118,27 +121,31 @@ export namespace Log { const next = new Date() const diff = next.getTime() - last last = next.getTime() - return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n" + return ( + [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message] + .filter(Boolean) + .join(" ") + "\n" + ) } const result: Logger = { debug(message?: any, extra?: Record) { if (shouldLog("DEBUG")) { - process.stderr.write("DEBUG " + build(message, extra)) + write("DEBUG " + build(message, extra)) } }, info(message?: any, extra?: Record) { if (shouldLog("INFO")) { - process.stderr.write("INFO " + build(message, extra)) + write("INFO " + build(message, extra)) } }, error(message?: any, extra?: Record) { if (shouldLog("ERROR")) { - process.stderr.write("ERROR " + build(message, extra)) + write("ERROR " + build(message, extra)) } }, warn(message?: any, extra?: Record) { if (shouldLog("WARN")) { - process.stderr.write("WARN " + build(message, extra)) + write("WARN " + build(message, extra)) } }, tag(key: string, value: string) { From 8b26a1f9bdb61e636cd93d023bb1d17cbc505bf1 Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Thu, 6 Nov 2025 01:38:49 +0100 Subject: [PATCH 050/218] upgrade to opentui 0.1.35, mitigating disappearing content and crashes --- bun.lock | 20 ++++--------------- packages/opencode/package.json | 4 ++-- .../src/cli/cmd/tui/routes/session/index.tsx | 1 + 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/bun.lock b/bun.lock index 1706cd770..7b73d7b04 100644 --- a/bun.lock +++ b/bun.lock @@ -185,8 +185,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.33", - "@opentui/solid": "0.1.33", + "@opentui/core": "0.1.35", + "@opentui/solid": "0.1.35", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -962,21 +962,9 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.1.33", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.33", "@opentui/core-darwin-x64": "0.1.33", "@opentui/core-linux-arm64": "0.1.33", "@opentui/core-linux-x64": "0.1.33", "@opentui/core-win32-arm64": "0.1.33", "@opentui/core-win32-x64": "0.1.33", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vwHdrPIqnsY6YnG2JTNhenHSsx+HUPYrQTBZdmEfCj9ROGVzKgUKbSDH1xGK2OtSNRb2KVBg4XaMpq0bie6afQ=="], + "@opentui/core": ["@opentui/core@0.1.35", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.35", "@opentui/core-darwin-x64": "0.1.35", "@opentui/core-linux-arm64": "0.1.35", "@opentui/core-linux-x64": "0.1.35", "@opentui/core-win32-arm64": "0.1.35", "@opentui/core-win32-x64": "0.1.35", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-t7xUaie+ri7ROXcWWLrQ0XY0Mo2kH6gqyagZrNRgUjH1zudqPBYJddiw7Kc9LMuPtV8usPzlzjXIny+EzuawkA=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JBvzcP2V7fT9KxFAMenHRd/t72qPP5IL5kzge2uok1T7t2nw3Wa+CWI5s6FYP42p2b1W9qZkv5Fno5gA7OAYuQ=="], - - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-x7DY6VCkAky10z/2o4UkkuNW/nIvoX7uAh3dJOHWZCLbiKywSFvFk3QZVVcH5BMk4tOOophYTzika4s4HpaeMg=="], - - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-bBc1EdkVxsLBtqGjXM2BYpBJLa57ogcrSADSZbc5cQkPu0muSGzUwBbVnVZJUjWEfk6n5jcd4dDmLezVoQga0A=="], - - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.33", "", { "os": "linux", "cpu": "x64" }, "sha512-3oVL5mrLlKLUc1lc4v7xS3BJ9N7PnnimbGwAvlnVpfaAygotAs1XkPcjsUe6ItMnSJyi0FWiDHUE2+GiDtM5Nw=="], - - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q68v7wssE+r0OG1KIGfi7m3fnu8KOK4ZNg9ML6EwE47VF9/bqgUe+6fPiXh5mmHzTwof7nAOdXCf052av5/upQ=="], - - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.33", "", { "os": "win32", "cpu": "x64" }, "sha512-PvuchmUnbMCUXXMzfle/WTzhNGIdJ6RGCCoclx3YVUyNUVuUicPf42OEV+td2m81/Hr3CgcLn98HYX1TLIzPrw=="], - - "@opentui/solid": ["@opentui/solid@0.1.33", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.33", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-bWSALdGJ2j51zwZ2gK1ZIBxFgauHq+V1ejEnyd4XamYMdWfpAKU+AUWDVLbpx1T9XG1oAnycJZfYX7BsZdVOOg=="], + "@opentui/solid": ["@opentui/solid@0.1.35", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.35", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-VIhPmnZstthNpDWZzouOE6YzEnLHI3HbuT9wL6wf0K7Fp0kJEGUA7dlYscSpX9ZBY15B7COnIkSagC/C7hUQkw=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index cf4c87a23..b2d3e8c7c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -54,8 +54,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.33", - "@opentui/solid": "0.1.33", + "@opentui/core": "0.1.35", + "@opentui/solid": "0.1.35", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 1968987d0..37820759d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1011,6 +1011,7 @@ function TextPart(props: { part: TextPart; message: AssistantMessage }) { Date: Wed, 5 Nov 2025 20:00:09 -0500 Subject: [PATCH 051/218] fix log --- packages/opencode/src/util/log.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 6b31952fe..548f6b2b5 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -52,7 +52,7 @@ export namespace Log { export function file() { return logpath } - let write: (msg: string) => void + let write = process.stderr.write export async function init(options: Options) { if (options.level) level = options.level @@ -65,7 +65,7 @@ export namespace Log { const logfile = Bun.file(logpath) await fs.truncate(logpath).catch(() => {}) const writer = logfile.writer() - write = (msg) => { + write = (msg: any) => { writer.write(msg) writer.flush() return true From 247ce447760f6afbf9c522865fbfc43a15e2cefc Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 20:01:57 -0500 Subject: [PATCH 052/218] fix log --- packages/opencode/src/util/log.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 548f6b2b5..5b013c05f 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -52,7 +52,7 @@ export namespace Log { export function file() { return logpath } - let write = process.stderr.write + let write = (msg: any) => Bun.stderr.write(msg) export async function init(options: Options) { if (options.level) level = options.level @@ -68,7 +68,6 @@ export namespace Log { write = (msg: any) => { writer.write(msg) writer.flush() - return true } } From 6555a33eff502268bfa008c12fb61c556261a917 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 20:14:31 -0500 Subject: [PATCH 053/218] type errors --- packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 3 ++- packages/opencode/src/util/log.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index c3ae1407d..663e4f520 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -8,6 +8,7 @@ import { t, dim, fg, + type KeyBinding, } from "@opentui/core" import { createEffect, createMemo, Match, Switch, type JSX, onMount, batch } from "solid-js" import { useLocal } from "@tui/context/local" @@ -84,7 +85,7 @@ export function Prompt(props: PromptProps) { shift: binding.shift || undefined, action: "submit" as const, })), - ] + ] satisfies KeyBinding[] }) const fileStyleId = syntax().getStyleId("extmark.file")! diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 5b013c05f..e771a903a 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -65,9 +65,10 @@ export namespace Log { const logfile = Bun.file(logpath) await fs.truncate(logpath).catch(() => {}) const writer = logfile.writer() - write = (msg: any) => { - writer.write(msg) + write = async (msg: any) => { + const num = writer.write(msg) writer.flush() + return num } } From ef25650cedb46af3ba7ea6632761d496d2179f53 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 20:30:40 -0500 Subject: [PATCH 054/218] regen bunlock --- bun.lock | 201 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 85 deletions(-) diff --git a/bun.lock b/bun.lock index 7b73d7b04..50481cb75 100644 --- a/bun.lock +++ b/bun.lock @@ -352,7 +352,6 @@ "trustedDependencies": [ "sharp", "esbuild", - "tree-sitter", "web-tree-sitter", "tree-sitter-bash", ], @@ -572,7 +571,7 @@ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.8", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Ky929MfHh+qPhwCapYrRPwPVHtA2Ioex/DbGZyskGyNRDe9Ru3WThYZivyNVaPy5ergQSgMs9OKrM9Ajtz9F6w=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.9", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Drm7qlTKnvncEv+DANiQNEonq0H0LyIsoFZYJ6tJ8OhAoy5udIE8yp6BsVDYcIjcYLIybp4M7c/P7ly/56SoHg=="], "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="], @@ -602,7 +601,7 @@ "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], - "@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="], "@emotion/is-prop-valid": ["@emotion/is-prop-valid@0.8.8", "", { "dependencies": { "@emotion/memoize": "0.7.4" } }, "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA=="], @@ -612,57 +611,57 @@ "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], "@expressive-code/core": ["@expressive-code/core@0.41.3", "", { "dependencies": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", "hast-util-to-text": "^4.0.1", "hastscript": "^9.0.0", "postcss": "^8.4.38", "postcss-nested": "^6.0.1", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1" } }, "sha512-9qzohqU7O0+JwMEEgQhnBPOw5DtsQRBXhW++5fvEywsuX44vCGGof1SL5OvPElvNgaWZ4pFZAFSlkNOkGyLwSQ=="], @@ -964,6 +963,18 @@ "@opentui/core": ["@opentui/core@0.1.35", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.35", "@opentui/core-darwin-x64": "0.1.35", "@opentui/core-linux-arm64": "0.1.35", "@opentui/core-linux-x64": "0.1.35", "@opentui/core-win32-arm64": "0.1.35", "@opentui/core-win32-x64": "0.1.35", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-t7xUaie+ri7ROXcWWLrQ0XY0Mo2kH6gqyagZrNRgUjH1zudqPBYJddiw7Kc9LMuPtV8usPzlzjXIny+EzuawkA=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.35", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mSu6sS4a8DBejMqr06xtRdLtS+8m4oRzFVNtiKuY1T9PL74SnRmUl3VFxseY19ZMSVVAz/7hHPFX+ttmoT2pEw=="], + + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.35", "", { "os": "darwin", "cpu": "x64" }, "sha512-HqIbf25OSJgsxfe+G6ZvpsGolj1V8yTIXrAUcRSqCmw/Xdp82JqTnI6zx1cxQSllVmF4SxT+WwTRG216DE5MMw=="], + + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.35", "", { "os": "linux", "cpu": "arm64" }, "sha512-zNZpKg2fxZTsMJAjW3M9Il+1if61oe+d3m98hPm1i/UDtM+Fj8pkU0XBnsIJHfP5GVNnVHuLksXDpqmHquNmOg=="], + + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.35", "", { "os": "linux", "cpu": "x64" }, "sha512-KKLw9Po+KC0u0JWG9p1bCWKro3dzSg56QQ8Y9CR/wT2ywTatNrtiYeymoeft3BogWSz65PxQr7Dw8kwdUOD3yQ=="], + + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.35", "", { "os": "win32", "cpu": "arm64" }, "sha512-5ycmzf0D8SkaR4xlTZqneegFt4w4RK2Q9yZC3ZiAclNMtffVnWOkK/3zIpCwTdi2I1wHPVG0doX6pi0EUM7f2A=="], + + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.35", "", { "os": "win32", "cpu": "x64" }, "sha512-m/l9NkzT/asdpPPN0525z5JcYiHc++feIisqmHS1+IfqirTDuReyKk7HxU3MydxrFcsp1LBMCxFipWiYV2V9hw=="], + "@opentui/solid": ["@opentui/solid@0.1.35", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.35", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-VIhPmnZstthNpDWZzouOE6YzEnLHI3HbuT9wL6wf0K7Fp0kJEGUA7dlYscSpX9ZBY15B7COnIkSagC/C7hUQkw=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -1030,7 +1041,7 @@ "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="], - "@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="], + "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], "@poppinss/exception": ["@poppinss/exception@1.2.2", "", {}, "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg=="], @@ -1176,7 +1187,7 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - "@sindresorhus/is": ["@sindresorhus/is@7.1.0", "", {}, "sha512-7F/yz2IphV39hiS2zB4QYVkivrptHHh0K8qJJd9HhuWSdvf8AN7NpebW3CcDZDBQsUPMoDKWsY2WWgW7bqOcfA=="], + "@sindresorhus/is": ["@sindresorhus/is@7.1.1", "", {}, "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], @@ -1194,7 +1205,7 @@ "@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="], - "@smithy/config-resolver": ["@smithy/config-resolver@4.4.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-BciDJ5hkyYEGBBKMbjGB1A/Zq8bYZ41Zo9BMnGdKF6QD1fY4zIkYx6zui/0CHaVGnv6h0iy8y4rnPX9CPCAPyQ=="], + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-4Jys0ni2tB2VZzgslbEgszZyMdTkPOFGA8g+So/NjR8oy6Qwaq4eSwsrRI+NMtb0Dq4kqCzGUu/nGUx7OM/xfw=="], "@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="], @@ -1256,7 +1267,7 @@ "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GwaGjv/QLuL/QHQaqhf/maM7+MnRFQQs7Bsl6FlaeK6lm6U7mV5AAnVabw68cIoMl5FQFyKK62u7RWRzWL25OQ=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.7", "", { "dependencies": { "@smithy/config-resolver": "^4.4.1", "@smithy/credential-provider-imds": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-6hinjVqec0WYGsqN7h9hL/ywfULmJJNXGXnNZW7jrIn/cFuC/aVlVaiDfBIJEvKcOrmN8/EgsW69eY0gXABeHw=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.8", "", { "dependencies": { "@smithy/config-resolver": "^4.4.2", "@smithy/credential-provider-imds": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-gIoTf9V/nFSIZ0TtgDNLd+Ws59AJvijmMDYrOozoMHPJaG9cMRdqNO50jZTlbM6ydzQYY8L/mQ4tKSw/TB+s6g=="], "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], @@ -1310,7 +1321,7 @@ "@solidjs/start": ["@solidjs/start@1.2.0", "", { "dependencies": { "@tanstack/server-functions-plugin": "1.121.21", "@vinxi/plugin-directives": "^0.5.0", "@vinxi/server-components": "^0.5.0", "cookie-es": "^2.0.0", "defu": "^6.1.2", "error-stack-parser": "^2.1.4", "html-to-image": "^1.11.11", "radix3": "^1.1.0", "seroval": "^1.0.2", "seroval-plugins": "^1.0.2", "shiki": "^1.26.1", "source-map-js": "^1.0.2", "terracotta": "^1.0.4", "tinyglobby": "^0.2.2", "vite-plugin-solid": "^2.11.1" }, "peerDependencies": { "vinxi": "^0.5.7" } }, "sha512-SRv1g3R+4sxZnxCBPK1IedtLKsPhPJ7W/Yv4xEHjM4jJGPWi3ed35/yd0D5zhRK0C7zJIkZKbhnR/S3g8JUD5w=="], - "@speed-highlight/core": ["@speed-highlight/core@1.2.8", "", {}, "sha512-IGytNtnUnPIobIbOq5Y6LIlqiHNX+vnToQIS7lj6L5819C+rA8TXRDkkG8vePsiBOGcoW9R6i+dp2YBUKdB09Q=="], + "@speed-highlight/core": ["@speed-highlight/core@1.2.12", "", {}, "sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA=="], "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], @@ -1438,7 +1449,7 @@ "@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="], - "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], @@ -1580,7 +1591,7 @@ "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], - "axios": ["axios@1.13.1", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw=="], + "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], @@ -1608,13 +1619,13 @@ "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], - "bare-url": ["bare-url@2.3.1", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw=="], + "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="], "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="], @@ -1692,7 +1703,7 @@ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001752", "", {}, "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g=="], + "caniuse-lite": ["caniuse-lite@1.0.30001753", "", {}, "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -1910,7 +1921,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.244", "", {}, "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.245", "", {}, "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ=="], "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], @@ -1952,7 +1963,7 @@ "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], - "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "esbuild-plugin-copy": ["esbuild-plugin-copy@2.1.1", "", { "dependencies": { "chalk": "^4.1.2", "chokidar": "^3.5.3", "fs-extra": "^10.0.1", "globby": "^11.0.3" }, "peerDependencies": { "esbuild": ">= 0.14.0" } }, "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw=="], @@ -2656,7 +2667,7 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "miniflare": ["miniflare@4.20251011.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Qbw1Z8HTYM1adWl6FAtzhrj34/6dPRDPwdYOx21dkae8a/EaxbMzRIPbb4HKVGMVvtqbK1FaRCgDLVLolNzGHg=="], + "miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="], "minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], @@ -2740,11 +2751,11 @@ "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], - "ofetch": ["ofetch@1.5.0", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-A7llJ7eZyziA5xq9//3ZurA8OhFqtS99K5/V1sLBJ5j137CM/OAjlbA/TEJXBuOWwOfLqih+oH5U3ran4za1FQ=="], + "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], - "oidc-token-hash": ["oidc-token-hash@5.1.1", "", {}, "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g=="], + "oidc-token-hash": ["oidc-token-hash@5.2.0", "", {}, "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw=="], "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="], @@ -3234,7 +3245,7 @@ "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], - "style-to-js": ["style-to-js@1.1.18", "", { "dependencies": { "style-to-object": "1.0.11" } }, "sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg=="], + "style-to-js": ["style-to-js@1.1.19", "", { "dependencies": { "style-to-object": "1.0.12" } }, "sha512-Ev+SgeqiNGT1ufsXyVC5RrJRXdrkRJ1Gol9Qw7Pb72YCKJXrBvP0ckZhBeVSrw2m06DJpei2528uIpjMb4TsoQ=="], "style-to-object": ["style-to-object@1.0.12", "", { "dependencies": { "inline-style-parser": "0.2.6" } }, "sha512-ddJqYnoT4t97QvN2C95bCgt+m7AAgXjVnkk/jxAfmp7EAB8nnqqZYEbMd3em7/vEomDb2LAQKAy1RFfv41mdNw=="], @@ -3262,7 +3273,7 @@ "terracotta": ["terracotta@1.0.6", "", { "dependencies": { "solid-use": "^0.9.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-yVrmT/Lg6a3tEbeYEJH8ksb1PYkR5FA9k5gr1TchaSNIiA2ZWs5a+koEbePXwlBP0poaV7xViZ/v50bQFcMgqw=="], - "terser": ["terser@5.44.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w=="], + "terser": ["terser@5.44.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw=="], "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], @@ -3296,8 +3307,6 @@ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - "tree-sitter": ["tree-sitter@0.25.0", "", { "dependencies": { "node-addon-api": "^8.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-PGZZzFW63eElZJDe/b/R/LbsjDDYJa5UEjLZJB59RQsMX+fo0j54fqBPn1MGKav/QNa0JR0zBiVaikYDWCj5KQ=="], - "tree-sitter-bash": ["tree-sitter-bash@0.25.0", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-gZtlj9+qFS81qKxpLfD6H0UssQ3QBc/F0nKkPsiFDyfQF2YBqYvglFJUzchrPpVhZe9kLZTrJ9n2J6lmka69Vg=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -3412,7 +3421,7 @@ "unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="], - "unstorage": ["unstorage@1.17.1", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ=="], + "unstorage": ["unstorage@1.17.2", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.0", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-cKEsD6iBWJgOMJ6vW1ID/SYuqNf8oN4yqRk8OYqaVQ3nnkJXOT1PSpaMh2QfzLs78UN5kSNRD2c/mgjT8tX7+w=="], "untun": ["untun@0.1.3", "", { "dependencies": { "citty": "^0.1.5", "consola": "^3.2.3", "pathe": "^1.1.1" }, "bin": { "untun": "bin/untun.mjs" } }, "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ=="], @@ -3498,7 +3507,7 @@ "workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="], - "wrangler": ["wrangler@4.45.3", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.8", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-0ddEA9t4HeBgSVTVTcqtBHl7Z5CorWZ8tGgTQCP5XuL+9E1TJRwS6t/zzG51Ruwjb17SZYCaLchoM8V629S8cw=="], + "wrangler": ["wrangler@4.45.4", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.9", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-niXT7B463wQi7WXIHjYK8txgWhuKQLrGmhjoR58SnPhlkq4wGjd3rFrkVyRc/O58clGTfs672BSGOph4XMoQKw=="], "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], @@ -3536,7 +3545,7 @@ "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "youch": ["youch@4.1.0-beta.11", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-sQi6PERyO/mT8w564ojOVeAlYTtVQmC2GaktQAf+IdI75/GKIggosBuvyVXvEV+FATAT6RbLdIjFoiIId4ozoQ=="], + "youch": ["youch@4.1.0-beta.12", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.5", "@speed-highlight/core": "^1.2.9", "cookie-es": "^2.0.0", "youch-core": "^0.3.3" } }, "sha512-X+AQ2EdigcZb2h1XQmBMm19TrrfKXxEXWpnf8ThbARwiiSf/pA7MvRTCj5VHCI9z3vjJBsDeqWWyvaI9Bfp9Pg=="], "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], @@ -3580,6 +3589,8 @@ "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/markdown-remark/shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], + "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.8", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.4", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-uFNyFWadnULWK2cOw4n0hLKeu+xaVWeuECdP10cQ3K2fkybtTlhb7J7TcScdjmS8Yps7oje9S/ehYMfZrhrgCg=="], "@astrojs/sitemap/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -3600,13 +3611,13 @@ "@cloudflare/kv-asset-handler/mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], - "@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.21", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="], + "@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - "@expressive-code/plugin-shiki/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="], + "@expressive-code/plugin-shiki/shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], "@hono/zod-validator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -3748,9 +3759,9 @@ "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], @@ -3766,6 +3777,8 @@ "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@vercel/nft/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -3792,10 +3805,10 @@ "astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], - "astro/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "astro/shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], + "astro/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -3840,7 +3853,7 @@ "dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - "dot-prop/type-fest": ["type-fest@5.1.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-wQ531tuWvB6oK+pchHIu5lHe5f5wpSCqB8Kf4dWQRbOYc9HTge7JL0G4Qd44bh6QuJCccIzL3bugb8GI0MwHrg=="], + "dot-prop/type-fest": ["type-fest@5.2.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA=="], "drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], @@ -3876,8 +3889,6 @@ "gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], - "gel/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "giget/tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], @@ -4020,9 +4031,7 @@ "send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], - "sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - - "sitemap/sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], + "sitemap/sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -4038,8 +4047,6 @@ "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "style-to-js/style-to-object": ["style-to-object@1.0.11", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow=="], - "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], "sucrase/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -4050,8 +4057,6 @@ "token-types/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - "tree-sitter/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="], - "tree-sitter-bash/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="], "tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], @@ -4088,7 +4093,7 @@ "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], - "wrangler/unenv": ["unenv@2.0.0-rc.21", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="], + "wrangler/unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -4124,11 +4129,23 @@ "@actions/github/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + "@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], + + "@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], + + "@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], + + "@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], + + "@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], + + "@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.4", "", {}, "sha512-lDA9MqE8WGi7T/t2BMi+EAXhs4Vcvr94Gqx3q15cFEz8oFZMO4/SFBqYr/UcmNlvW+35alowkVj+w9VhLvs5Cw=="], "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], @@ -4182,17 +4199,17 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], - "@expressive-code/plugin-shiki/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], + "@expressive-code/plugin-shiki/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -4370,6 +4387,20 @@ "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "astro/sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "astro/shiki/@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], + + "astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], + + "astro/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], + + "astro/shiki/@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], + + "astro/shiki/@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], + + "astro/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], + "axios/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="], @@ -4378,6 +4409,8 @@ "babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "c12/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -4490,7 +4523,7 @@ "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], - "parse-bmfont-xml/xml2js/sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], + "parse-bmfont-xml/xml2js/sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], @@ -4514,8 +4547,6 @@ "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "style-to-js/style-to-object/inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], - "sucrase/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -4598,17 +4629,17 @@ "@actions/github/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], + "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], @@ -4740,7 +4771,7 @@ "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="], - "nitropack/c12/giget/nypm/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], + "nitropack/c12/giget/nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], "opencontrol/@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], From e136a40771fc02b16fdf7c08e294225b9678a312 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 20:31:13 -0500 Subject: [PATCH 055/218] ignore tmp type rrror --- packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 663e4f520..c977f7318 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -539,7 +539,8 @@ export function Prompt(props: PromptProps) { syncExtmarksWithPromptParts() }} keyBindings={textareaKeybindings()} - onKeyDown={async (e: KeyEvent) => { + // TODO: fix this any + onKeyDown={async (e: any) => { if (props.disabled) { e.preventDefault() return From c7031dfd77219312c450b5ea846b66a4b1a7db27 Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 01:35:40 +0000 Subject: [PATCH 056/218] release: v1.0.31 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index 50481cb75..d41ed5b17 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.30", + "version": "1.0.31", "bin": { "opencode": "./bin/opencode", }, @@ -244,7 +244,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -264,7 +264,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.30", + "version": "1.0.31", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -275,7 +275,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -288,7 +288,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -318,7 +318,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 3b12a6864..d44f20602 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.30" + "version": "1.0.31" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 4c9b67923..52cd15a2a 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.30", + "version": "1.0.31", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index b07210926..320d55126 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.30", + "version": "1.0.31", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 7bd6061fe..7fd3f40f5 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.30", + "version": "1.0.31", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index edadbc9a2..b2483eaf9 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.30", + "version": "1.0.31", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index ce3939105..7ea38ad38 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.30", + "version": "1.0.31", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b2d3e8c7c..783707ef3 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.30", + "version": "1.0.31", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index a8531f0ce..44146c5f7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.30", + "version": "1.0.31", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 64c2c8ed6..a89c29ddc 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.30", + "version": "1.0.31", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index d7ed96b04..9879db9d0 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.30", + "version": "1.0.31", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 96a3aa433..653ba92e8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.30", + "version": "1.0.31", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 577249797..dc230766b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.30", + "version": "1.0.31", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index a53147d89..471f3bdb8 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.30", + "version": "1.0.31", "publisher": "sst-dev", "repository": { "type": "git", From 5a84b9f4674f624fe0117d66d5d1af48886fb345 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 20:38:23 -0500 Subject: [PATCH 057/218] temporarily use strip-ansi package till bun bug is fixed --- bun.lock | 1 + packages/opencode/package.json | 1 + packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bun.lock b/bun.lock index d41ed5b17..ccfa89aa5 100644 --- a/bun.lock +++ b/bun.lock @@ -208,6 +208,7 @@ "partial-json": "0.1.7", "remeda": "catalog:", "solid-js": "catalog:", + "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", "turndown": "7.2.0", "ulid": "catalog:", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 783707ef3..10e3adf98 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -77,6 +77,7 @@ "partial-json": "0.1.7", "remeda": "catalog:", "solid-js": "catalog:", + "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", "turndown": "7.2.0", "ulid": "catalog:", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 37820759d..de8b2b897 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -68,6 +68,7 @@ import { useKV } from "../../context/kv.tsx" import { Editor } from "../../util/editor" import { Global } from "@/global" import fs from "fs/promises" +import stripAnsi from "strip-ansi" addDefaultParsers(parsers.parsers) @@ -1172,7 +1173,7 @@ ToolRegistry.register({ name: "bash", container: "block", render(props) { - const output = createMemo(() => props.metadata.output?.trim() ?? "") + const output = createMemo(() => stripAnsi(props.metadata.output?.trim() ?? "")) const { theme } = useTheme() return ( <> From bb6acc0ec6d8c01644b4b3bc5f47b36afa6948ca Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 01:43:35 +0000 Subject: [PATCH 058/218] release: v1.0.32 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index ccfa89aa5..3165da34c 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.31", + "version": "1.0.32", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.31", + "version": "1.0.32", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index d44f20602..3172193f3 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.31" + "version": "1.0.32" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 52cd15a2a..3f5aca73e 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.31", + "version": "1.0.32", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 320d55126..db6712c5a 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.31", + "version": "1.0.32", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 7fd3f40f5..75d82be24 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.31", + "version": "1.0.32", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index b2483eaf9..4e870b992 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.31", + "version": "1.0.32", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 7ea38ad38..55996a800 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.31", + "version": "1.0.32", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 10e3adf98..3062ffcaa 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.31", + "version": "1.0.32", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 44146c5f7..6cc844b00 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.31", + "version": "1.0.32", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index a89c29ddc..3cd806478 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.31", + "version": "1.0.32", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 9879db9d0..8b734d170 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.31", + "version": "1.0.32", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 653ba92e8..3633ea5e2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.31", + "version": "1.0.32", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index dc230766b..a3747f393 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.31", + "version": "1.0.32", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 471f3bdb8..df75dd4ce 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.31", + "version": "1.0.32", "publisher": "sst-dev", "repository": { "type": "git", From 6e553f7e200c074b9645bd897ec73e977f3969ff Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Thu, 6 Nov 2025 02:40:52 +0100 Subject: [PATCH 059/218] upgrade to opentui v0.1.36 --- bun.lock | 20 ++++++++++---------- packages/opencode/package.json | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bun.lock b/bun.lock index 3165da34c..ca5b62efe 100644 --- a/bun.lock +++ b/bun.lock @@ -185,8 +185,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.35", - "@opentui/solid": "0.1.35", + "@opentui/core": "0.1.36", + "@opentui/solid": "0.1.36", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -962,21 +962,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.1.35", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.35", "@opentui/core-darwin-x64": "0.1.35", "@opentui/core-linux-arm64": "0.1.35", "@opentui/core-linux-x64": "0.1.35", "@opentui/core-win32-arm64": "0.1.35", "@opentui/core-win32-x64": "0.1.35", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-t7xUaie+ri7ROXcWWLrQ0XY0Mo2kH6gqyagZrNRgUjH1zudqPBYJddiw7Kc9LMuPtV8usPzlzjXIny+EzuawkA=="], + "@opentui/core": ["@opentui/core@0.1.36", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.36", "@opentui/core-darwin-x64": "0.1.36", "@opentui/core-linux-arm64": "0.1.36", "@opentui/core-linux-x64": "0.1.36", "@opentui/core-win32-arm64": "0.1.36", "@opentui/core-win32-x64": "0.1.36", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-urDrj33udJ0dJGkZv+T5U0mCFBOOvUt9Tvqkrj8aRvi6kN0Bc5d2COuWcpAKo0TO9/PvjSwHC+CMnw2Sr46/ug=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.35", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mSu6sS4a8DBejMqr06xtRdLtS+8m4oRzFVNtiKuY1T9PL74SnRmUl3VFxseY19ZMSVVAz/7hHPFX+ttmoT2pEw=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.36", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/fb0k1H0CeTroVt2UoEAcVrEx1cIYy4B2zfX0MrwUkIfXi36aoIBnisBeYvyCpsQfxFAkyLYCCA3NzaYEyC5hg=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.35", "", { "os": "darwin", "cpu": "x64" }, "sha512-HqIbf25OSJgsxfe+G6ZvpsGolj1V8yTIXrAUcRSqCmw/Xdp82JqTnI6zx1cxQSllVmF4SxT+WwTRG216DE5MMw=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.36", "", { "os": "darwin", "cpu": "x64" }, "sha512-PZMydJbSDUoEWqZsyEV8+FSwMT+r7mWFL0ABgdALI3AOrSr7Z8dMcRnFWl8LhriuHS589THvETJEN28L4q/E2Q=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.35", "", { "os": "linux", "cpu": "arm64" }, "sha512-zNZpKg2fxZTsMJAjW3M9Il+1if61oe+d3m98hPm1i/UDtM+Fj8pkU0XBnsIJHfP5GVNnVHuLksXDpqmHquNmOg=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.36", "", { "os": "linux", "cpu": "arm64" }, "sha512-ATR+vdtraZEC/gHR1mQa/NYPlqFNBpsnnJAGepQmcxm85VceLYM701QaaIgNAwyYXiP6RQN1ZCv06MD1Ph1m4w=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.35", "", { "os": "linux", "cpu": "x64" }, "sha512-KKLw9Po+KC0u0JWG9p1bCWKro3dzSg56QQ8Y9CR/wT2ywTatNrtiYeymoeft3BogWSz65PxQr7Dw8kwdUOD3yQ=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.36", "", { "os": "linux", "cpu": "x64" }, "sha512-INsnPtcZVx68C+0Vd0L9+akDwNbWblUDqLmY9CftfmeLFubzvJXNRYTBvr7lX68fcst6Ho+0beUxyUoClKc0rg=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.35", "", { "os": "win32", "cpu": "arm64" }, "sha512-5ycmzf0D8SkaR4xlTZqneegFt4w4RK2Q9yZC3ZiAclNMtffVnWOkK/3zIpCwTdi2I1wHPVG0doX6pi0EUM7f2A=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.36", "", { "os": "win32", "cpu": "arm64" }, "sha512-x9lDZTL+xE8jsG1hP4pdsqCsZBu77JNR/ze5F7ZQkYQEC6Zl/XJtL1YT08nUlWOu4NMSws2xXV0lS/sJkbEgPA=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.35", "", { "os": "win32", "cpu": "x64" }, "sha512-m/l9NkzT/asdpPPN0525z5JcYiHc++feIisqmHS1+IfqirTDuReyKk7HxU3MydxrFcsp1LBMCxFipWiYV2V9hw=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.36", "", { "os": "win32", "cpu": "x64" }, "sha512-WVU+qtAfJe8ikPWbw8Hfli15GuQTMKiceTkF5lql5AQYy7PKYtGTzWszxOZKeUU1/eEd2X4REi8Bn0TprEMxYw=="], - "@opentui/solid": ["@opentui/solid@0.1.35", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.35", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-VIhPmnZstthNpDWZzouOE6YzEnLHI3HbuT9wL6wf0K7Fp0kJEGUA7dlYscSpX9ZBY15B7COnIkSagC/C7hUQkw=="], + "@opentui/solid": ["@opentui/solid@0.1.36", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.36", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-oHI01kZgyNecvXRFyQKJEDC5TCcsvfTPxHCa/XjbcZzH2qE2rfYMUF0mpwlLqoY9b3pm3w7Tpa8upzi1euBGJg=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 3062ffcaa..b292ec132 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -54,8 +54,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.35", - "@opentui/solid": "0.1.35", + "@opentui/core": "0.1.36", + "@opentui/solid": "0.1.36", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", From 0a5a02043c37d677fc336a7685ab3b4e5a66f1f2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 21:13:35 -0500 Subject: [PATCH 060/218] tui: move debug shortcuts to command palette for better discoverability --- packages/opencode/src/cli/cmd/tui/app.tsx | 29 +++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 1c309e895..c382a0f14 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -176,19 +176,6 @@ function App() { const { theme, mode, setMode } = useTheme() const exit = useExit() - useKeyboard(async (evt) => { - if (!Installation.isLocal()) return - if (evt.meta && evt.name === "t") { - renderer.toggleDebugOverlay() - return - } - - if (evt.meta && evt.name === "d") { - renderer.console.toggle() - return - } - }) - createEffect(() => { console.log(JSON.stringify(route.data)) }) @@ -310,6 +297,22 @@ function App() { onSelect: exit, category: "System", }, + { + title: "Toggle debug panel", + value: "app.debug", + onSelect: (dialog) => { + renderer.toggleDebugOverlay() + dialog.clear() + }, + }, + { + title: "Toggle console", + value: "app.fps", + onSelect: (dialog) => { + renderer.console.toggle() + dialog.clear() + }, + }, ]) createEffect(() => { From 86247b8ea96d629a5831927195c0d4a4d97b1e3d Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 02:18:41 +0000 Subject: [PATCH 061/218] release: v1.0.33 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index ca5b62efe..d3fa9343a 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.32", + "version": "1.0.33", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.32", + "version": "1.0.33", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 3172193f3..469183351 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.32" + "version": "1.0.33" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 3f5aca73e..ec8a9e91c 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.32", + "version": "1.0.33", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index db6712c5a..8fcaede90 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.32", + "version": "1.0.33", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 75d82be24..08e09bc92 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.32", + "version": "1.0.33", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 4e870b992..a13528432 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.32", + "version": "1.0.33", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 55996a800..b271d0e01 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.32", + "version": "1.0.33", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b292ec132..d583df4af 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.32", + "version": "1.0.33", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 6cc844b00..3a57ef859 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.32", + "version": "1.0.33", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 3cd806478..02b3f0bf8 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.32", + "version": "1.0.33", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 8b734d170..47b661c00 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.32", + "version": "1.0.33", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 3633ea5e2..a63e63d7c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.32", + "version": "1.0.33", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index a3747f393..0352ff975 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.32", + "version": "1.0.33", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index df75dd4ce..d8108acc2 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.32", + "version": "1.0.33", "publisher": "sst-dev", "repository": { "type": "git", From 2cc072b3dc0a75fd01e68f0803cc3de43ee14039 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 21:26:58 -0500 Subject: [PATCH 062/218] enable scrollbar --- packages/opencode/src/cli/cmd/tui/app.tsx | 2 ++ .../opencode/src/cli/cmd/tui/routes/session/index.tsx | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index c382a0f14..9d30ed6d8 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -299,6 +299,7 @@ function App() { }, { title: "Toggle debug panel", + category: "System", value: "app.debug", onSelect: (dialog) => { renderer.toggleDebugOverlay() @@ -307,6 +308,7 @@ function App() { }, { title: "Toggle console", + category: "System", value: "app.fps", onSelect: (dialog) => { renderer.console.toggle() diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index de8b2b897..c7c0e4a20 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -677,7 +677,15 @@ export function Session() { (scroll = r)} - scrollbarOptions={{ visible: false }} + scrollbarOptions={{ + trackOptions: { + backgroundColor: theme.backgroundElement, + foregroundColor: theme.primary, + }, + arrowOptions: { + foregroundColor: theme.primary, + }, + }} stickyScroll={true} stickyStart="bottom" flexGrow={1} From 11d6005b77dcf190e8a72f1beba8e89c2b397e41 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 5 Nov 2025 21:30:40 -0500 Subject: [PATCH 063/218] tui: reduce scrollbar visual prominence for less distracting interface --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index c7c0e4a20..971ed8170 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -680,10 +680,7 @@ export function Session() { scrollbarOptions={{ trackOptions: { backgroundColor: theme.backgroundElement, - foregroundColor: theme.primary, - }, - arrowOptions: { - foregroundColor: theme.primary, + foregroundColor: theme.border, }, }} stickyScroll={true} From 7c098c8849ef25eae38219fb6effa919476e6162 Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 02:35:17 +0000 Subject: [PATCH 064/218] release: v1.0.34 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index d3fa9343a..2a2fb35b6 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.33", + "version": "1.0.34", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.33", + "version": "1.0.34", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 469183351..6221f3961 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.33" + "version": "1.0.34" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ec8a9e91c..9b415ed44 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.33", + "version": "1.0.34", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 8fcaede90..7b1493698 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.33", + "version": "1.0.34", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 08e09bc92..55ec7aa07 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.33", + "version": "1.0.34", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a13528432..eeb9b9545 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.33", + "version": "1.0.34", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index b271d0e01..55a0eaa35 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.33", + "version": "1.0.34", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d583df4af..283ee35ea 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.33", + "version": "1.0.34", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 3a57ef859..93cd51f7d 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.33", + "version": "1.0.34", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 02b3f0bf8..c77789e10 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.33", + "version": "1.0.34", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 47b661c00..4284981e9 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.33", + "version": "1.0.34", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index a63e63d7c..a5caadeba 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.33", + "version": "1.0.34", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 0352ff975..e0f30c300 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.33", + "version": "1.0.34", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index d8108acc2..f114e1a6f 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.33", + "version": "1.0.34", "publisher": "sst-dev", "repository": { "type": "git", From afe85089490032cc00161e35c6eff210f3fa10db Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 6 Nov 2025 00:37:44 -0500 Subject: [PATCH 065/218] fix homebrew upgrade --- packages/opencode/src/installation/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index 323810e3e..f4209e5b8 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -132,6 +132,7 @@ export namespace Installation { const formula = await getBrewFormula() cmd = $`brew install ${formula}`.env({ HOMEBREW_NO_AUTO_UPDATE: "1", + ...process.env, }) break } From cc6d5c8ddd94e5d831ac89f0564b0582cdaad2bc Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 05:42:24 +0000 Subject: [PATCH 066/218] release: v1.0.35 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index 2a2fb35b6..a49f00939 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.34", + "version": "1.0.35", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.34", + "version": "1.0.35", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 6221f3961..457a823de 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.34" + "version": "1.0.35" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 9b415ed44..fe2fde6f0 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.34", + "version": "1.0.35", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 7b1493698..ab270d547 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.34", + "version": "1.0.35", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 55ec7aa07..131b9145c 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.34", + "version": "1.0.35", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index eeb9b9545..b7183d679 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.34", + "version": "1.0.35", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 55a0eaa35..ca92e33d3 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.34", + "version": "1.0.35", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 283ee35ea..c396d5c0b 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.34", + "version": "1.0.35", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 93cd51f7d..c510b519b 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.34", + "version": "1.0.35", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c77789e10..97830ba1b 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.34", + "version": "1.0.35", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 4284981e9..543f1bbcf 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.34", + "version": "1.0.35", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index a5caadeba..d1889af6d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.34", + "version": "1.0.35", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index e0f30c300..df95ff76f 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.34", + "version": "1.0.35", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index f114e1a6f..fd516fb1b 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.34", + "version": "1.0.35", "publisher": "sst-dev", "repository": { "type": "git", From e52bfab79db9ab064ebfd2620d7bba32db23a460 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 6 Nov 2025 00:37:16 -0500 Subject: [PATCH 067/218] Update sst --- bun.lock | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bun.lock b/bun.lock index a49f00939..8dd6cae29 100644 --- a/bun.lock +++ b/bun.lock @@ -11,7 +11,7 @@ "@tsconfig/bun": "catalog:", "husky": "9.1.7", "prettier": "3.6.2", - "sst": "3.17.19", + "sst": "3.17.22", "turbo": "2.5.6", }, }, @@ -3178,23 +3178,23 @@ "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], - "sst": ["sst@3.17.19", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.19", "sst-darwin-x64": "3.17.19", "sst-linux-arm64": "3.17.19", "sst-linux-x64": "3.17.19", "sst-linux-x86": "3.17.19", "sst-win32-arm64": "3.17.19", "sst-win32-x64": "3.17.19", "sst-win32-x86": "3.17.19" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-j0FlQhFZW+QWCczzqfPr6fZAF0Um7lP1tbGdd7zkbjFlxdk9BUBI4CYXUnopC6KaTMtjvpfg3XRF7v0bDc9g+A=="], + "sst": ["sst@3.17.22", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.22", "sst-darwin-x64": "3.17.22", "sst-linux-arm64": "3.17.22", "sst-linux-x64": "3.17.22", "sst-linux-x86": "3.17.22", "sst-win32-arm64": "3.17.22", "sst-win32-x64": "3.17.22", "sst-win32-x86": "3.17.22" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-C+XMTbm6fx+7eT+ESAMATqG7qV7+pyVfxYQb6osdH3jd4u91QW1VU/xlEru+RU1rs1ZE58ixXdRP75UGPn+gog=="], - "sst-darwin-arm64": ["sst-darwin-arm64@3.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6FeEgPqXkRT3o5qV0xktJ1eUiscJiPLBcGaxOxIEClpkVggZM83hO7Nizx/cAaAMhr1XQhbOZcKYueDHPdUY+Q=="], + "sst-darwin-arm64": ["sst-darwin-arm64@3.17.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B2pKq1dWc60+7HfXQ6/9etskxxNv9axxlQKveCLQAuG2a3mmtv2/jcR0Ch3mvSTGtW+KfhzUXda2kj7nZ/phBA=="], - "sst-darwin-x64": ["sst-darwin-x64@3.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-/z78dxfLHG8FtOhpjMnYSpKSdQjfdyKbq+cL3eud2+g2BQr7IyQ8BWNGimk2oadh38V3r6dO1/5aVJh3x3l1rg=="], + "sst-darwin-x64": ["sst-darwin-x64@3.17.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-flikYqXvhwwrS6x2FDOde+MQODHaZCIbUkVHYO3/gYo99rbAMQ8VpC/3LXnmnPEQkLOwWCSzLp4S4F9nG/PW2g=="], - "sst-linux-arm64": ["sst-linux-arm64@3.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-vbcMjiuLVxZ7352ajGlMqsS4J5AkAYvjLmsEALySUBVQhJUO9U7pk2P+Orfn702ZcO+6+NkGG9AL/g3K9EM1Tg=="], + "sst-linux-arm64": ["sst-linux-arm64@3.17.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pyD8Oej9js8XeCCebiEIde02vC5hc+bLl2/jR02K+9gYkGVJ6n5bkT8AlR8zWdS4FJKPyeJYUfjliT1T33j+g=="], - "sst-linux-x64": ["sst-linux-x64@3.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-gkNNmuHyvKjcb7RwMyoUH4wtgd7/bH7vUlMbcVsDzwt38y7+iTxyPMbcihucw42wDQRaDJtkDneSqj08U+MTFQ=="], + "sst-linux-x64": ["sst-linux-x64@3.17.22", "", { "os": "linux", "cpu": "x64" }, "sha512-A5p941edP9wgfgsbLUMeEPvi9JExj0OSaxgtFAC6/6BYoW4zruGAPzq206Ln6dNYP3gRdo5TJbSjio3F0ot8qg=="], - "sst-linux-x86": ["sst-linux-x86@3.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-Bsvunkh4onZRVv4Rxq7bT/63qQOg2KJoQKhAQtFkJdbri/cOA2QWkzqH8+pC5Sv9rSvbcIJAEIhMXILC0pqCJw=="], + "sst-linux-x86": ["sst-linux-x86@3.17.22", "", { "os": "linux", "cpu": "none" }, "sha512-pFDIi+ZwH8GOvy5He9wsbAjRGf/sTGhGE/V480w0A6itb9BC4jQ9sblJkk3Jx/fP2g27pKN2RNz+ifOU+GrUYQ=="], - "sst-win32-arm64": ["sst-win32-arm64@3.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-dKxR4v24AODJLHiT9yNena0JUgyz3cHyCi6HZyxyG3dXyWncMe1ZXMXIgs1ZEUcU4XeYM2HVy+Nnz4KB1US1Kg=="], + "sst-win32-arm64": ["sst-win32-arm64@3.17.22", "", { "os": "win32", "cpu": "arm64" }, "sha512-9KaIrk+Z6hLDNi9GShf9NLrZi9jC/NNGpUAn6HvTXr8c6HUyQzg6takMH8nrISGCPn92y+IYWqdglaqbgnJTog=="], - "sst-win32-x64": ["sst-win32-x64@3.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-zgxSkGWZ1dewAr4R3slN/d3X9yumQDvAUOlJiX/6QE9Z67t/XNlow4+5i3L2oz4WHAFi59Un12YxbfM+RsBDmA=="], + "sst-win32-x64": ["sst-win32-x64@3.17.22", "", { "os": "win32", "cpu": "x64" }, "sha512-cvzyet4octGHK7w05jPUSPmUdlAWyh8IzjB8Pcs873K9AUGJEtQCftOKZjXaFdIG9DTvFWCCBi9zdzClxT9jJg=="], - "sst-win32-x86": ["sst-win32-x86@3.17.19", "", { "os": "win32", "cpu": "none" }, "sha512-z8S0kyb0ibz9Q3cNYDpcKYX47jys7j/mdebC8HUhtED1qKEAfqQ1vsR+zvWyN64Z9Ijj7aPi1KwNV6Et3d7F8g=="], + "sst-win32-x86": ["sst-win32-x86@3.17.22", "", { "os": "win32", "cpu": "none" }, "sha512-ol5icDJuHzG+AjbGbCIQoF8z3oiikTF9CtccdK/udqEF861DnngWzM99IY5TJvmJlN+38yOV0MY4XI5hM6SEQA=="], "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="], diff --git a/package.json b/package.json index de6c62a5f..e0bf476c6 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@tsconfig/bun": "catalog:", "husky": "9.1.7", "prettier": "3.6.2", - "sst": "3.17.19", + "sst": "3.17.22", "turbo": "2.5.6" }, "dependencies": { From 49e4cfb286858a80ce8227ec4387b71a9f172777 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 01:09:59 -0600 Subject: [PATCH 068/218] =?UTF-8?q?Added=20big=20dot=20(=E2=97=8F)=20indic?= =?UTF-8?q?ator=20for=20current=20session=20in=20modal=20(#3980)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: opencode-agent[bot] Co-authored-by: rekram1-node Co-authored-by: Aiden Cline --- .../src/cli/cmd/tui/component/dialog-session-list.tsx | 5 +++++ packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 4a720aa15..95792ad83 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -20,6 +20,10 @@ export function DialogSessionList() { const deleteKeybind = "ctrl+d" + const currentSessionID = createMemo(() => + route.data.type === "session" ? route.data.sessionID : undefined + ) + const options = createMemo(() => { const today = new Date().toDateString() return sync.data.session @@ -50,6 +54,7 @@ export function DialogSessionList() { title="Sessions" options={options()} limit={50} + current={currentSessionID()} onMove={() => { setToDelete(undefined) }} diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 1c37b981c..6dd0b5cd4 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -273,6 +273,11 @@ function Option(props: { const { theme } = useTheme() return ( <> + + + ● + + Date: Thu, 6 Nov 2025 12:04:56 +0000 Subject: [PATCH 069/218] ignore: update download stats 2025-11-06 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 28a0e3da9..56d6d39fd 100644 --- a/STATS.md +++ b/STATS.md @@ -131,3 +131,4 @@ | 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) | | 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) | | 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) | +| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) | From ab345cf0dac4378163292a5fb99b102bb2922ee1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 06:05:08 -0600 Subject: [PATCH 070/218] feat(desktop): better tooltips --- .../desktop/src/components/prompt-input.tsx | 36 +++++++--- packages/ui/src/components/icon.tsx | 1 + packages/ui/src/components/tooltip.css | 69 ++++++++++--------- packages/ui/src/components/tooltip.tsx | 8 +-- 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index 7dc67f6ae..cad1f2098 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -1,6 +1,6 @@ -import { Button, Icon, IconButton, Select, SelectDialog } from "@opencode-ai/ui" +import { Button, Icon, IconButton, Select, SelectDialog, Tooltip } from "@opencode-ai/ui" import { useFilteredList } from "@opencode-ai/ui/hooks" -import { createEffect, on, Component, Show, For, onMount, onCleanup } from "solid-js" +import { createEffect, on, Component, Show, For, onMount, onCleanup, Switch, Match } from "solid-js" import { createStore } from "solid-js/store" import { FileIcon } from "@/ui" import { getDirectory, getFilename } from "@/utils" @@ -519,12 +519,32 @@ export const PromptInput: Component = (props) => { )} - + + +
+ Stop + ESC +
+
+ +
+ Send + +
+
+ + } + > + +
diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 617997201..2e96b9d85 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -151,6 +151,7 @@ const newIcons = { "square-arrow-top-right": ``, "circle-ban-sign": ``, stop: ``, + enter: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tooltip.css b/packages/ui/src/components/tooltip.css index 0577365d6..92825aca1 100644 --- a/packages/ui/src/components/tooltip.css +++ b/packages/ui/src/components/tooltip.css @@ -6,54 +6,55 @@ [data-component="tooltip"] { z-index: 1000; max-width: 320px; - border-radius: 12px; + border-radius: 6px; background-color: var(--surface-float-base); - color: var(--white); - padding: 2px 12px 2px 12px; + color: rgba(253, 252, 252, 0.94); + padding: 2px 8px; + border: 0.5px solid rgba(253, 252, 252, 0.2); box-shadow: var(--shadow-md); pointer-events: none !important; - transition: all 150ms ease-out; - transform: translate3d(0, 0, 0); - transform-origin: var(--kb-tooltip-content-transform-origin); + /* transition: all 150ms ease-out; */ + /* transform: translate3d(0, 0, 0); */ + /* transform-origin: var(--kb-tooltip-content-transform-origin); */ - /* text-14-regular */ + /* text-12-medium */ font-family: var(--font-family-sans); - font-size: var(--font-size-base); + font-size: var(--font-size-small); font-style: normal; - font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 171.429% */ + font-weight: var(--font-weight-medium); + line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); &[data-expanded] { opacity: 1; - transform: translate3d(0, 0, 0); + /* transform: translate3d(0, 0, 0); */ } &[data-closed] { opacity: 0; } - &[data-placement="top"] { - &[data-closed] { - transform: translate3d(0, 4px, 0); - } - } - - &[data-placement="bottom"] { - &[data-closed] { - transform: translate3d(0, -4px, 0); - } - } - - &[data-placement="left"] { - &[data-closed] { - transform: translate3d(4px, 0, 0); - } - } - - &[data-placement="right"] { - &[data-closed] { - transform: translate3d(-4px, 0, 0); - } - } + /* &[data-placement="top"] { */ + /* &[data-closed] { */ + /* transform: translate3d(0, 4px, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="bottom"] { */ + /* &[data-closed] { */ + /* transform: translate3d(0, -4px, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="left"] { */ + /* &[data-closed] { */ + /* transform: translate3d(4px, 0, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="right"] { */ + /* &[data-closed] { */ + /* transform: translate3d(-4px, 0, 0); */ + /* } */ + /* } */ } diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx index ff13c8d61..c3d1947d3 100644 --- a/packages/ui/src/components/tooltip.tsx +++ b/packages/ui/src/components/tooltip.tsx @@ -1,9 +1,9 @@ import { Tooltip as KobalteTooltip } from "@kobalte/core/tooltip" -import { children, createEffect, createSignal, splitProps } from "solid-js" +import { children, createEffect, createSignal, splitProps, type JSX } from "solid-js" import type { ComponentProps } from "solid-js" export interface TooltipProps extends ComponentProps { - value: string | (() => string) + value: JSX.Element class?: string } @@ -29,13 +29,13 @@ export function Tooltip(props: TooltipProps) { }) return ( - + {c()} - {typeof others.value === "function" ? others.value() : others.value} + {others.value} {/* */} From 146bae82cb584c253a42ed4565d423d9cded93a6 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 06:24:39 -0600 Subject: [PATCH 071/218] fix(desktop): button styles --- packages/desktop/src/pages/layout.tsx | 9 +++------ packages/ui/src/components/button.css | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 1d9669604..85f55e8bd 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -3,7 +3,6 @@ import { createMemo, For, ParentProps, Show } from "solid-js" import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" -import { VList } from "virtua/solid" import { A, useParams } from "@solidjs/router" export default function Layout(props: ParentProps) { @@ -19,11 +18,9 @@ export default function Layout(props: ParentProps) { {getFilename(sync.data.path.directory)}
- - - +
diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index cef6c5555..9dacc7100 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -5,6 +5,7 @@ import type { FileContent, FileNode, Model, Provider, File as FileStatus } from import { createSimpleContext } from "./helper" import { useSDK } from "./sdk" import { useSync } from "./sync" +import { makePersisted } from "@solid-primitives/storage" export type LocalFile = FileNode & Partial<{ @@ -456,11 +457,45 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } })() + const layout = (() => { + const [store, setStore] = makePersisted( + createStore({ + sidebar: { + opened: true, + width: 240, + }, + }), + { + name: "layout", + }, + ) + + return { + sidebar: { + opened: createMemo(() => store.sidebar.opened), + open() { + setStore("sidebar", "opened", true) + }, + close() { + setStore("sidebar", "opened", false) + }, + toggle() { + setStore("sidebar", "opened", (x) => !x) + }, + width: createMemo(() => store.sidebar.width), + resize(width: number) { + setStore("sidebar", "width", width) + }, + }, + } + })() + const result = { model, agent, file, context, + layout, } return result }, diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 85f55e8bd..e1560accb 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -1,27 +1,42 @@ -import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" +import { Button, Tooltip, DiffChanges, IconButton } from "@opencode-ai/ui" import { createMemo, For, ParentProps, Show } from "solid-js" -import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" import { A, useParams } from "@solidjs/router" +import { useLocal } from "@/context/local" export default function Layout(props: ParentProps) { const params = useParams() const sync = useSync() + const local = useLocal() return (
-
-
- {getFilename(sync.data.path.directory)} -
-
- -
+
+
+
+ + + +
+
+ + + + +
+ +
+ + + + +
{props.children}
diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index f5a08067a..f76d7465b 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -7,6 +7,7 @@ border-radius: 6px; text-decoration: none; user-select: none; + cursor: default; outline: none; &[data-variant="primary"] { @@ -93,11 +94,12 @@ gap: 8px; + /* text-12-medium */ font-family: var(--font-family-sans); - font-size: var(--font-size-base); + font-size: var(--font-size-small); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 171.429% */ + line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); } diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index 6fe95fccf..a491074fe 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -2,20 +2,11 @@ display: inline-flex; align-items: center; justify-content: center; - border-radius: 100%; + border-radius: 6px; text-decoration: none; user-select: none; aspect-ratio: 1; - - &:disabled { - background-color: var(--icon-strong-disabled); - color: var(--icon-invert-base); - cursor: not-allowed; - } - - &:focus { - outline: none; - } + flex-shrink: 0; &[data-variant="primary"] { background-color: var(--icon-strong-base); @@ -51,45 +42,62 @@ } &[data-variant="secondary"] { + border: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); + box-shadow: var(--shadow-xs-border); &:hover:not(:disabled) { - background-color: var(--surface-hover); + background-color: var(--button-secondary-hover); } &:active:not(:disabled) { - background-color: var(--surface-active); + background-color: var(--button-secondary-base); } &:focus:not(:disabled) { - background-color: var(--surface-focus); + background-color: var(--button-secondary-base); + } + &:focus-visible:not(:active) { + background-color: var(--button-secondary-base); + box-shadow: var(--shadow-xs-border-focus); + } + &:focus-visible:active { + box-shadow: none; + } + + [data-slot="icon"] { + color: var(--icon-strong-base); } } &[data-variant="ghost"] { background-color: transparent; + /* color: var(--icon-base); */ [data-slot="icon"] { - color: var(--icon-weak-base); - - &:hover:not(:disabled) { - color: var(--icon-weak-hover); - } - &:active:not(:disabled) { - color: var(--icon-string-active); - } + color: var(--icon-base); } - /* color: var(--text-strong); */ - /**/ - /* &:hover:not(:disabled) { */ - /* background-color: var(--surface-hover); */ - /* } */ - /* &:active:not(:disabled) { */ - /* background-color: var(--surface-active); */ - /* } */ - /* &:focus:not(:disabled) { */ - /* background-color: var(--surface-focus); */ - /* } */ + &:hover:not(:disabled) { + background-color: var(--surface-base-hover); + + [data-slot="icon"] { + color: var(--icon-hover); + } + } + &:active:not(:disabled) { + [data-slot="icon"] { + color: var(--icon-active); + } + } + &:selected:not(:disabled) { + background-color: var(--surface-base-active); + [data-slot="icon"] { + color: var(--icon-selected); + } + } + &:focus:not(:disabled) { + background-color: var(--surface-focus); + } } &[data-size="normal"] { @@ -103,9 +111,14 @@ &[data-size="large"] { height: 32px; - padding: 0 8px 0 6px; + /* padding: 0 8px 0 6px; */ gap: 8px; + [data-slot="icon"] { + height: 16px; + width: 16px; + } + /* text-12-medium */ font-family: var(--font-family-sans); font-size: var(--font-size-small); @@ -114,4 +127,14 @@ line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); } + + &:disabled { + background-color: var(--icon-strong-disabled); + color: var(--icon-invert-base); + cursor: not-allowed; + } + + &:focus { + outline: none; + } } diff --git a/packages/ui/src/components/icon-button.tsx b/packages/ui/src/components/icon-button.tsx index abc82609b..fccdebd04 100644 --- a/packages/ui/src/components/icon-button.tsx +++ b/packages/ui/src/components/icon-button.tsx @@ -2,7 +2,7 @@ import { Button as Kobalte } from "@kobalte/core/button" import { type ComponentProps, splitProps } from "solid-js" import { Icon, IconProps } from "./icon" -export interface IconButtonProps { +export interface IconButtonProps extends ComponentProps { icon: IconProps["name"] size?: "normal" | "large" iconSize?: IconProps["size"] @@ -22,7 +22,11 @@ export function IconButton(props: ComponentProps<"button"> & IconButtonProps) { [split.class ?? ""]: !!split.class, }} > - + ) } diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 2e96b9d85..082bbea90 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -152,6 +152,8 @@ const newIcons = { "circle-ban-sign": ``, stop: ``, enter: ``, + "layout-left": ``, + "speech-bubble": ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index 1d786fb4a..67f289283 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -7,7 +7,7 @@ overflow: clip; [data-slot="list"] { - height: 40px; + height: 48px; width: 100%; position: relative; display: flex; @@ -39,7 +39,7 @@ [data-slot="trigger"] { position: relative; height: 100%; - padding: 8px 24px; + padding: 14px 24px; display: flex; align-items: center; color: var(--text-base); diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs index 24987ca35..d67bebe0a 100644 --- a/packages/web/astro.config.mjs +++ b/packages/web/astro.config.mjs @@ -110,6 +110,7 @@ export default defineConfig({ ], redirects: { "/discord": "https://discord.gg/opencode", + "/desktop-feedback": "https://discord.gg/h5TNnkFVNy", }, }) From 81ab127f6321e53a3d3e39fcc26c88d5443a7900 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:49:39 -0600 Subject: [PATCH 073/218] fix(desktop): demo type error --- packages/ui/src/demo.tsx | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/demo.tsx b/packages/ui/src/demo.tsx index 5893ca751..6d60c25ae 100644 --- a/packages/ui/src/demo.tsx +++ b/packages/ui/src/demo.tsx @@ -126,7 +126,7 @@ const Demo: Component = () => { - `Dynamic tooltip: ${new Date().toLocaleTimeString()}`} placement="top"> +
@@ -141,7 +141,9 @@ const Demo: Component = () => { setInputValue(e.currentTarget.value)} + onInput={(e: InputEvent & { currentTarget: HTMLInputElement }) => + setInputValue(e.currentTarget.value) + } /> @@ -158,8 +160,15 @@ const Demo: Component = () => { checked={checked()} onChange={setChecked} /> - - + + { Example Dialog - This is an example dialog with a title and description. -
+ + This is an example dialog with a title and description. + +
@@ -249,7 +267,10 @@ const Demo: Component = () => {
-

Kobalte is a UI toolkit for building accessible web apps and design systems with SolidJS.

+

+ Kobalte is a UI toolkit for building accessible web apps and design systems with + SolidJS. +

From 492bf51a0dfa4fd278337b6e75da807048d44ff1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:56:46 -0600 Subject: [PATCH 074/218] fix(desktop): sidebar collapsed width --- packages/desktop/src/pages/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index e1560accb..c5957a2d2 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -16,7 +16,7 @@ export default function Layout(props: ParentProps) {
Date: Thu, 6 Nov 2025 11:25:37 -0500 Subject: [PATCH 075/218] fix undo command breaking other commands --- .../opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index f40d40360..88ca32420 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -210,7 +210,6 @@ export function Autocomplete(props: { display: "/undo", description: "undo the last message", onSelect: () => { - hide() command.trigger("session.undo") }, }, @@ -374,7 +373,7 @@ export function Autocomplete(props: { function hide() { const text = props.input().plainText - if (store.visible === "/" && !text.endsWith(" ")) { + if (store.visible === "/" && !text.endsWith(" ") && text.startsWith("/")) { const cursor = props.input().logicalCursor props.input().deleteRange(0, 0, cursor.row, cursor.col) } From 065f656fb00adf9287dee38ddb13928048f56d3d Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 6 Nov 2025 10:39:32 -0600 Subject: [PATCH 076/218] chore: rm hanging test --- packages/opencode/test/tool/patch.test.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/opencode/test/tool/patch.test.ts b/packages/opencode/test/tool/patch.test.ts index a34d7718d..78d1fecb1 100644 --- a/packages/opencode/test/tool/patch.test.ts +++ b/packages/opencode/test/tool/patch.test.ts @@ -53,22 +53,6 @@ describe("tool.patch", () => { }) }) - test("should reject files outside working directory", async () => { - await Instance.provide({ - directory: "/tmp", - fn: async () => { - const maliciousPatch = `*** Begin Patch -*** Add File: /etc/passwd -+malicious content -*** End Patch` - - await expect(patchTool.execute({ patchText: maliciousPatch }, ctx)).rejects.toThrow( - "is not in the current working directory", - ) - }, - }) - }) - test("should handle simple add file operation", async () => { await using fixture = await tmpdir() From 67f3c934fec7e118de90fe845c262a4d75ce03ba Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 6 Nov 2025 11:42:46 -0500 Subject: [PATCH 077/218] fix tests --- packages/opencode/src/permission/index.ts | 38 ++++++++++++++++++++--- packages/opencode/test/tool/patch.test.ts | 25 ++++++++++++--- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index eb541049d..fbcda6cf6 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -75,12 +75,23 @@ export namespace Permission { async (state) => { for (const pending of Object.values(state.pending)) { for (const item of Object.values(pending)) { - item.reject(new RejectedError(item.info.sessionID, item.info.id, item.info.callID, item.info.metadata)) + item.reject( + new RejectedError( + item.info.sessionID, + item.info.id, + item.info.callID, + item.info.metadata, + ), + ) } } }, ) + export function pending() { + return state().pending + } + export async function ask(input: { type: Info["type"] title: Info["title"] @@ -139,7 +150,11 @@ export namespace Permission { export const Response = z.enum(["once", "always", "reject"]) export type Response = z.infer - export function respond(input: { sessionID: Info["sessionID"]; permissionID: Info["id"]; response: Response }) { + export function respond(input: { + sessionID: Info["sessionID"] + permissionID: Info["id"] + response: Response + }) { log.info("response", input) const { pending, approved } = state() const match = pending[input.sessionID]?.[input.permissionID] @@ -151,7 +166,14 @@ export namespace Permission { response: input.response, }) if (input.response === "reject") { - match.reject(new RejectedError(input.sessionID, input.permissionID, match.info.callID, match.info.metadata)) + match.reject( + new RejectedError( + input.sessionID, + input.permissionID, + match.info.callID, + match.info.metadata, + ), + ) return } match.resolve() @@ -166,7 +188,11 @@ export namespace Permission { for (const item of Object.values(items)) { const itemKeys = toKeys(item.info.pattern, item.info.type) if (covered(itemKeys, approved[input.sessionID])) { - respond({ sessionID: item.info.sessionID, permissionID: item.info.id, response: input.response }) + respond({ + sessionID: item.info.sessionID, + permissionID: item.info.id, + response: input.response, + }) } } } @@ -179,7 +205,9 @@ export namespace Permission { public readonly toolCallID?: string, public readonly metadata?: Record, ) { - super(`The user rejected permission to use this specific tool call. You may try again with different parameters.`) + super( + `The user rejected permission to use this specific tool call. You may try again with different parameters.`, + ) } } } diff --git a/packages/opencode/test/tool/patch.test.ts b/packages/opencode/test/tool/patch.test.ts index 78d1fecb1..141759db1 100644 --- a/packages/opencode/test/tool/patch.test.ts +++ b/packages/opencode/test/tool/patch.test.ts @@ -3,6 +3,7 @@ import path from "path" import { PatchTool } from "../../src/tool/patch" import { Instance } from "../../src/project/instance" import { tmpdir } from "../fixture/fixture" +import { Permission } from "../../src/permission" import * as fs from "fs/promises" const ctx = { @@ -21,9 +22,7 @@ describe("tool.patch", () => { await Instance.provide({ directory: "/tmp", fn: async () => { - await expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow( - "patchText is required", - ) + expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required") }, }) }) @@ -32,7 +31,7 @@ describe("tool.patch", () => { await Instance.provide({ directory: "/tmp", fn: async () => { - await expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow( + expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow( "Failed to parse patch", ) }, @@ -46,13 +45,29 @@ describe("tool.patch", () => { const emptyPatch = `*** Begin Patch *** End Patch` - await expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow( + expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow( "No file changes found in patch", ) }, }) }) + test("should ask permission for files outside working directory", async () => { + await Instance.provide({ + directory: "/tmp", + fn: async () => { + const maliciousPatch = `*** Begin Patch +*** Add File: /etc/passwd ++malicious content +*** End Patch` + patchTool.execute({ patchText: maliciousPatch }, ctx) + // TODO: this sucks + await new Promise((resolve) => setTimeout(resolve, 100)) + expect(Permission.pending()[ctx.sessionID]).toBeDefined() + }, + }) + }) + test("should handle simple add file operation", async () => { await using fixture = await tmpdir() From d8bcf1f5f3923f6130cd8be02b67a565da1dfa19 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 6 Nov 2025 11:46:42 -0600 Subject: [PATCH 078/218] ci: update auto label --- .github/workflows/auto-label-tui.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-label-tui.yml b/.github/workflows/auto-label-tui.yml index 73cccf928..8f2d80b59 100644 --- a/.github/workflows/auto-label-tui.yml +++ b/.github/workflows/auto-label-tui.yml @@ -20,8 +20,8 @@ jobs: const title = issue.title; const description = issue.body || ''; - // Check for "opencode web" or desktop keywords - const webPattern = /(opencode web|\bdesktop\b)/i; + // Check for "opencode web" keyword + const webPattern = /(opencode web)/i; const isWebRelated = webPattern.test(title) || webPattern.test(description); // Check for version patterns like v1.0.x or 1.0.x From 8729edc5e059a9a29346044b016a6e05d9aca835 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 6 Nov 2025 11:55:57 -0600 Subject: [PATCH 079/218] update import command to accept share links --- packages/opencode/src/cli/cmd/import.ts | 57 ++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts index afebadc3d..ef7d243f5 100644 --- a/packages/opencode/src/cli/cmd/import.ts +++ b/packages/opencode/src/cli/cmd/import.ts @@ -8,26 +8,73 @@ import { EOL } from "os" export const ImportCommand = cmd({ command: "import ", - describe: "import session data from JSON file", + describe: "import session data from JSON file or URL", builder: (yargs: Argv) => { return yargs.positional("file", { - describe: "path to JSON file to import", + describe: "path to JSON file or opencode.ai share URL", type: "string", demandOption: true, }) }, handler: async (args) => { await bootstrap(process.cwd(), async () => { - const file = Bun.file(args.file) - const exportData = (await file.json().catch(() => {})) as { + let exportData: { info: Session.Info messages: Array<{ info: any parts: any[] }> + } | undefined + + const isUrl = args.file.startsWith("http://") || args.file.startsWith("https://") + + if (isUrl) { + const urlMatch = args.file.match(/https?:\/\/opencode\.ai\/s\/([a-zA-Z0-9_-]+)/) + if (!urlMatch) { + process.stdout.write(`Invalid URL format. Expected: https://opencode.ai/s/`) + process.stdout.write(EOL) + return + } + + const slug = urlMatch[1] + const response = await fetch(`https://api.opencode.ai/share_data?id=${slug}`) + + if (!response.ok) { + process.stdout.write(`Failed to fetch share data: ${response.statusText}`) + process.stdout.write(EOL) + return + } + + const data = await response.json() + + if (!data.info || !data.messages || Object.keys(data.messages).length === 0) { + process.stdout.write(`Share not found: ${slug}`) + process.stdout.write(EOL) + return + } + + exportData = { + info: data.info, + messages: Object.values(data.messages).map((msg: any) => { + const { parts, ...info } = msg + return { + info, + parts, + } + }), + } + } else { + const file = Bun.file(args.file) + exportData = await file.json().catch(() => {}) + if (!exportData) { + process.stdout.write(`File not found: ${args.file}`) + process.stdout.write(EOL) + return + } } + if (!exportData) { - process.stdout.write(`File not found: ${args.file}`) + process.stdout.write(`Failed to read session data`) process.stdout.write(EOL) return } From 1ea3a8eb9beeb7d510fd29164ea741acec1ee04d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 6 Nov 2025 13:03:02 -0500 Subject: [PATCH 080/218] big format --- AGENTS.md | 46 +- github/sst-env.d.ts | 2 +- ...2480f80fa29b850af461dce619c0b2f-audit.json | 28 +- .../console/app/src/component/dropdown.css | 2 +- packages/console/app/src/component/faq.tsx | 5 +- packages/console/app/src/component/icon.tsx | 133 +++++- packages/console/app/src/component/modal.css | 2 +- .../console/app/src/routes/auth/authorize.ts | 5 +- .../console/app/src/routes/brand/index.css | 11 +- packages/console/app/src/routes/user-menu.css | 2 +- .../app/src/routes/workspace-picker.css | 2 +- .../app/src/routes/workspace-picker.tsx | 11 +- packages/console/app/src/routes/workspace.css | 2 +- .../[id]/billing/billing-section.module.css | 2 +- .../billing/monthly-limit-section.module.css | 2 +- .../[id]/billing/payment-section.tsx | 5 +- .../[id]/billing/reload-section.module.css | 1 - .../[id]/keys/key-section.module.css | 9 +- .../workspace/[id]/keys/key-section.tsx | 10 +- .../workspace/[id]/members/member-section.tsx | 7 +- .../workspace/[id]/members/role-dropdown.css | 2 +- .../[id]/new-user-section.module.css | 2 +- .../workspace/[id]/new-user-section.tsx | 15 +- .../[id]/provider-section.module.css | 3 +- .../workspace/[id]/provider-section.tsx | 20 +- .../[id]/settings/settings-section.module.css | 6 +- packages/console/app/src/routes/zen/index.tsx | 132 ++++-- .../src/routes/zen/util/provider/anthropic.ts | 61 ++- .../zen/util/provider/openai-compatible.ts | 35 +- .../src/routes/zen/util/provider/openai.ts | 79 +++- packages/console/app/src/style/token/font.css | 3 +- packages/console/app/sst-env.d.ts | 2 +- .../core/migrations/meta/0018_snapshot.json | 57 +-- .../core/migrations/meta/0019_snapshot.json | 57 +-- .../core/migrations/meta/0020_snapshot.json | 57 +-- .../core/migrations/meta/0021_snapshot.json | 57 +-- .../core/migrations/meta/0022_snapshot.json | 62 +-- .../core/migrations/meta/0023_snapshot.json | 70 +-- .../core/migrations/meta/0024_snapshot.json | 70 +-- .../core/migrations/meta/0025_snapshot.json | 70 +-- .../core/migrations/meta/0026_snapshot.json | 65 +-- .../core/migrations/meta/0027_snapshot.json | 65 +-- .../core/migrations/meta/0028_snapshot.json | 65 +-- .../core/migrations/meta/0029_snapshot.json | 65 +-- .../core/migrations/meta/0030_snapshot.json | 75 +--- .../core/migrations/meta/0031_snapshot.json | 85 +--- .../core/migrations/meta/0032_snapshot.json | 85 +--- .../core/migrations/meta/0033_snapshot.json | 85 +--- .../core/migrations/meta/0034_snapshot.json | 90 +--- .../core/migrations/meta/0035_snapshot.json | 90 +--- .../core/migrations/meta/0036_snapshot.json | 98 +---- .../core/migrations/meta/0037_snapshot.json | 98 +---- .../core/migrations/meta/_journal.json | 2 +- packages/console/core/src/aws.ts | 53 +-- packages/console/core/src/drizzle/index.ts | 10 +- packages/console/core/src/key.ts | 10 +- packages/console/core/src/provider.ts | 11 +- packages/console/core/src/schema/auth.sql.ts | 9 +- packages/console/core/src/schema/model.sql.ts | 5 +- .../console/core/src/schema/provider.sql.ts | 5 +- packages/console/core/src/schema/user.sql.ts | 10 +- packages/console/core/src/user.ts | 28 +- packages/console/core/sst-env.d.ts | 138 +++--- packages/console/function/sst-env.d.ts | 138 +++--- .../mail/emails/templates/InviteEmail.tsx | 25 +- packages/console/mail/sst-env.d.ts | 2 +- packages/console/resource/sst-env.d.ts | 138 +++--- packages/desktop/src/sst-env.d.ts | 6 +- packages/desktop/sst-env.d.ts | 2 +- packages/function/src/api.ts | 6 +- packages/function/sst-env.d.ts | 138 +++--- packages/opencode/script/postinstall.mjs | 7 +- packages/opencode/script/schema.ts | 15 +- packages/opencode/src/agent/agent.ts | 18 +- packages/opencode/src/bus/index.ts | 10 +- packages/opencode/src/cli/cmd/auth.ts | 405 +++++++++--------- packages/opencode/src/cli/cmd/debug/lsp.ts | 6 +- .../opencode/src/cli/cmd/debug/ripgrep.ts | 7 +- .../opencode/src/cli/cmd/debug/snapshot.ts | 3 +- packages/opencode/src/cli/cmd/generate.ts | 2 +- packages/opencode/src/cli/cmd/github.ts | 60 ++- packages/opencode/src/cli/cmd/run.ts | 18 +- .../cli/cmd/tui/component/dialog-model.tsx | 6 +- .../cmd/tui/component/dialog-session-list.tsx | 4 +- .../src/cli/cmd/tui/component/logo.tsx | 14 +- .../src/cli/cmd/tui/context/route.tsx | 8 +- .../opencode/src/cli/cmd/tui/context/sync.tsx | 1 + .../cli/cmd/tui/context/theme/nightowl.json | 2 +- .../cmd/tui/routes/session/dialog-message.tsx | 4 +- .../tui/routes/session/dialog-timeline.tsx | 12 +- .../src/cli/cmd/tui/routes/session/index.tsx | 5 +- packages/opencode/src/cli/cmd/tui/spawn.ts | 7 +- .../src/cli/cmd/tui/ui/dialog-confirm.tsx | 4 +- .../opencode/src/cli/cmd/tui/util/editor.ts | 5 +- packages/opencode/src/cli/cmd/upgrade.ts | 4 +- packages/opencode/src/config/config.ts | 5 +- packages/opencode/src/file/fzf.ts | 4 +- packages/opencode/src/file/ripgrep.ts | 11 +- packages/opencode/src/file/time.ts | 5 +- packages/opencode/src/file/watcher.ts | 6 +- packages/opencode/src/id/id.ts | 6 +- packages/opencode/src/lsp/client.ts | 25 +- packages/opencode/src/patch/index.ts | 211 +++++---- packages/opencode/src/provider/transform.ts | 18 +- packages/opencode/src/server/server.ts | 2 +- packages/opencode/src/session/compaction.ts | 34 +- packages/opencode/src/session/lock.ts | 5 +- packages/opencode/src/session/message-v2.ts | 35 +- packages/opencode/src/session/message.ts | 23 +- packages/opencode/src/session/prompt.ts | 3 - packages/opencode/src/session/revert.ts | 4 +- packages/opencode/src/session/system.ts | 9 +- packages/opencode/src/session/todo.ts | 4 +- packages/opencode/src/share/share.ts | 9 +- packages/opencode/src/snapshot/index.ts | 25 +- packages/opencode/src/tool/edit.ts | 30 +- packages/opencode/src/tool/grep.ts | 10 +- packages/opencode/src/tool/ls.ts | 9 +- packages/opencode/src/tool/lsp-diagnostics.ts | 4 +- packages/opencode/src/tool/multiedit.ts | 9 +- packages/opencode/src/tool/read.ts | 12 +- packages/opencode/src/tool/registry.ts | 7 +- packages/opencode/src/tool/task.ts | 12 +- packages/opencode/src/tool/webfetch.ts | 10 +- packages/opencode/src/tool/write.ts | 8 +- packages/opencode/src/util/binary.ts | 6 +- packages/opencode/src/util/defer.ts | 4 +- packages/opencode/src/util/eventloop.ts | 10 +- packages/opencode/src/util/lock.ts | 7 +- packages/opencode/src/util/rpc.ts | 5 +- packages/opencode/src/util/wildcard.ts | 17 +- packages/opencode/sst-env.d.ts | 2 +- packages/opencode/test/patch/patch.test.ts | 139 +++--- packages/opencode/test/session/retry.test.ts | 4 +- packages/opencode/test/util/wildcard.test.ts | 13 +- packages/plugin/package.json | 2 +- packages/plugin/src/index.ts | 5 +- packages/plugin/sst-env.d.ts | 2 +- packages/script/sst-env.d.ts | 2 +- packages/sdk/go/.github/workflows/ci.yml | 14 +- packages/sdk/go/.release-please-manifest.json | 2 +- packages/sdk/go/CHANGELOG.md | 147 ++++--- packages/sdk/go/release-please-config.json | 7 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/server.ts | 16 +- packages/sdk/js/sst-env.d.ts | 2 +- packages/sdk/python/README.md | 18 +- packages/sdk/python/docs/generation.md | 3 + packages/sdk/python/docs/index.md | 1 + packages/sdk/python/docs/installation.md | 5 + packages/sdk/python/docs/publishing.md | 3 + packages/sdk/python/docs/testing.md | 1 + packages/sdk/python/mkdocs.yml | 2 +- packages/slack/src/index.ts | 15 +- packages/slack/sst-env.d.ts | 2 +- .../ui/src/assets/favicon/site.webmanifest | 2 +- packages/ui/src/components/checkbox.tsx | 13 +- packages/ui/src/components/dialog.tsx | 6 +- packages/ui/src/styles/base.css | 4 +- packages/ui/src/styles/tailwind/colors.css | 2 +- packages/ui/src/styles/theme.css | 17 +- packages/ui/sst-env.d.ts | 2 +- packages/ui/tsconfig.json | 11 +- packages/web/config.mjs | 3 +- packages/web/src/components/Share.tsx | 46 +- packages/web/src/components/icons/index.tsx | 9 +- packages/web/src/components/share/common.tsx | 7 +- .../web/src/components/share/content-code.tsx | 6 +- .../web/src/components/share/content-diff.tsx | 20 +- .../web/src/components/share/copy-button.tsx | 6 +- packages/web/src/components/share/part.tsx | 115 ++++- packages/web/src/content/docs/1-0.mdx | 3 +- packages/web/src/content/docs/index.mdx | 24 +- packages/web/src/content/docs/permissions.mdx | 1 - packages/web/src/content/docs/rules.mdx | 6 +- .../web/src/content/docs/troubleshooting.mdx | 2 +- packages/web/src/styles/custom.css | 29 +- packages/web/sst-env.d.ts | 2 +- script/format.ts | 2 +- script/stats.ts | 14 +- sdks/vscode/src/extension.ts | 133 +++--- sdks/vscode/sst-env.d.ts | 2 +- sst-env.d.ts | 150 +++---- 183 files changed, 2629 insertions(+), 2497 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9c6111b08..22b305dac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,27 +21,27 @@ json { - "recipient_name": "multi_tool_use.parallel", - "parameters": { - "tool_uses": [ - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.tsx" - } - }, - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.ts" - } - }, - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.md" - } - } - ] - } +"recipient_name": "multi_tool_use.parallel", +"parameters": { +"tool_uses": [ +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.tsx" +} +}, +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.ts" +} +}, +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.md" +} +} +] +} } diff --git a/github/sst-env.d.ts b/github/sst-env.d.ts index f742a1200..6b69016e7 100644 --- a/github/sst-env.d.ts +++ b/github/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json b/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json index 7c57ef350..41cb01a2b 100644 --- a/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json +++ b/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json @@ -1,15 +1,15 @@ { - "keep": { - "days": true, - "amount": 14 - }, - "auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json", - "files": [ - { - "date": 1759827172859, - "name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log", - "hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16" - } - ], - "hashType": "sha256" -} \ No newline at end of file + "keep": { + "days": true, + "amount": 14 + }, + "auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json", + "files": [ + { + "date": 1759827172859, + "name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log", + "hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16" + } + ], + "hashType": "sha256" +} diff --git a/packages/console/app/src/component/dropdown.css b/packages/console/app/src/component/dropdown.css index 982367c6b..242940e6a 100644 --- a/packages/console/app/src/component/dropdown.css +++ b/packages/console/app/src/component/dropdown.css @@ -77,4 +77,4 @@ background-color: var(--color-accent-alpha); } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/component/faq.tsx b/packages/console/app/src/component/faq.tsx index 753a0dce4..47dca9513 100644 --- a/packages/console/app/src/component/faq.tsx +++ b/packages/console/app/src/component/faq.tsx @@ -13,7 +13,10 @@ export function Faq(props: ParentProps & { question: string }) { fill="currentColor" xmlns="http://www.w3.org/2000/svg" > - + ) { - - + + - + ) { fill-opacity="0.2" /> - - + + @@ -40,9 +61,21 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - - + + + @@ -53,16 +86,40 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - + + - - + + - - + + ) @@ -70,7 +127,14 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { export function IconCopy(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconCheck(props: JSX.SvgSVGAttributes) { return ( - - + + ) } @@ -113,7 +189,14 @@ export function IconStripe(props: JSX.SvgSVGAttributes) { export function IconChevron(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconWorkspaceLogo(props: JSX.SvgSVGAttributes) { return ( - + ) @@ -144,7 +234,10 @@ export function IconOpenAI(props: JSX.SvgSVGAttributes) { export function IconAnthropic(props: JSX.SvgSVGAttributes) { return ( - + {(workspace) => ( - handleSelectWorkspace(workspace.id)}> + handleSelectWorkspace(workspace.id)} + > {workspace.name || workspace.slug} )} @@ -95,7 +98,11 @@ export function WorkspacePicker() { - setStore("showForm", false)} title="Create New Workspace"> + setStore("showForm", false)} + title="Create New Workspace" + >
{key.email} - + {key.timeUsed ? formatDateForTable(key.timeUsed) : "-"} diff --git a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx index 5aa1b969e..4b2a12fdc 100644 --- a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx @@ -85,7 +85,12 @@ const updateMember = action(async (form: FormData) => { ) }, "member.update") -function MemberRow(props: { member: any; workspaceID: string; actorID: string; actorRole: string }) { +function MemberRow(props: { + member: any + workspaceID: string + actorID: string + actorRole: string +}) { const submission = useSubmission(updateMember) const isCurrentUser = () => props.actorID === props.member.id const isAdmin = () => props.actorRole === "admin" diff --git a/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css b/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css index 29f55a977..7a64fd9c7 100644 --- a/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css +++ b/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css @@ -69,4 +69,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css index aaad823ab..bb58df79b 100644 --- a/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css @@ -140,4 +140,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx index 65edc6847..7b949c661 100644 --- a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx @@ -43,15 +43,24 @@ export function NewUserSection() {

Tested & Verified Models

-

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

+

+ 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.

+

+ 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.

+

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

diff --git a/packages/console/app/src/routes/workspace/[id]/provider-section.module.css b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css index 1a450d3dc..1dc7085b7 100644 --- a/packages/console/app/src/routes/workspace/[id]/provider-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css @@ -128,7 +128,6 @@ } @media (max-width: 40rem) { - th, td { padding: var(--space-2) var(--space-3); @@ -136,4 +135,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx index 6ec8477b4..67314fbdc 100644 --- a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx @@ -22,7 +22,9 @@ const removeProvider = action(async (form: FormData) => { if (!provider) return { error: "Provider is required" } const workspaceID = form.get("workspaceID")?.toString() if (!workspaceID) return { error: "Workspace ID is required" } - return json(await withActor(() => Provider.remove({ provider }), workspaceID), { revalidate: listProviders.key }) + return json(await withActor(() => Provider.remove({ provider }), workspaceID), { + revalidate: listProviders.key, + }) }, "provider.remove") const saveProvider = action(async (form: FormData) => { @@ -53,7 +55,10 @@ const listProviders = query(async (workspaceID: string) => { function ProviderRow(props: { provider: Provider }) { const params = useParams() const providers = createAsync(() => listProviders(params.id)) - const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key) + const saveSubmission = useSubmission( + saveProvider, + ([fd]) => fd.get("provider")?.toString() === props.provider.key, + ) const removeSubmission = useSubmission( removeProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key, @@ -89,9 +94,16 @@ function ProviderRow(props: { provider: Provider }) { {providerData() ? maskCredentials(providerData()!.credentials) : "-"}} + fallback={ + {providerData() ? maskCredentials(providerData()!.credentials) : "-"} + } > -
+
(input = r)} diff --git a/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css index 058fbe301..6764a0534 100644 --- a/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css @@ -31,7 +31,7 @@ margin: 0; } - >button { + > button { align-self: flex-start; } } @@ -80,7 +80,7 @@ } } - >button[type="reset"] { + > button[type="reset"] { align-self: flex-start; } @@ -91,4 +91,4 @@ margin-top: calc(var(--space-1) * -1); } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/zen/index.tsx b/packages/console/app/src/routes/zen/index.tsx index 08e58e160..92cc0a508 100644 --- a/packages/console/app/src/routes/zen/index.tsx +++ b/packages/console/app/src/routes/zen/index.tsx @@ -28,7 +28,10 @@ export default function Home() { createAsync(() => checkLoggedIn()) return (
- + OpenCode Zen | A curated set of reliable optimized models for coding agents @@ -44,13 +47,19 @@ export default function Home() { zen logo dark Reliable optimized models for coding agents

- Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically - for coding agents. No need to worry about inconsistent performance and quality, use validated models - that work. + Zen gives you access to a curated set of AI models that OpenCode has tested and + benchmarked specifically for coding agents. No need to worry about inconsistent + performance and quality, use validated models that work.

- +
- - + +
- +
- +
- + Get started with Zen - +

- Add $20 Pay as you go balance (+$1.23 card processing fee) + Add $20 Pay as you go balance{" "} + (+$1.23 card processing fee)

Use with any agent. Set monthly spend limits. Cancel any time.

-
@@ -140,8 +191,8 @@ export default function Home() {

What problem is Zen solving?

- There are so many models available, but only a few work well with coding agents. Most providers - configure them differently with varying results. + There are so many models available, but only a few work well with coding agents. + Most providers configure them differently with varying results.

We're fixing this for everyone, not just OpenCode users.

@@ -176,14 +227,15 @@ export default function Home() {
  • [2]
  • [3]
    - Auto-top up - when your balance reaches $5 we’ll automatically add $20 + Auto-top up - when your balance reaches $5 we’ll automatically + add $20
  • @@ -195,8 +247,9 @@ export default function Home() {
    [*]

    - All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data - for model training, with the following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and + do not use your data for model training, with the{" "} + following exceptions.

    @@ -251,7 +304,8 @@ export default function Home() { ex-Head of Design, Laravel
    - With @OpenCode Zen I know all the models are tested and perfect for coding agents. + With @OpenCode Zen I know all the models are tested and perfect for + coding agents.
    @@ -275,38 +329,44 @@ export default function Home() {
    • - Zen is a curated set of AI models tested and benchmarked for coding agents created by the team behind - OpenCode. + Zen is a curated set of AI models tested and benchmarked for coding agents created + by the team behind OpenCode.
    • - Zen only provides models that have been specifically tested and benchmarked for coding agents. You - wouldn’t use a butter knife to cut steak, don’t use poor models for coding. + Zen only provides models that have been specifically tested and benchmarked for + coding agents. You wouldn’t use a butter knife to cut steak, don’t use poor models + for coding.
    • - Zen is not for profit. Zen passes through the costs from the model providers to you. The higher Zen’s - usage the more OpenCode can negotiate better rates and pass those to you. + Zen is not for profit. Zen passes through the costs from the model providers to + you. The higher Zen’s usage the more OpenCode can negotiate better rates and pass + those to you.
    • - Zen charges per request with zero markups, so you pay exactly what - the model provider charges. Your total cost depends on usage, and you can set monthly spend limits in - your account. To cover costs, OpenCode adds only a small payment processing fee of - $1.23 per $20 balance top-up. + Zen charges per request with zero markups, so you + pay exactly what the model provider charges. Your total cost depends on usage, and + you can set monthly spend limits in your account. To cover + costs, OpenCode adds only a small payment processing fee of $1.23 per $20 balance + top-up.
    • - All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data - for model training, with the following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and + do not use your data for model training, with the{" "} + following exceptions.
    • - Yes, you can set monthly spending limits in your account. + + Yes, you can set monthly spending limits in your account. +
    • @@ -315,8 +375,8 @@ export default function Home() {
    • - While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in - your preferred coding agent. + While Zen works great with OpenCode, you can use Zen with any agent. Follow the + setup instructions in your preferred coding agent.
    diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.ts index 603d8917b..f4e8dc44d 100644 --- a/packages/console/app/src/routes/zen/util/provider/anthropic.ts +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.ts @@ -98,7 +98,10 @@ export function fromAnthropicRequest(body: any): CommonRequest { typeof (src as any).media_type === "string" && typeof (src as any).data === "string" ) - return { type: "image_url", image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` } } + return { + type: "image_url", + image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` }, + } return undefined } @@ -120,12 +123,15 @@ export function fromAnthropicRequest(body: any): CommonRequest { if ((p as any).type === "tool_result") { const id = (p as any).tool_use_id const content = - typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) + typeof (p as any).content === "string" + ? (p as any).content + : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } if (partsOut.length > 0) { - if (partsOut.length === 1 && partsOut[0].type === "text") msgs.push({ role: "user", content: partsOut[0].text }) + if (partsOut.length === 1 && partsOut[0].type === "text") + msgs.push({ role: "user", content: partsOut[0].text }) else msgs.push({ role: "user", content: partsOut }) } continue @@ -137,7 +143,8 @@ export function fromAnthropicRequest(body: any): CommonRequest { const tcs: any[] = [] for (const p of partsIn) { if (!p || !(p as any).type) continue - if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text) + if ((p as any).type === "text" && typeof (p as any).text === "string") + texts.push((p as any).text) if ((p as any).type === "tool_use") { const name = (p as any).name const id = (p as any).id @@ -165,7 +172,11 @@ export function fromAnthropicRequest(body: any): CommonRequest { .filter((t: any) => t && typeof t === "object" && "input_schema" in t) .map((t: any) => ({ type: "function", - function: { name: (t as any).name, description: (t as any).description, parameters: (t as any).input_schema }, + function: { + name: (t as any).name, + description: (t as any).description, + parameters: (t as any).input_schema, + }, })) : undefined @@ -203,7 +214,9 @@ export function fromAnthropicRequest(body: any): CommonRequest { export function toAnthropicRequest(body: CommonRequest) { if (!body || typeof body !== "object") return body - const sysIn = Array.isArray(body.messages) ? body.messages.filter((m: any) => m && m.role === "system") : [] + const sysIn = Array.isArray(body.messages) + ? body.messages.filter((m: any) => m && m.role === "system") + : [] let ccCount = 0 const cc = () => { ccCount++ @@ -354,7 +367,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const idIn = (resp as any).id const id = - typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^msg_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -397,7 +412,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined const cached = - typeof (u as any).cache_read_input_tokens === "number" ? (u as any).cache_read_input_tokens : undefined + typeof (u as any).cache_read_input_tokens === "number" + ? (u as any).cache_read_input_tokens + : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, @@ -452,7 +469,12 @@ export function toAnthropicResponse(resp: CommonResponse) { } catch { input = (tc as any).function.arguments } - content.push({ type: "tool_use", id: (tc as any).id, name: (tc as any).function.name, input }) + content.push({ + type: "tool_use", + id: (tc as any).id, + name: (tc as any).function.name, + input, + }) } } } @@ -511,13 +533,22 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { if (json.type === "content_block_start") { const cb = json.content_block if (cb?.type === "text") { - out.choices.push({ index: json.index ?? 0, delta: { role: "assistant", content: "" }, finish_reason: null }) + out.choices.push({ + index: json.index ?? 0, + delta: { role: "assistant", content: "" }, + finish_reason: null, + }) } else if (cb?.type === "tool_use") { out.choices.push({ index: json.index ?? 0, delta: { tool_calls: [ - { index: json.index ?? 0, id: cb.id, type: "function", function: { name: cb.name, arguments: "" } }, + { + index: json.index ?? 0, + id: cb.id, + type: "function", + function: { name: cb.name, arguments: "" }, + }, ], }, finish_reason: null, @@ -532,7 +563,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { } else if (d?.type === "input_json_delta") { out.choices.push({ index: json.index ?? 0, - delta: { tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }] }, + delta: { + tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }], + }, finish_reason: null, }) } @@ -558,7 +591,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { prompt_tokens: u.input_tokens, completion_tokens: u.output_tokens, total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0), - ...(u.cache_read_input_tokens ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } : {}), + ...(u.cache_read_input_tokens + ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } + : {}), } } diff --git a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts index daf650275..d69985728 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts @@ -57,7 +57,8 @@ export const oaCompatHelper = { const inputTokens = usage.prompt_tokens ?? 0 const outputTokens = usage.completion_tokens ?? 0 const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? undefined - const cacheReadTokens = usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined + const cacheReadTokens = + usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined return { inputTokens: inputTokens - (cacheReadTokens ?? 0), outputTokens, @@ -79,7 +80,8 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) + msgsOut.push({ role: "system", content: m.content }) continue } @@ -90,10 +92,12 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") + parts.push({ type: "text", text: p.text }) if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url }) } - if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -137,7 +141,8 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (p.type === "image_url" && p.image_url) return { type: "image_url", image_url: p.image_url } const s = (p as any).source if (!s || typeof s !== "object") return undefined - if (s.type === "url" && typeof s.url === "string") return { type: "image_url", image_url: { url: s.url } } + if (s.type === "url" && typeof s.url === "string") + return { type: "image_url", image_url: { url: s.url } } if (s.type === "base64" && typeof s.media_type === "string" && typeof s.data === "string") return { type: "image_url", image_url: { url: `data:${s.media_type};base64,${s.data}` } } return undefined @@ -147,7 +152,8 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) + msgsOut.push({ role: "system", content: m.content }) continue } @@ -160,11 +166,13 @@ export function toOaCompatibleRequest(body: CommonRequest) { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") + parts.push({ type: "text", text: p.text }) const ip = toImg(p) if (ip) parts.push(ip) } - if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -317,7 +325,9 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const idIn = (resp as any).id const id = - typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^msg_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -359,7 +369,8 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const pt = typeof u.input_tokens === "number" ? u.input_tokens : undefined const ct = typeof u.output_tokens === "number" ? u.output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined - const cached = typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined + const cached = + typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, @@ -532,7 +543,9 @@ export function toOaCompatibleChunk(chunk: CommonChunk): string { total_tokens: chunk.usage.total_tokens, ...(chunk.usage.prompt_tokens_details?.cached_tokens ? { - prompt_tokens_details: { cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens }, + prompt_tokens_details: { + cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens, + }, } : {}), } diff --git a/packages/console/app/src/routes/zen/util/provider/openai.ts b/packages/console/app/src/routes/zen/util/provider/openai.ts index d17300991..fa0776b7a 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai.ts @@ -77,13 +77,20 @@ export function fromOpenaiRequest(body: any): CommonRequest { typeof (s as any).media_type === "string" && typeof (s as any).data === "string" ) - return { type: "image_url", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } } + return { + type: "image_url", + image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` }, + } return undefined } const msgs: any[] = [] - const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : [] + const inMsgs = Array.isArray(body.input) + ? body.input + : Array.isArray(body.messages) + ? body.messages + : [] for (const m of inMsgs) { if (!m) continue @@ -96,7 +103,9 @@ export function fromOpenaiRequest(body: any): CommonRequest { const args = typeof a === "string" ? a : JSON.stringify(a ?? {}) msgs.push({ role: "assistant", - tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }], + tool_calls: [ + { id: (m as any).id, type: "function", function: { name, arguments: args } }, + ], }) } if ((m as any).type === "function_call_output") { @@ -113,7 +122,8 @@ export function fromOpenaiRequest(body: any): CommonRequest { if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c }) if (Array.isArray(c)) { const t = c.find((p: any) => p && typeof p.text === "string") - if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text }) + if (t && typeof t.text === "string" && t.text.length > 0) + msgs.push({ role: "system", content: t.text }) } continue } @@ -126,18 +136,24 @@ export function fromOpenaiRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of c) { if (!p || !(p as any).type) continue - if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string") + if ( + ((p as any).type === "text" || (p as any).type === "input_text") && + typeof (p as any).text === "string" + ) parts.push({ type: "text", text: (p as any).text }) const ip = toImg(p) if (ip) parts.push(ip) if ((p as any).type === "tool_result") { const id = (p as any).tool_call_id const content = - typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) + typeof (p as any).content === "string" + ? (p as any).content + : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } - if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgs.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgs.push({ role: "user", content: parts }) } continue @@ -153,7 +169,11 @@ export function fromOpenaiRequest(body: any): CommonRequest { } if ((m as any).role === "tool") { - msgs.push({ role: "tool", tool_call_id: (m as any).tool_call_id, content: (m as any).content }) + msgs.push({ + role: "tool", + tool_call_id: (m as any).tool_call_id, + content: (m as any).content, + }) continue } } @@ -210,7 +230,10 @@ export function toOpenaiRequest(body: CommonRequest) { typeof (s as any).media_type === "string" && typeof (s as any).data === "string" ) - return { type: "input_image", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } } + return { + type: "input_image", + image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` }, + } return undefined } @@ -257,7 +280,10 @@ export function toOpenaiRequest(body: CommonRequest) { } if ((m as any).role === "tool") { - const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content) + const out = + typeof (m as any).content === "string" + ? (m as any).content + : JSON.stringify((m as any).content) input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out }) continue } @@ -325,7 +351,9 @@ export function fromOpenaiResponse(resp: any): CommonResponse { const idIn = (r as any).id const id = - typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^resp_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (r as any).model ?? (resp as any).model const out = Array.isArray((r as any).output) ? (r as any).output : [] @@ -452,7 +480,9 @@ export function toOpenaiResponse(resp: CommonResponse) { })() return { - id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`, + id: + (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? + `resp_${Math.random().toString(36).slice(2)}`, object: "response", model: (resp as any).model, output: outputItems, @@ -498,7 +528,9 @@ export function fromOpenaiChunk(chunk: string): CommonChunk | string { if (typeof name === "string" && name.length > 0) { out.choices.push({ index: 0, - delta: { tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }] }, + delta: { + tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }], + }, finish_reason: null, }) } @@ -555,7 +587,12 @@ export function toOpenaiChunk(chunk: CommonChunk): string { const model = chunk.model if (d.content) { - const data = { id, type: "response.output_text.delta", delta: d.content, response: { id, model } } + const data = { + id, + type: "response.output_text.delta", + delta: d.content, + response: { id, model }, + } return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}` } @@ -565,7 +602,13 @@ export function toOpenaiChunk(chunk: CommonChunk): string { const data = { type: "response.output_item.added", output_index: 0, - item: { id: tc.id, type: "function_call", name: tc.function.name, call_id: tc.id, arguments: "" }, + item: { + id: tc.id, + type: "function_call", + name: tc.function.name, + call_id: tc.id, + arguments: "", + }, } return `event: response.output_item.added\ndata: ${JSON.stringify(data)}` } @@ -593,7 +636,11 @@ export function toOpenaiChunk(chunk: CommonChunk): string { } : undefined - const data: any = { id, type: "response.completed", response: { id, model, ...(usage ? { usage } : {}) } } + const data: any = { + id, + type: "response.completed", + response: { id, model, ...(usage ? { usage } : {}) }, + } return `event: response.completed\ndata: ${JSON.stringify(data)}` } diff --git a/packages/console/app/src/style/token/font.css b/packages/console/app/src/style/token/font.css index 67143e662..dc0d298f1 100644 --- a/packages/console/app/src/style/token/font.css +++ b/packages/console/app/src/style/token/font.css @@ -15,6 +15,7 @@ body { --font-size-9xl: 8rem; --font-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; --font-sans: var(--font-mono); } diff --git a/packages/console/app/sst-env.d.ts b/packages/console/app/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/console/app/sst-env.d.ts +++ b/packages/console/app/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/core/migrations/meta/0018_snapshot.json b/packages/console/core/migrations/meta/0018_snapshot.json index 398ba2619..3e3c64c73 100644 --- a/packages/console/core/migrations/meta/0018_snapshot.json +++ b/packages/console/core/migrations/meta/0018_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0019_snapshot.json b/packages/console/core/migrations/meta/0019_snapshot.json index 8f1afeb51..9a0d4d243 100644 --- a/packages/console/core/migrations/meta/0019_snapshot.json +++ b/packages/console/core/migrations/meta/0019_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0020_snapshot.json b/packages/console/core/migrations/meta/0020_snapshot.json index 662093f55..9defceb5a 100644 --- a/packages/console/core/migrations/meta/0020_snapshot.json +++ b/packages/console/core/migrations/meta/0020_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -592,10 +571,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -603,10 +579,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -663,9 +636,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -673,9 +644,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -692,4 +661,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0021_snapshot.json b/packages/console/core/migrations/meta/0021_snapshot.json index b285e34fa..64d3e9d24 100644 --- a/packages/console/core/migrations/meta/0021_snapshot.json +++ b/packages/console/core/migrations/meta/0021_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0022_snapshot.json b/packages/console/core/migrations/meta/0022_snapshot.json index 9486ee345..8a1c4e7d8 100644 --- a/packages/console/core/migrations/meta/0022_snapshot.json +++ b/packages/console/core/migrations/meta/0022_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -613,18 +592,12 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -632,10 +605,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -692,9 +662,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -702,9 +670,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -721,4 +687,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0023_snapshot.json b/packages/console/core/migrations/meta/0023_snapshot.json index 8a40e42a4..4f6f66283 100644 --- a/packages/console/core/migrations/meta/0023_snapshot.json +++ b/packages/console/core/migrations/meta/0023_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -613,32 +592,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -646,10 +615,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -706,9 +672,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -716,9 +680,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -735,4 +697,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0024_snapshot.json b/packages/console/core/migrations/meta/0024_snapshot.json index 4f50945d6..1ef25970a 100644 --- a/packages/console/core/migrations/meta/0024_snapshot.json +++ b/packages/console/core/migrations/meta/0024_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,32 +578,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -632,10 +601,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -692,9 +658,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -702,9 +666,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -721,4 +683,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0025_snapshot.json b/packages/console/core/migrations/meta/0025_snapshot.json index 4b0cef0c0..6746a6e8c 100644 --- a/packages/console/core/migrations/meta/0025_snapshot.json +++ b/packages/console/core/migrations/meta/0025_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,17 +480,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -511,10 +493,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -606,32 +585,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -639,10 +608,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -699,9 +665,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -709,9 +673,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -728,4 +690,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0026_snapshot.json b/packages/console/core/migrations/meta/0026_snapshot.json index 543ab44c3..d3c7dc496 100644 --- a/packages/console/core/migrations/meta/0026_snapshot.json +++ b/packages/console/core/migrations/meta/0026_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,9 +480,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -503,10 +488,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -598,32 +580,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -631,10 +603,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -691,9 +660,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -701,9 +668,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -720,4 +685,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0027_snapshot.json b/packages/console/core/migrations/meta/0027_snapshot.json index 9b6910223..408766f71 100644 --- a/packages/console/core/migrations/meta/0027_snapshot.json +++ b/packages/console/core/migrations/meta/0027_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -584,32 +566,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -617,10 +589,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -677,9 +646,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -687,9 +654,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -706,4 +671,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0028_snapshot.json b/packages/console/core/migrations/meta/0028_snapshot.json index 8242ae52d..827cb53c5 100644 --- a/packages/console/core/migrations/meta/0028_snapshot.json +++ b/packages/console/core/migrations/meta/0028_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -584,32 +566,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -617,10 +589,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -677,9 +646,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -687,9 +654,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -706,4 +671,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0029_snapshot.json b/packages/console/core/migrations/meta/0029_snapshot.json index 959004f33..d235697cc 100644 --- a/packages/console/core/migrations/meta/0029_snapshot.json +++ b/packages/console/core/migrations/meta/0029_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -605,32 +587,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -638,10 +610,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -698,9 +667,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -708,9 +675,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -727,4 +692,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0030_snapshot.json b/packages/console/core/migrations/meta/0030_snapshot.json index 6a6eb38cb..66978dfa5 100644 --- a/packages/console/core/migrations/meta/0030_snapshot.json +++ b/packages/console/core/migrations/meta/0030_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -549,10 +531,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -560,10 +539,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -676,32 +652,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -709,10 +675,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -769,9 +732,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -779,9 +740,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -798,4 +757,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0031_snapshot.json b/packages/console/core/migrations/meta/0031_snapshot.json index ba964881d..c47165925 100644 --- a/packages/console/core/migrations/meta/0031_snapshot.json +++ b/packages/console/core/migrations/meta/0031_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -549,10 +531,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -560,10 +539,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -627,10 +603,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -638,10 +611,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -754,32 +724,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -787,10 +747,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -847,9 +804,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -857,9 +812,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -876,4 +829,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0032_snapshot.json b/packages/console/core/migrations/meta/0032_snapshot.json index 344fde6fd..51e84a1d3 100644 --- a/packages/console/core/migrations/meta/0032_snapshot.json +++ b/packages/console/core/migrations/meta/0032_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -405,10 +395,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,9 +473,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -496,10 +481,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -556,10 +538,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -567,10 +546,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -634,10 +610,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -645,10 +618,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -761,32 +731,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -794,10 +754,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -854,9 +811,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -864,9 +819,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -883,4 +836,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0033_snapshot.json b/packages/console/core/migrations/meta/0033_snapshot.json index eb682adca..76d4720e8 100644 --- a/packages/console/core/migrations/meta/0033_snapshot.json +++ b/packages/console/core/migrations/meta/0033_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -187,9 +185,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -197,10 +193,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -287,10 +280,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -412,10 +402,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,9 +480,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -503,10 +488,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -563,10 +545,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -574,10 +553,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -641,10 +617,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -652,10 +625,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -768,32 +738,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -801,10 +761,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -861,9 +818,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -871,9 +826,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -890,4 +843,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0034_snapshot.json b/packages/console/core/migrations/meta/0034_snapshot.json index 36acbdef4..e9c999be7 100644 --- a/packages/console/core/migrations/meta/0034_snapshot.json +++ b/packages/console/core/migrations/meta/0034_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -117,10 +115,7 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true } }, @@ -257,9 +252,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -267,10 +260,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -357,10 +347,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -482,10 +469,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -563,9 +547,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -573,10 +555,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -633,10 +612,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -644,10 +620,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -711,10 +684,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -722,10 +692,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -838,32 +805,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -871,10 +828,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -931,9 +885,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -941,9 +893,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -960,4 +910,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0035_snapshot.json b/packages/console/core/migrations/meta/0035_snapshot.json index 7478337b5..815d120ea 100644 --- a/packages/console/core/migrations/meta/0035_snapshot.json +++ b/packages/console/core/migrations/meta/0035_snapshot.json @@ -102,17 +102,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -249,9 +244,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -259,10 +252,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -349,10 +339,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -474,10 +461,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -555,9 +539,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -565,10 +547,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -625,10 +604,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -636,10 +612,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -703,10 +676,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -714,10 +684,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -830,32 +797,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -863,10 +820,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -923,9 +877,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -933,9 +885,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -952,4 +902,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0036_snapshot.json b/packages/console/core/migrations/meta/0036_snapshot.json index b030e30ea..926b143eb 100644 --- a/packages/console/core/migrations/meta/0036_snapshot.json +++ b/packages/console/core/migrations/meta/0036_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -263,9 +254,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -273,10 +262,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -363,10 +349,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -488,10 +471,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -569,9 +549,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -579,10 +557,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -639,10 +614,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -650,10 +622,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -717,10 +686,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -728,10 +694,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -844,32 +807,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -877,10 +830,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -937,9 +887,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -947,9 +895,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -966,4 +912,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0037_snapshot.json b/packages/console/core/migrations/meta/0037_snapshot.json index 690bae87a..8a80ea522 100644 --- a/packages/console/core/migrations/meta/0037_snapshot.json +++ b/packages/console/core/migrations/meta/0037_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -277,9 +268,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -287,10 +276,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -377,10 +363,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -502,10 +485,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -583,9 +563,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -593,10 +571,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -653,10 +628,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -664,10 +636,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -731,10 +700,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -742,10 +708,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -858,32 +821,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -891,10 +844,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -951,9 +901,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -961,9 +909,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -980,4 +926,4 @@ "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 f2c6c6fc5..250fe59b3 100644 --- a/packages/console/core/migrations/meta/_journal.json +++ b/packages/console/core/migrations/meta/_journal.json @@ -269,4 +269,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/console/core/src/aws.ts b/packages/console/core/src/aws.ts index e87ada6ef..ce4a20f44 100644 --- a/packages/console/core/src/aws.ts +++ b/packages/console/core/src/aws.ts @@ -24,37 +24,40 @@ export namespace AWS { body: z.string(), }), async (input) => { - const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", { - method: "POST", - headers: { - "X-Amz-Target": "SES.SendEmail", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - FromEmailAddress: `OpenCode Zen `, - Destination: { - ToAddresses: [input.to], + const res = await createClient().fetch( + "https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", + { + method: "POST", + headers: { + "X-Amz-Target": "SES.SendEmail", + "Content-Type": "application/json", }, - Content: { - Simple: { - Subject: { - Charset: "UTF-8", - Data: input.subject, - }, - Body: { - Text: { + body: JSON.stringify({ + FromEmailAddress: `OpenCode Zen `, + Destination: { + ToAddresses: [input.to], + }, + Content: { + Simple: { + Subject: { Charset: "UTF-8", - Data: input.body, + Data: input.subject, }, - Html: { - Charset: "UTF-8", - Data: input.body, + Body: { + Text: { + Charset: "UTF-8", + Data: input.body, + }, + Html: { + Charset: "UTF-8", + Data: input.body, + }, }, }, }, - }, - }), - }) + }), + }, + ) if (!res.ok) { throw new Error(`Failed to send email: ${res.statusText}`) } diff --git a/packages/console/core/src/drizzle/index.ts b/packages/console/core/src/drizzle/index.ts index f0f065de4..8b37b1f9c 100644 --- a/packages/console/core/src/drizzle/index.ts +++ b/packages/console/core/src/drizzle/index.ts @@ -5,7 +5,10 @@ import { Client } from "@planetscale/database" import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core" import type { ExtractTablesWithRelations } from "drizzle-orm" -import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless" +import type { + PlanetScalePreparedQueryHKT, + PlanetscaleQueryResultHKT, +} from "drizzle-orm/planetscale-serverless" import { Context } from "../context" import { memo } from "../util/memo" @@ -67,7 +70,10 @@ export namespace Database { } } - export async function transaction(callback: (tx: TxOrDb) => Promise, config?: MySqlTransactionConfig) { + export async function transaction( + callback: (tx: TxOrDb) => Promise, + config?: MySqlTransactionConfig, + ) { try { const { tx } = TransactionContext.use() return callback(tx) diff --git a/packages/console/core/src/key.ts b/packages/console/core/src/key.ts index 688f19b3d..6396fd0b8 100644 --- a/packages/console/core/src/key.ts +++ b/packages/console/core/src/key.ts @@ -20,8 +20,14 @@ export namespace Key { email: AuthTable.subject, }) .from(KeyTable) - .innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID))) - .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .innerJoin( + UserTable, + and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)), + ) + .innerJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where( and( ...[ diff --git a/packages/console/core/src/provider.ts b/packages/console/core/src/provider.ts index cf2040b59..0af642f71 100644 --- a/packages/console/core/src/provider.ts +++ b/packages/console/core/src/provider.ts @@ -11,7 +11,9 @@ export namespace Provider { tx .select() .from(ProviderTable) - .where(and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted))), + .where( + and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted)), + ), ), ) @@ -50,7 +52,12 @@ export namespace Provider { return Database.transaction((tx) => tx .delete(ProviderTable) - .where(and(eq(ProviderTable.provider, provider), eq(ProviderTable.workspaceID, Actor.workspace()))), + .where( + and( + eq(ProviderTable.provider, provider), + eq(ProviderTable.workspaceID, Actor.workspace()), + ), + ), ) }, ) diff --git a/packages/console/core/src/schema/auth.sql.ts b/packages/console/core/src/schema/auth.sql.ts index 27c926d6f..d55e605aa 100644 --- a/packages/console/core/src/schema/auth.sql.ts +++ b/packages/console/core/src/schema/auth.sql.ts @@ -1,4 +1,11 @@ -import { index, mysqlEnum, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core" +import { + index, + mysqlEnum, + mysqlTable, + primaryKey, + uniqueIndex, + varchar, +} from "drizzle-orm/mysql-core" import { id, timestamps, ulid } from "../drizzle/types" export const AuthProvider = ["email", "github", "google"] as const diff --git a/packages/console/core/src/schema/model.sql.ts b/packages/console/core/src/schema/model.sql.ts index 1c032aad2..343b0c4f3 100644 --- a/packages/console/core/src/schema/model.sql.ts +++ b/packages/console/core/src/schema/model.sql.ts @@ -9,5 +9,8 @@ export const ModelTable = mysqlTable( ...timestamps, model: varchar("model", { length: 64 }).notNull(), }, - (table) => [...workspaceIndexes(table), uniqueIndex("model_workspace_model").on(table.workspaceID, table.model)], + (table) => [ + ...workspaceIndexes(table), + uniqueIndex("model_workspace_model").on(table.workspaceID, table.model), + ], ) diff --git a/packages/console/core/src/schema/provider.sql.ts b/packages/console/core/src/schema/provider.sql.ts index 11be5b4d7..04d11e2e5 100644 --- a/packages/console/core/src/schema/provider.sql.ts +++ b/packages/console/core/src/schema/provider.sql.ts @@ -10,5 +10,8 @@ export const ProviderTable = mysqlTable( provider: varchar("provider", { length: 64 }).notNull(), credentials: text("credentials").notNull(), }, - (table) => [...workspaceIndexes(table), uniqueIndex("workspace_provider").on(table.workspaceID, table.provider)], + (table) => [ + ...workspaceIndexes(table), + uniqueIndex("workspace_provider").on(table.workspaceID, table.provider), + ], ) diff --git a/packages/console/core/src/schema/user.sql.ts b/packages/console/core/src/schema/user.sql.ts index 7fd7f5e1e..ce5b6c53e 100644 --- a/packages/console/core/src/schema/user.sql.ts +++ b/packages/console/core/src/schema/user.sql.ts @@ -1,4 +1,12 @@ -import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index, bigint } from "drizzle-orm/mysql-core" +import { + mysqlTable, + uniqueIndex, + varchar, + int, + mysqlEnum, + index, + bigint, +} from "drizzle-orm/mysql-core" import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types" import { workspaceIndexes } from "./workspace.sql" diff --git a/packages/console/core/src/user.ts b/packages/console/core/src/user.ts index 8b7a96f44..cbb1ac827 100644 --- a/packages/console/core/src/user.ts +++ b/packages/console/core/src/user.ts @@ -26,7 +26,10 @@ export namespace User { authEmail: AuthTable.subject, }) .from(UserTable) - .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .leftJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where(and(eq(UserTable.workspaceID, Actor.workspace()), isNull(UserTable.timeDeleted))), ), ) @@ -36,7 +39,13 @@ export namespace User { tx .select() .from(UserTable) - .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id), isNull(UserTable.timeDeleted))) + .where( + and( + eq(UserTable.workspaceID, Actor.workspace()), + eq(UserTable.id, id), + isNull(UserTable.timeDeleted), + ), + ) .then((rows) => rows[0]), ), ) @@ -48,7 +57,10 @@ export namespace User { email: AuthTable.subject, }) .from(UserTable) - .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .leftJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id))) .then((rows) => rows[0]?.email), ), @@ -130,10 +142,16 @@ export namespace User { workspaceName: WorkspaceTable.name, }) .from(UserTable) - .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .innerJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, workspaceID)) .where( - and(eq(UserTable.workspaceID, workspaceID), eq(UserTable.id, Actor.assert("user").properties.userID)), + and( + eq(UserTable.workspaceID, workspaceID), + eq(UserTable.id, Actor.assert("user").properties.userID), + ), ) .then((rows) => rows[0]), ) diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string + Console: { + type: "sst.cloudflare.SolidStart" + url: string } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string } - "STRIPE_SECRET_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 + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string + Web: { + type: "sst.cloudflare.Astro" + url: string } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS2: { + 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 {} \ No newline at end of file +export {} diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string + Console: { + type: "sst.cloudflare.SolidStart" + url: string } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string } - "STRIPE_SECRET_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 + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string + Web: { + type: "sst.cloudflare.Astro" + url: string } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS2: { + 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 {} \ No newline at end of file +export {} diff --git a/packages/console/mail/emails/templates/InviteEmail.tsx b/packages/console/mail/emails/templates/InviteEmail.tsx index baf0d383f..e94eb564c 100644 --- a/packages/console/mail/emails/templates/InviteEmail.tsx +++ b/packages/console/mail/emails/templates/InviteEmail.tsx @@ -1,6 +1,18 @@ // @ts-nocheck import React from "react" -import { Img, Row, Html, Link, Body, Head, Button, Column, Preview, Section, Container } from "@jsx-email/all" +import { + Img, + Row, + Html, + Link, + Body, + Head, + Button, + Column, + Preview, + Section, + Container, +} from "@jsx-email/all" import { Text, Fonts, Title, A, Span } from "../components" import { unit, @@ -52,8 +64,8 @@ export const InviteEmail = ({
    Join your team's OpenCode workspace - You have been invited by {inviter} to join the{" "} - {workspaceName} workspace on OpenCode. + You have been invited by {inviter} to join + the {workspaceName} workspace on OpenCode.
    @@ -61,7 +73,12 @@ export const InviteEmail = ({ diff --git a/packages/console/mail/sst-env.d.ts b/packages/console/mail/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/console/mail/sst-env.d.ts +++ b/packages/console/mail/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string + Console: { + type: "sst.cloudflare.SolidStart" + url: string } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string } - "STRIPE_SECRET_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 + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string + Web: { + type: "sst.cloudflare.Astro" + url: string } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS2: { + 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 {} \ No newline at end of file +export {} diff --git a/packages/desktop/src/sst-env.d.ts b/packages/desktop/src/sst-env.d.ts index 47a8fbec7..1b1683a1e 100644 --- a/packages/desktop/src/sst-env.d.ts +++ b/packages/desktop/src/sst-env.d.ts @@ -2,9 +2,7 @@ /* tslint:disable */ /* eslint-disable */ /// -interface ImportMetaEnv { - -} +interface ImportMetaEnv {} interface ImportMeta { readonly env: ImportMetaEnv -} \ No newline at end of file +} diff --git a/packages/desktop/sst-env.d.ts b/packages/desktop/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/desktop/sst-env.d.ts +++ b/packages/desktop/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts index 6f00dae9a..3475f5d72 100644 --- a/packages/function/src/api.ts +++ b/packages/function/src/api.ts @@ -268,7 +268,11 @@ export default new Hono<{ Bindings: Env }>() // Verify permissions const userClient = new Octokit({ auth: token }) const { data: repoData } = await userClient.repos.get({ owner, repo }) - if (!repoData.permissions.admin && !repoData.permissions.push && !repoData.permissions.maintain) + if ( + !repoData.permissions.admin && + !repoData.permissions.push && + !repoData.permissions.maintain + ) throw new Error("User does not have write permissions") // Get installation token diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string + Console: { + type: "sst.cloudflare.SolidStart" + url: string } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string } - "STRIPE_SECRET_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 + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string + Web: { + type: "sst.cloudflare.Astro" + url: string } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ZEN_MODELS2: { + 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 {} \ No newline at end of file +export {} diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index b875d158f..41865273d 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -77,7 +77,8 @@ async function regenerateWindowsCmdWrappers() { // npm_config_global is string | undefined // if it exists, the value is true - const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) + const isGlobal = + process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) // The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links // We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts @@ -93,7 +94,9 @@ async function regenerateWindowsCmdWrappers() { console.log("Successfully rebuilt npm bin links") } catch (error) { console.error("Error rebuilding npm links:", error.message) - console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts") + console.error( + "npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts", + ) } } diff --git a/packages/opencode/script/schema.ts b/packages/opencode/script/schema.ts index 585701c95..48bf65442 100755 --- a/packages/opencode/script/schema.ts +++ b/packages/opencode/script/schema.ts @@ -19,12 +19,23 @@ const result = z.toJSONSchema(Config.Info, { const schema = ctx.jsonSchema // Preserve strictness: set additionalProperties: false for objects - if (schema && typeof schema === "object" && schema.type === "object" && schema.additionalProperties === undefined) { + if ( + schema && + typeof schema === "object" && + schema.type === "object" && + schema.additionalProperties === undefined + ) { schema.additionalProperties = false } // Add examples and default descriptions for string fields with defaults - if (schema && typeof schema === "object" && "type" in schema && schema.type === "string" && schema?.default) { + if ( + schema && + typeof schema === "object" && + "type" in schema && + schema.type === "string" && + schema?.default + ) { if (!schema.examples) { schema.examples = [schema.default] } diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index a6933708b..16f401629 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -143,7 +143,18 @@ export namespace Agent { tools: {}, builtIn: false, } - const { name, model, prompt, tools, description, temperature, top_p, mode, permission, ...extra } = value + const { + name, + model, + prompt, + tools, + description, + temperature, + top_p, + mode, + permission, + ...extra + } = value item.options = { ...item.options, ...extra, @@ -212,7 +223,10 @@ export namespace Agent { } } -function mergeAgentPermissions(basePermission: any, overridePermission: any): Agent.Info["permission"] { +function mergeAgentPermissions( + basePermission: any, + overridePermission: any, +): Agent.Info["permission"] { if (typeof basePermission.bash === "string") { basePermission.bash = { "*": basePermission.bash, diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index f4dd3ed2c..c424eb879 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -19,7 +19,10 @@ export namespace Bus { const registry = new Map() - export function event(type: Type, properties: Properties) { + export function event( + type: Type, + properties: Properties, + ) { const result = { type, properties, @@ -70,7 +73,10 @@ export namespace Bus { export function subscribe( def: Definition, - callback: (event: { type: Definition["type"]; properties: z.infer }) => void, + callback: (event: { + type: Definition["type"] + properties: z.infer + }) => void, ) { return raw(def.type, callback) } diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index aa833e977..b4c47f0a4 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -14,7 +14,11 @@ export const AuthCommand = cmd({ command: "auth", describe: "manage credentials", builder: (yargs) => - yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(), + yargs + .command(AuthLoginCommand) + .command(AuthLogoutCommand) + .command(AuthListCommand) + .demandCommand(), async handler() {}, }) @@ -60,7 +64,9 @@ export const AuthListCommand = cmd({ prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`) } - prompts.outro(`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s")) + prompts.outro( + `${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"), + ) } }, }) @@ -80,7 +86,9 @@ export const AuthLoginCommand = cmd({ UI.empty() prompts.intro("Add credential") if (args.url) { - const wellknown = await fetch(`${args.url}/.well-known/opencode`).then((x) => x.json() as any) + const wellknown = await fetch(`${args.url}/.well-known/opencode`).then( + (x) => x.json() as any, + ) prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``) const proc = Bun.spawn({ cmd: wellknown.auth.command, @@ -102,223 +110,224 @@ export const AuthLoginCommand = cmd({ prompts.outro("Done") return } - await ModelsDev.refresh().catch(() => {}) - const providers = await ModelsDev.get() - const priority: Record = { - opencode: 0, - anthropic: 1, - "github-copilot": 2, - openai: 3, - google: 4, - openrouter: 5, - vercel: 6, - } - let provider = await prompts.autocomplete({ - message: "Select provider", - maxItems: 8, - options: [ - ...pipe( - providers, - values(), - sortBy( - (x) => priority[x.id] ?? 99, - (x) => x.name ?? x.id, - ), - map((x) => ({ - label: x.name, - value: x.id, - hint: priority[x.id] <= 1 ? "recommended" : undefined, - })), - ), - { - value: "other", - label: "Other", - }, - ], - }) - - if (prompts.isCancel(provider)) throw new UI.CancelledError() - - const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) - if (plugin && plugin.auth) { - let index = 0 - if (plugin.auth.methods.length > 1) { - const method = await prompts.select({ - message: "Login method", - options: [ - ...plugin.auth.methods.map((x, index) => ({ - label: x.label, - value: index.toString(), + await ModelsDev.refresh().catch(() => {}) + const providers = await ModelsDev.get() + const priority: Record = { + opencode: 0, + anthropic: 1, + "github-copilot": 2, + openai: 3, + google: 4, + openrouter: 5, + vercel: 6, + } + let provider = await prompts.autocomplete({ + message: "Select provider", + maxItems: 8, + options: [ + ...pipe( + providers, + values(), + sortBy( + (x) => priority[x.id] ?? 99, + (x) => x.name ?? x.id, + ), + map((x) => ({ + label: x.name, + value: x.id, + hint: priority[x.id] <= 1 ? "recommended" : undefined, })), - ], - }) - if (prompts.isCancel(method)) throw new UI.CancelledError() - index = parseInt(method) - } - const method = plugin.auth.methods[index] + ), + { + value: "other", + label: "Other", + }, + ], + }) - // Handle prompts for all auth types - await new Promise((resolve) => setTimeout(resolve, 10)) - const inputs: Record = {} - if (method.prompts) { - for (const prompt of method.prompts) { - if (prompt.condition && !prompt.condition(inputs)) { - continue - } - if (prompt.type === "select") { - const value = await prompts.select({ - message: prompt.message, - options: prompt.options, - }) - if (prompts.isCancel(value)) throw new UI.CancelledError() - inputs[prompt.key] = value - } else { - const value = await prompts.text({ - message: prompt.message, - placeholder: prompt.placeholder, - validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined, - }) - if (prompts.isCancel(value)) throw new UI.CancelledError() - inputs[prompt.key] = value - } - } - } + if (prompts.isCancel(provider)) throw new UI.CancelledError() - if (method.type === "oauth") { - const authorize = await method.authorize(inputs) - - if (authorize.url) { - prompts.log.info("Go to: " + authorize.url) - } - - if (authorize.method === "auto") { - if (authorize.instructions) { - prompts.log.info(authorize.instructions) - } - const spinner = prompts.spinner() - spinner.start("Waiting for authorization...") - const result = await authorize.callback() - if (result.type === "failed") { - spinner.stop("Failed to authorize", 1) - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - if ("refresh" in result) { - const { type: _, provider: __, refresh, access, expires, ...extraFields } = result - await Auth.set(saveProvider, { - type: "oauth", - refresh, - access, - expires, - ...extraFields, - }) - } - if ("key" in result) { - await Auth.set(saveProvider, { - type: "api", - key: result.key, - }) - } - spinner.stop("Login successful") - } - } - - if (authorize.method === "code") { - const code = await prompts.text({ - message: "Paste the authorization code here: ", - validate: (x) => (x && x.length > 0 ? undefined : "Required"), + const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) + if (plugin && plugin.auth) { + let index = 0 + if (plugin.auth.methods.length > 1) { + const method = await prompts.select({ + message: "Login method", + options: [ + ...plugin.auth.methods.map((x, index) => ({ + label: x.label, + value: index.toString(), + })), + ], }) - if (prompts.isCancel(code)) throw new UI.CancelledError() - const result = await authorize.callback(code) - if (result.type === "failed") { - prompts.log.error("Failed to authorize") - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - if ("refresh" in result) { - const { type: _, provider: __, refresh, access, expires, ...extraFields } = result - await Auth.set(saveProvider, { - type: "oauth", - refresh, - access, - expires, - ...extraFields, - }) + if (prompts.isCancel(method)) throw new UI.CancelledError() + index = parseInt(method) + } + const method = plugin.auth.methods[index] + + // Handle prompts for all auth types + await new Promise((resolve) => setTimeout(resolve, 10)) + const inputs: Record = {} + if (method.prompts) { + for (const prompt of method.prompts) { + if (prompt.condition && !prompt.condition(inputs)) { + continue } - if ("key" in result) { + if (prompt.type === "select") { + const value = await prompts.select({ + message: prompt.message, + options: prompt.options, + }) + if (prompts.isCancel(value)) throw new UI.CancelledError() + inputs[prompt.key] = value + } else { + const value = await prompts.text({ + message: prompt.message, + placeholder: prompt.placeholder, + validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined, + }) + if (prompts.isCancel(value)) throw new UI.CancelledError() + inputs[prompt.key] = value + } + } + } + + if (method.type === "oauth") { + const authorize = await method.authorize(inputs) + + if (authorize.url) { + prompts.log.info("Go to: " + authorize.url) + } + + if (authorize.method === "auto") { + if (authorize.instructions) { + prompts.log.info(authorize.instructions) + } + const spinner = prompts.spinner() + spinner.start("Waiting for authorization...") + const result = await authorize.callback() + if (result.type === "failed") { + spinner.stop("Failed to authorize", 1) + } + if (result.type === "success") { + const saveProvider = result.provider ?? provider + if ("refresh" in result) { + const { type: _, provider: __, refresh, access, expires, ...extraFields } = result + await Auth.set(saveProvider, { + type: "oauth", + refresh, + access, + expires, + ...extraFields, + }) + } + if ("key" in result) { + await Auth.set(saveProvider, { + type: "api", + key: result.key, + }) + } + spinner.stop("Login successful") + } + } + + if (authorize.method === "code") { + const code = await prompts.text({ + message: "Paste the authorization code here: ", + validate: (x) => (x && x.length > 0 ? undefined : "Required"), + }) + if (prompts.isCancel(code)) throw new UI.CancelledError() + const result = await authorize.callback(code) + if (result.type === "failed") { + prompts.log.error("Failed to authorize") + } + if (result.type === "success") { + const saveProvider = result.provider ?? provider + if ("refresh" in result) { + const { type: _, provider: __, refresh, access, expires, ...extraFields } = result + await Auth.set(saveProvider, { + type: "oauth", + refresh, + access, + expires, + ...extraFields, + }) + } + if ("key" in result) { + await Auth.set(saveProvider, { + type: "api", + key: result.key, + }) + } + prompts.log.success("Login successful") + } + } + + prompts.outro("Done") + return + } + + if (method.type === "api") { + if (method.authorize) { + const result = await method.authorize(inputs) + if (result.type === "failed") { + prompts.log.error("Failed to authorize") + } + if (result.type === "success") { + const saveProvider = result.provider ?? provider await Auth.set(saveProvider, { type: "api", key: result.key, }) + prompts.log.success("Login successful") } - prompts.log.success("Login successful") + prompts.outro("Done") + return } } + } + if (provider === "other") { + provider = await prompts.text({ + message: "Enter provider id", + validate: (x) => + x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only", + }) + if (prompts.isCancel(provider)) throw new UI.CancelledError() + provider = provider.replace(/^@ai-sdk\//, "") + if (prompts.isCancel(provider)) throw new UI.CancelledError() + prompts.log.warn( + `This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`, + ) + } + + if (provider === "amazon-bedrock") { + prompts.log.info( + "Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID", + ) prompts.outro("Done") return } - if (method.type === "api") { - if (method.authorize) { - const result = await method.authorize(inputs) - if (result.type === "failed") { - prompts.log.error("Failed to authorize") - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - await Auth.set(saveProvider, { - type: "api", - key: result.key, - }) - prompts.log.success("Login successful") - } - prompts.outro("Done") - return - } + if (provider === "opencode") { + prompts.log.info("Create an api key at https://opencode.ai/auth") } - } - if (provider === "other") { - provider = await prompts.text({ - message: "Enter provider id", - validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), + if (provider === "vercel") { + prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token") + } + + const key = await prompts.password({ + message: "Enter your API key", + validate: (x) => (x && x.length > 0 ? undefined : "Required"), + }) + if (prompts.isCancel(key)) throw new UI.CancelledError() + await Auth.set(provider, { + type: "api", + key, }) - if (prompts.isCancel(provider)) throw new UI.CancelledError() - provider = provider.replace(/^@ai-sdk\//, "") - if (prompts.isCancel(provider)) throw new UI.CancelledError() - prompts.log.warn( - `This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`, - ) - } - if (provider === "amazon-bedrock") { - prompts.log.info( - "Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID", - ) prompts.outro("Done") - return - } - - if (provider === "opencode") { - prompts.log.info("Create an api key at https://opencode.ai/auth") - } - - if (provider === "vercel") { - prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token") - } - - const key = await prompts.password({ - message: "Enter your API key", - validate: (x) => (x && x.length > 0 ? undefined : "Required"), - }) - if (prompts.isCancel(key)) throw new UI.CancelledError() - await Auth.set(provider, { - type: "api", - key, - }) - - prompts.outro("Done") }, }) }, diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index 2f5977195..8492395d1 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -7,7 +7,11 @@ import { EOL } from "os" export const LSPCommand = cmd({ command: "lsp", builder: (yargs) => - yargs.command(DiagnosticsCommand).command(SymbolsCommand).command(DocumentSymbolsCommand).demandCommand(), + yargs + .command(DiagnosticsCommand) + .command(SymbolsCommand) + .command(DocumentSymbolsCommand) + .demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/debug/ripgrep.ts b/packages/opencode/src/cli/cmd/debug/ripgrep.ts index 66cfba20d..7c1d0d96a 100644 --- a/packages/opencode/src/cli/cmd/debug/ripgrep.ts +++ b/packages/opencode/src/cli/cmd/debug/ripgrep.ts @@ -6,7 +6,8 @@ import { cmd } from "../cmd" export const RipgrepCommand = cmd({ command: "rg", - builder: (yargs) => yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), + builder: (yargs) => + yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), async handler() {}, }) @@ -18,7 +19,9 @@ const TreeCommand = cmd({ }), async handler(args) { await bootstrap(process.cwd(), async () => { - process.stdout.write(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit }) + EOL) + process.stdout.write( + (await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit })) + EOL, + ) }) }, }) diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts index 1849fe270..b114122b7 100644 --- a/packages/opencode/src/cli/cmd/debug/snapshot.ts +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -4,7 +4,8 @@ import { cmd } from "../cmd" export const SnapshotCommand = cmd({ command: "snapshot", - builder: (yargs) => yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), + builder: (yargs) => + yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/generate.ts b/packages/opencode/src/cli/cmd/generate.ts index 0cefb2533..c29a22a82 100644 --- a/packages/opencode/src/cli/cmd/generate.ts +++ b/packages/opencode/src/cli/cmd/generate.ts @@ -6,7 +6,7 @@ export const GenerateCommand = { handler: async () => { const specs = await Server.openapi() const json = JSON.stringify(specs, null, 2) - + // Wait for stdout to finish writing before process.exit() is called await new Promise((resolve, reject) => { process.stdout.write(json, (err) => { diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index cd3ceb94b..6fbeee2ea 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -189,7 +189,9 @@ export const GithubInstallCommand = cmd({ async function getAppInfo() { const project = Instance.project if (project.vcs !== "git") { - prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) + prompts.log.error( + `Could not find git repository. Please run this command from a git repository.`, + ) throw new UI.CancelledError() } @@ -202,9 +204,13 @@ export const GithubInstallCommand = cmd({ // ie. git@github.com:sst/opencode // ie. ssh://git@github.com/sst/opencode.git // ie. ssh://git@github.com/sst/opencode - const parsed = info.match(/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/) + const parsed = info.match( + /^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/, + ) if (!parsed) { - prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) + prompts.log.error( + `Could not find git repository. Please run this command from a git repository.`, + ) throw new UI.CancelledError() } const [, owner, repo] = parsed @@ -445,7 +451,9 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToLocalBranch(summary) } - const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) + const hasShared = prData.comments.nodes.some((c) => + c.body.includes(`${shareBaseUrl}/s/${shareId}`), + ) await updateComment(`${response}${footer({ image: !hasShared })}`) } // Fork PR @@ -457,7 +465,9 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToForkBranch(summary, prData) } - const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) + const hasShared = prData.comments.nodes.some((c) => + c.body.includes(`${shareBaseUrl}/s/${shareId}`), + ) await updateComment(`${response}${footer({ image: !hasShared })}`) } } @@ -547,8 +557,12 @@ export const GithubRunCommand = cmd({ // ie. Image // ie. [api.json](https://github.com/user-attachments/files/21433810/api.json) // ie. ![Image](https://github.com/user-attachments/assets/xxxx) - const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi) - const tagMatches = prompt.matchAll(//gi) + const mdMatches = prompt.matchAll( + /!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi, + ) + const tagMatches = prompt.matchAll( + //gi, + ) const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index) console.log("Images", JSON.stringify(matches, null, 2)) @@ -573,7 +587,10 @@ export const GithubRunCommand = cmd({ // Replace img tag with file path, ie. @image.png const replacement = `@${filename}` - prompt = prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length) + prompt = + prompt.slice(0, start + offset) + + replacement + + prompt.slice(start + offset + tag.length) offset += replacement.length - tag.length const contentType = res.headers.get("content-type") @@ -856,7 +873,8 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` throw new Error(`Failed to check permissions for user ${actor}: ${error}`) } - if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`) + if (!["admin", "write"].includes(permission)) + throw new Error(`User ${actor} does not have write permissions`) } async function createComment() { @@ -904,7 +922,9 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` return `${titleAlt}\n` })() - const shareUrl = shareId ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` : "" + const shareUrl = shareId + ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` + : "" return `\n\n${image}${shareUrl}[github run](${runUrl})` } @@ -1080,9 +1100,13 @@ query($owner: String!, $repo: String!, $number: Int!) { }) .map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`) - const files = (pr.files.nodes || []).map((f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`) + const files = (pr.files.nodes || []).map( + (f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`, + ) const reviewData = (pr.reviews.nodes || []).map((r) => { - const comments = (r.comments.nodes || []).map((c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`) + const comments = (r.comments.nodes || []).map( + (c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`, + ) return [ `- ${r.author.login} at ${r.submittedAt}:`, ` - Review body: ${r.body}`, @@ -1104,9 +1128,15 @@ query($owner: String!, $repo: String!, $number: Int!) { `Deletions: ${pr.deletions}`, `Total Commits: ${pr.commits.totalCount}`, `Changed Files: ${pr.files.nodes.length} files`, - ...(comments.length > 0 ? ["", ...comments, ""] : []), - ...(files.length > 0 ? ["", ...files, ""] : []), - ...(reviewData.length > 0 ? ["", ...reviewData, ""] : []), + ...(comments.length > 0 + ? ["", ...comments, ""] + : []), + ...(files.length > 0 + ? ["", ...files, ""] + : []), + ...(reviewData.length > 0 + ? ["", ...reviewData, ""] + : []), "", ].join("\n") } diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 39b0a55fd..9add4bff7 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -137,7 +137,9 @@ export const RunCommand = cmd({ const outputJsonEvent = (type: string, data: any) => { if (args.format === "json") { - process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL) + process.stdout.write( + JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL, + ) return true } return false @@ -157,7 +159,9 @@ export const RunCommand = cmd({ const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] const title = part.state.title || - (Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown") + (Object.keys(part.state.input).length > 0 + ? JSON.stringify(part.state.input) + : "Unknown") printEvent(color, tool, title) if (part.tool === "bash" && part.state.output?.trim()) { UI.println() @@ -280,7 +284,10 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + if ( + cfgResult.data && + (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) + ) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) @@ -333,7 +340,10 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + if ( + cfgResult.data && + (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) + ) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 04f2f6523..154995997 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -52,7 +52,11 @@ export function DialogModel() { description: provider.name, category: provider.name, })), - filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))), + filter( + (x) => + Boolean(ref()?.filter) || + !local.model.recent().find((y) => isDeepEqual(y, x.value)), + ), ), ), ), diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 95792ad83..dc770ce28 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -20,8 +20,8 @@ export function DialogSessionList() { const deleteKeybind = "ctrl+d" - const currentSessionID = createMemo(() => - route.data.type === "session" ? route.data.sessionID : undefined + const currentSessionID = createMemo(() => + route.data.type === "session" ? route.data.sessionID : undefined, ) const options = createMemo(() => { diff --git a/packages/opencode/src/cli/cmd/tui/component/logo.tsx b/packages/opencode/src/cli/cmd/tui/component/logo.tsx index 59db5fe7d..7cac51ecc 100644 --- a/packages/opencode/src/cli/cmd/tui/component/logo.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/logo.tsx @@ -3,9 +3,19 @@ import { TextAttributes } from "@opentui/core" import { For } from "solid-js" import { useTheme } from "@tui/context/theme" -const LOGO_LEFT = [` `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`] +const LOGO_LEFT = [ + ` `, + `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, + `█░░█ █░░█ █▀▀▀ █░░█`, + `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`, +] -const LOGO_RIGHT = [` ▄ `, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`] +const LOGO_RIGHT = [ + ` ▄ `, + `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, + `█░░░ █░░█ █░░█ █▀▀▀`, + `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`, +] export function Logo() { const { theme } = useTheme() diff --git a/packages/opencode/src/cli/cmd/tui/context/route.tsx b/packages/opencode/src/cli/cmd/tui/context/route.tsx index ef230dc98..dd8ede156 100644 --- a/packages/opencode/src/cli/cmd/tui/context/route.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/route.tsx @@ -17,13 +17,11 @@ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({ init: (props: { data?: Route }) => { const [store, setStore] = createStore( props.data ?? - ( - process.env["OPENCODE_ROUTE"] + (process.env["OPENCODE_ROUTE"] ? JSON.parse(process.env["OPENCODE_ROUTE"]) : { - type: "home", - } - ), + type: "home", + }), ) return { diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 5d8f1dac8..8ba73a4b6 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -269,6 +269,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string) { const now = Date.now() + console.log("syncing", sessionID) const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), sdk.client.session.messages({ path: { id: sessionID } }), diff --git a/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json b/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json index 8eff42c5f..24c74733d 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json +++ b/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json @@ -218,4 +218,4 @@ "light": "nightOwlFg" } } -} \ No newline at end of file +} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index ee2b77afc..cfdd4d694 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -7,7 +7,9 @@ import { useRoute } from "@tui/context/route" export function DialogMessage(props: { messageID: string; sessionID: string }) { const sync = useSync() const sdk = useSDK() - const message = createMemo(() => sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID)) + const message = createMemo(() => + sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID), + ) const route = useRoute() return ( diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx index f5976cdf0..b440ee1a0 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx @@ -19,7 +19,9 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s const result = [] as DialogSelectOption[] for (const message of messages) { if (message.role !== "user") continue - const part = (sync.data.part[message.id] ?? []).find((x) => x.type === "text" && !x.synthetic) as TextPart + const part = (sync.data.part[message.id] ?? []).find( + (x) => x.type === "text" && !x.synthetic, + ) as TextPart if (!part) continue result.push({ title: part.text.replace(/\n/g, " "), @@ -33,5 +35,11 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s return result }) - return props.onMove(option.value)} title="Timeline" options={options()} /> + return ( + props.onMove(option.value)} + title="Timeline" + options={options()} + /> + ) } diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 971ed8170..7aa8ab2f4 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -105,14 +105,15 @@ export function Session() { const sidebarVisible = createMemo(() => sidebar() === "show" || (sidebar() === "auto" && wide())) const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4) - createEffect(() => { - sync.session.sync(route.sessionID).catch(() => { + createEffect(async () => { + await sync.session.sync(route.sessionID).catch(() => { toast.show({ message: `Session not found: ${route.sessionID}`, variant: "error", }) return navigate({ type: "home" }) }) + scroll.scrollBy(100_000) }) const toast = useToast() diff --git a/packages/opencode/src/cli/cmd/tui/spawn.ts b/packages/opencode/src/cli/cmd/tui/spawn.ts index 29c9a3590..6a1975131 100644 --- a/packages/opencode/src/cli/cmd/tui/spawn.ts +++ b/packages/opencode/src/cli/cmd/tui/spawn.ts @@ -41,7 +41,12 @@ export const TuiSpawnCommand = cmd({ ) cwd = new URL("../../../../", import.meta.url).pathname } else cmd.push(process.execPath) - cmd.push("attach", server.url.toString(), "--dir", args.project ? path.resolve(args.project) : process.cwd()) + cmd.push( + "attach", + server.url.toString(), + "--dir", + args.project ? path.resolve(args.project) : process.cwd(), + ) const proc = Bun.spawn({ cmd, cwd, diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx index dd5b238b1..f79ae0555 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx @@ -53,7 +53,9 @@ export function DialogConfirm(props: DialogConfirmProps) { dialog.clear() }} > - {Locale.titlecase(key)} + + {Locale.titlecase(key)} + )} diff --git a/packages/opencode/src/cli/cmd/tui/util/editor.ts b/packages/opencode/src/cli/cmd/tui/util/editor.ts index 0aa69dcd8..18a1400c9 100644 --- a/packages/opencode/src/cli/cmd/tui/util/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/util/editor.ts @@ -5,7 +5,10 @@ import { join } from "node:path" import { CliRenderer } from "@opentui/core" export namespace Editor { - export async function open(opts: { value: string; renderer: CliRenderer }): Promise { + export async function open(opts: { + value: string + renderer: CliRenderer + }): Promise { const editor = process.env["EDITOR"] if (!editor) return diff --git a/packages/opencode/src/cli/cmd/upgrade.ts b/packages/opencode/src/cli/cmd/upgrade.ts index 65f3bab4d..f0ca48014 100644 --- a/packages/opencode/src/cli/cmd/upgrade.ts +++ b/packages/opencode/src/cli/cmd/upgrade.ts @@ -27,7 +27,9 @@ export const UpgradeCommand = { const detectedMethod = await Installation.method() const method = (args.method as Installation.Method) ?? detectedMethod if (method === "unknown") { - prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`) + prompts.log.error( + `opencode is installed to ${process.execPath} and may be managed by a package manager`, + ) const install = await prompts.select({ message: "Install anyways?", options: [ diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index efd31ccba..c2ee63c61 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -574,7 +574,10 @@ export namespace Config { .object({ apiKey: z.string().optional(), baseURL: z.string().optional(), - enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"), + enterpriseUrl: z + .string() + .optional() + .describe("GitHub Enterprise URL for copilot authentication"), timeout: z .union([ z diff --git a/packages/opencode/src/file/fzf.ts b/packages/opencode/src/file/fzf.ts index cd0aa4fc8..702688b17 100644 --- a/packages/opencode/src/file/fzf.ts +++ b/packages/opencode/src/file/fzf.ts @@ -81,7 +81,9 @@ export namespace Fzf { }) } if (config.extension === "zip") { - const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()]))) + const zipFileReader = new ZipReader( + new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])), + ) const entries = await zipFileReader.getEntries() let fzfEntry: any for (const entry of entries) { diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index 29014d199..84a45e386 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -161,7 +161,9 @@ export namespace Ripgrep { } if (config.extension === "zip") { if (config.extension === "zip") { - const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()]))) + const zipFileReader = new ZipReader( + new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])), + ) const entries = await zipFileReader.getEntries() let rgEntry: any for (const entry of entries) { @@ -354,7 +356,12 @@ export namespace Ripgrep { return lines.join("\n") } - export async function search(input: { cwd: string; pattern: string; glob?: string[]; limit?: number }) { + export async function search(input: { + cwd: string + pattern: string + glob?: string[] + limit?: number + }) { const args = [`${await filepath()}`, "--json", "--hidden", "--glob='!.git/*'"] if (input.glob) { diff --git a/packages/opencode/src/file/time.ts b/packages/opencode/src/file/time.ts index 5cba5e820..fe1dcff49 100644 --- a/packages/opencode/src/file/time.ts +++ b/packages/opencode/src/file/time.ts @@ -27,7 +27,10 @@ export namespace FileTime { export async function assert(sessionID: string, filepath: string) { const time = get(sessionID, filepath) - if (!time) throw new Error(`You must read the file ${filepath} before overwriting it. Use the Read tool first`) + if (!time) + throw new Error( + `You must read the file ${filepath} before overwriting it. Use the Read tool first`, + ) const stats = await Bun.file(filepath).stat() if (stats.mtime.getTime() > time.getTime()) { throw new Error( diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index d5985b582..e9304af70 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -51,8 +51,10 @@ export namespace FileWatcher { for (const evt of evts) { log.info("event", evt) if (evt.type === "create") Bus.publish(Event.Updated, { file: evt.path, event: "add" }) - if (evt.type === "update") Bus.publish(Event.Updated, { file: evt.path, event: "change" }) - if (evt.type === "delete") Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) + if (evt.type === "update") + Bus.publish(Event.Updated, { file: evt.path, event: "change" }) + if (evt.type === "delete") + Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) } }, { diff --git a/packages/opencode/src/id/id.ts b/packages/opencode/src/id/id.ts index 99eb6c9ff..76b6a46be 100644 --- a/packages/opencode/src/id/id.ts +++ b/packages/opencode/src/id/id.ts @@ -49,7 +49,11 @@ export namespace Identifier { return result } - export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string { + export function create( + prefix: keyof typeof prefixes, + descending: boolean, + timestamp?: number, + ): string { const currentTimestamp = timestamp ?? Date.now() if (currentTimestamp !== lastTimestamp) { diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 1a6e2cb71..920cecc62 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -1,5 +1,9 @@ import path from "path" -import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node" +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, +} from "vscode-jsonrpc/node" import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types" import { Log } from "../util/log" import { LANGUAGE_EXTENSIONS } from "./language" @@ -34,7 +38,11 @@ export namespace LSPClient { ), } - export async function create(input: { serverID: string; server: LSPServer.Handle; root: string }) { + export async function create(input: { + serverID: string + server: LSPServer.Handle + root: string + }) { const l = log.clone().tag("serverID", input.serverID) l.info("starting client") @@ -129,7 +137,9 @@ export namespace LSPClient { }, notify: { async open(input: { path: string }) { - input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path) + input.path = path.isAbsolute(input.path) + ? input.path + : path.resolve(Instance.directory, input.path) const file = Bun.file(input.path) const text = await file.text() const extension = path.extname(input.path) @@ -171,13 +181,18 @@ export namespace LSPClient { return diagnostics }, async waitForDiagnostics(input: { path: string }) { - input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path) + input.path = path.isAbsolute(input.path) + ? input.path + : path.resolve(Instance.directory, input.path) log.info("waiting for diagnostics", input) let unsub: () => void return await withTimeout( new Promise((resolve) => { unsub = Bus.subscribe(Event.Diagnostics, (event) => { - if (event.properties.path === input.path && event.properties.serverID === result.serverID) { + if ( + event.properties.path === input.path && + event.properties.serverID === result.serverID + ) { log.info("got diagnostics", input) unsub?.() resolve() diff --git a/packages/opencode/src/patch/index.ts b/packages/opencode/src/patch/index.ts index c6e25c5cc..46467e0ba 100644 --- a/packages/opencode/src/patch/index.ts +++ b/packages/opencode/src/patch/index.ts @@ -20,7 +20,7 @@ export namespace Patch { workdir?: string } - export type Hunk = + export type Hunk = | { type: "add"; path: string; contents: string } | { type: "delete"; path: string } | { type: "update"; path: string; move_path?: string; chunks: UpdateFileChunk[] } @@ -71,60 +71,66 @@ export namespace Patch { } // Parser implementation - function parsePatchHeader(lines: string[], startIdx: number): { filePath: string; movePath?: string; nextIdx: number } | null { + function parsePatchHeader( + lines: string[], + startIdx: number, + ): { filePath: string; movePath?: string; nextIdx: number } | null { const line = lines[startIdx] - + if (line.startsWith("*** Add File:")) { const filePath = line.split(":", 2)[1]?.trim() return filePath ? { filePath, nextIdx: startIdx + 1 } : null } - + if (line.startsWith("*** Delete File:")) { const filePath = line.split(":", 2)[1]?.trim() return filePath ? { filePath, nextIdx: startIdx + 1 } : null } - + if (line.startsWith("*** Update File:")) { const filePath = line.split(":", 2)[1]?.trim() let movePath: string | undefined let nextIdx = startIdx + 1 - + // Check for move directive if (nextIdx < lines.length && lines[nextIdx].startsWith("*** Move to:")) { movePath = lines[nextIdx].split(":", 2)[1]?.trim() nextIdx++ } - + return filePath ? { filePath, movePath, nextIdx } : null } - + return null } - function parseUpdateFileChunks(lines: string[], startIdx: number): { chunks: UpdateFileChunk[]; nextIdx: number } { + function parseUpdateFileChunks( + lines: string[], + startIdx: number, + ): { chunks: UpdateFileChunk[]; nextIdx: number } { const chunks: UpdateFileChunk[] = [] let i = startIdx - + while (i < lines.length && !lines[i].startsWith("***")) { if (lines[i].startsWith("@@")) { // Parse context line const contextLine = lines[i].substring(2).trim() i++ - + const oldLines: string[] = [] const newLines: string[] = [] let isEndOfFile = false - + // Parse change lines while (i < lines.length && !lines[i].startsWith("@@") && !lines[i].startsWith("***")) { const changeLine = lines[i] - + if (changeLine === "*** End of File") { isEndOfFile = true i++ break } - + if (changeLine.startsWith(" ")) { // Keep line - appears in both old and new const content = changeLine.substring(1) @@ -137,10 +143,10 @@ export namespace Patch { // Add line - only in new newLines.push(changeLine.substring(1)) } - + i++ } - + chunks.push({ old_lines: oldLines, new_lines: newLines, @@ -151,26 +157,29 @@ export namespace Patch { i++ } } - + return { chunks, nextIdx: i } } - function parseAddFileContent(lines: string[], startIdx: number): { content: string; nextIdx: number } { + function parseAddFileContent( + lines: string[], + startIdx: number, + ): { content: string; nextIdx: number } { let content = "" let i = startIdx - + while (i < lines.length && !lines[i].startsWith("***")) { if (lines[i].startsWith("+")) { content += lines[i].substring(1) + "\n" } i++ } - + // Remove trailing newline if (content.endsWith("\n")) { content = content.slice(0, -1) } - + return { content, nextIdx: i } } @@ -178,28 +187,28 @@ export namespace Patch { const lines = patchText.split("\n") const hunks: Hunk[] = [] let i = 0 - + // Look for Begin/End patch markers const beginMarker = "*** Begin Patch" const endMarker = "*** End Patch" - - const beginIdx = lines.findIndex(line => line.trim() === beginMarker) - const endIdx = lines.findIndex(line => line.trim() === endMarker) - + + const beginIdx = lines.findIndex((line) => line.trim() === beginMarker) + const endIdx = lines.findIndex((line) => line.trim() === endMarker) + if (beginIdx === -1 || endIdx === -1 || beginIdx >= endIdx) { throw new Error("Invalid patch format: missing Begin/End markers") } - + // Parse content between markers i = beginIdx + 1 - + while (i < endIdx) { const header = parsePatchHeader(lines, i) if (!header) { i++ continue } - + if (lines[i].startsWith("*** Add File:")) { const { content, nextIdx } = parseAddFileContent(lines, header.nextIdx) hunks.push({ @@ -227,18 +236,19 @@ export namespace Patch { i++ } } - + return { hunks } } // Apply patch functionality - export function maybeParseApplyPatch(argv: string[]): + export function maybeParseApplyPatch( + argv: string[], + ): | { type: MaybeApplyPatch.Body; args: ApplyPatchArgs } | { type: MaybeApplyPatch.PatchParseError; error: Error } | { type: MaybeApplyPatch.NotApplyPatch } { - const APPLY_PATCH_COMMANDS = ["apply_patch", "applypatch"] - + // Direct invocation: apply_patch if (argv.length === 2 && APPLY_PATCH_COMMANDS.includes(argv[0])) { try { @@ -257,13 +267,13 @@ export namespace Patch { } } } - + // Bash heredoc form: bash -lc 'apply_patch <<"EOF" ...' if (argv.length === 3 && argv[0] === "bash" && argv[1] === "-lc") { // Simple extraction - in real implementation would need proper bash parsing const script = argv[2] const heredocMatch = script.match(/apply_patch\s*<<['"](\w+)['"]\s*\n([\s\S]*?)\n\1/) - + if (heredocMatch) { const patchContent = heredocMatch[2] try { @@ -283,7 +293,7 @@ export namespace Patch { } } } - + return { type: MaybeApplyPatch.NotApplyPatch } } @@ -293,7 +303,10 @@ export namespace Patch { content: string } - export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate { + export function deriveNewContentsFromChunks( + filePath: string, + chunks: UpdateFileChunk[], + ): ApplyPatchFileUpdate { // Read original file content let originalContent: string try { @@ -301,37 +314,41 @@ export namespace Patch { } catch (error) { throw new Error(`Failed to read file ${filePath}: ${error}`) } - + let originalLines = originalContent.split("\n") - + // Drop trailing empty element for consistent line counting if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") { originalLines.pop() } - + const replacements = computeReplacements(originalLines, filePath, chunks) let newLines = applyReplacements(originalLines, replacements) - + // Ensure trailing newline if (newLines.length === 0 || newLines[newLines.length - 1] !== "") { newLines.push("") } - + const newContent = newLines.join("\n") - + // Generate unified diff const unifiedDiff = generateUnifiedDiff(originalContent, newContent) - + return { unified_diff: unifiedDiff, content: newContent, } } - function computeReplacements(originalLines: string[], filePath: string, chunks: UpdateFileChunk[]): Array<[number, number, string[]]> { + function computeReplacements( + originalLines: string[], + filePath: string, + chunks: UpdateFileChunk[], + ): Array<[number, number, string[]]> { const replacements: Array<[number, number, string[]]> = [] let lineIndex = 0 - + for (const chunk of chunks) { // Handle context-based seeking if (chunk.change_context) { @@ -341,21 +358,22 @@ export namespace Patch { } lineIndex = contextIdx + 1 } - + // Handle pure addition (no old lines) if (chunk.old_lines.length === 0) { - const insertionIdx = originalLines.length > 0 && originalLines[originalLines.length - 1] === "" - ? originalLines.length - 1 - : originalLines.length + const insertionIdx = + originalLines.length > 0 && originalLines[originalLines.length - 1] === "" + ? originalLines.length - 1 + : originalLines.length replacements.push([insertionIdx, 0, chunk.new_lines]) continue } - + // Try to match old lines in the file let pattern = chunk.old_lines let newSlice = chunk.new_lines let found = seekSequence(originalLines, pattern, lineIndex) - + // Retry without trailing empty line if not found if (found === -1 && pattern.length > 0 && pattern[pattern.length - 1] === "") { pattern = pattern.slice(0, -1) @@ -364,79 +382,82 @@ export namespace Patch { } found = seekSequence(originalLines, pattern, lineIndex) } - + if (found !== -1) { replacements.push([found, pattern.length, newSlice]) lineIndex = found + pattern.length } else { throw new Error( - `Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}` + `Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}`, ) } } - + // Sort replacements by index to apply in order replacements.sort((a, b) => a[0] - b[0]) - + return replacements } - function applyReplacements(lines: string[], replacements: Array<[number, number, string[]]>): string[] { + function applyReplacements( + lines: string[], + replacements: Array<[number, number, string[]]>, + ): string[] { // Apply replacements in reverse order to avoid index shifting const result = [...lines] - + for (let i = replacements.length - 1; i >= 0; i--) { const [startIdx, oldLen, newSegment] = replacements[i] - + // Remove old lines result.splice(startIdx, oldLen) - + // Insert new lines for (let j = 0; j < newSegment.length; j++) { result.splice(startIdx + j, 0, newSegment[j]) } } - + return result } function seekSequence(lines: string[], pattern: string[], startIndex: number): number { if (pattern.length === 0) return -1 - + // Simple substring search implementation for (let i = startIndex; i <= lines.length - pattern.length; i++) { let matches = true - + for (let j = 0; j < pattern.length; j++) { if (lines[i + j] !== pattern[j]) { matches = false break } } - + if (matches) { return i } } - + return -1 } function generateUnifiedDiff(oldContent: string, newContent: string): string { const oldLines = oldContent.split("\n") const newLines = newContent.split("\n") - + // Simple diff generation - in a real implementation you'd use a proper diff algorithm let diff = "@@ -1 +1 @@\n" - + // Find changes (simplified approach) const maxLen = Math.max(oldLines.length, newLines.length) let hasChanges = false - + for (let i = 0; i < maxLen; i++) { const oldLine = oldLines[i] || "" const newLine = newLines[i] || "" - + if (oldLine !== newLine) { if (oldLine) diff += `-${oldLine}\n` if (newLine) diff += `+${newLine}\n` @@ -445,7 +466,7 @@ export namespace Patch { diff += ` ${oldLine}\n` } } - + return hasChanges ? diff : "" } @@ -454,11 +475,11 @@ export namespace Patch { if (hunks.length === 0) { throw new Error("No files were modified.") } - + const added: string[] = [] const modified: string[] = [] const deleted: string[] = [] - + for (const hunk of hunks) { switch (hunk.type) { case "add": @@ -467,28 +488,28 @@ export namespace Patch { if (addDir !== "." && addDir !== "/") { await fs.mkdir(addDir, { recursive: true }) } - + await fs.writeFile(hunk.path, hunk.contents, "utf-8") added.push(hunk.path) log.info(`Added file: ${hunk.path}`) break - + case "delete": await fs.unlink(hunk.path) deleted.push(hunk.path) log.info(`Deleted file: ${hunk.path}`) break - + case "update": const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks) - + if (hunk.move_path) { // Handle file move const moveDir = path.dirname(hunk.move_path) if (moveDir !== "." && moveDir !== "/") { await fs.mkdir(moveDir, { recursive: true }) } - + await fs.writeFile(hunk.move_path, fileUpdate.content, "utf-8") await fs.unlink(hunk.path) modified.push(hunk.move_path) @@ -502,7 +523,7 @@ export namespace Patch { break } } - + return { added, modified, deleted } } @@ -513,7 +534,10 @@ export namespace Patch { } // Async version of maybeParseApplyPatchVerified - export async function maybeParseApplyPatchVerified(argv: string[], cwd: string): Promise< + export async function maybeParseApplyPatchVerified( + argv: string[], + cwd: string, + ): Promise< | { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction } | { type: MaybeApplyPatchVerified.CorrectnessError; error: Error } | { type: MaybeApplyPatchVerified.NotApplyPatch } @@ -530,18 +554,21 @@ export namespace Patch { // Not a patch, continue } } - + const result = maybeParseApplyPatch(argv) - + switch (result.type) { case MaybeApplyPatch.Body: const { args } = result const effectiveCwd = args.workdir ? path.resolve(cwd, args.workdir) : cwd const changes = new Map() - + for (const hunk of args.hunks) { - const resolvedPath = path.resolve(effectiveCwd, hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path) - + const resolvedPath = path.resolve( + effectiveCwd, + hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path, + ) + switch (hunk.type) { case "add": changes.set(resolvedPath, { @@ -549,7 +576,7 @@ export namespace Patch { content: hunk.contents, }) break - + case "delete": // For delete, we need to read the current content const deletePath = path.resolve(effectiveCwd, hunk.path) @@ -566,7 +593,7 @@ export namespace Patch { } } break - + case "update": const updatePath = path.resolve(effectiveCwd, hunk.path) try { @@ -574,7 +601,9 @@ export namespace Patch { changes.set(resolvedPath, { type: "update", unified_diff: fileUpdate.unified_diff, - move_path: hunk.move_path ? path.resolve(effectiveCwd, hunk.move_path) : undefined, + move_path: hunk.move_path + ? path.resolve(effectiveCwd, hunk.move_path) + : undefined, new_content: fileUpdate.content, }) } catch (error) { @@ -586,7 +615,7 @@ export namespace Patch { break } } - + return { type: MaybeApplyPatchVerified.Body, action: { @@ -595,15 +624,15 @@ export namespace Patch { cwd: effectiveCwd, }, } - + case MaybeApplyPatch.PatchParseError: return { type: MaybeApplyPatchVerified.CorrectnessError, error: result.error, } - + case MaybeApplyPatch.NotApplyPatch: return { type: MaybeApplyPatchVerified.NotApplyPatch } } } -} \ No newline at end of file +} diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 6212edff8..9e095f5bf 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -40,7 +40,8 @@ export namespace ProviderTransform { } for (const msg of unique([...system, ...final])) { - const shouldUseContentOptions = providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0 + const shouldUseContentOptions = + providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0 if (shouldUseContentOptions) { const lastContent = msg.content[msg.content.length - 1] @@ -84,7 +85,11 @@ export namespace ProviderTransform { return undefined } - export function options(providerID: string, modelID: string, sessionID: string): Record | undefined { + export function options( + providerID: string, + modelID: string, + sessionID: string, + ): Record | undefined { const result: Record = {} if (providerID === "openai") { @@ -109,7 +114,11 @@ export namespace ProviderTransform { return result } - export function providerOptions(npm: string | undefined, providerID: string, options: { [x: string]: any }) { + export function providerOptions( + npm: string | undefined, + providerID: string, + options: { [x: string]: any }, + ) { switch (npm) { case "@ai-sdk/openai": case "@ai-sdk/azure": @@ -142,7 +151,8 @@ export namespace ProviderTransform { if (providerID === "anthropic") { const thinking = options?.["thinking"] - const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0 + const budgetTokens = + typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0 const enabled = thinking?.["type"] === "enabled" if (enabled && budgetTokens > 0) { // Return text tokens so that text + thinking <= model cap, preferring 32k text when possible. diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 59e066e15..308ed4380 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -755,7 +755,7 @@ export namespace Server { ), async (c) => { const messages = await Session.messages(c.req.valid("param").id) - return c.json(messages) + return c.json(messages.slice(-100)) }, ) .get( diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index cc6351675..021b544e0 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -1,4 +1,4 @@ -import { streamText, type ModelMessage, LoadAPIKeyError, type StreamTextResult, type Tool as AITool } from "ai" +import { streamText, type ModelMessage, type StreamTextResult, type Tool as AITool } from "ai" import { Session } from "." import { Identifier } from "../id/id" import { Instance } from "../project/instance" @@ -30,12 +30,17 @@ export namespace SessionCompaction { ), } - export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) { + export function isOverflow(input: { + tokens: MessageV2.Assistant["tokens"] + model: ModelsDev.Model + }) { if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false const context = input.model.limit.context if (context === 0) return false const count = input.tokens.input + input.tokens.cache.read + input.tokens.output - const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX + const output = + Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || + SessionPrompt.OUTPUT_TOKEN_MAX const usable = context - output return count > usable } @@ -87,9 +92,15 @@ export namespace SessionCompaction { } } - export async function run(input: { sessionID: string; providerID: string; modelID: string; signal?: AbortSignal }) { + export async function run(input: { + sessionID: string + providerID: string + modelID: string + signal?: AbortSignal + }) { if (!input.signal) SessionLock.assertUnlocked(input.sessionID) - await using lock = input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined + await using lock = + input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined const signal = input.signal ?? lock!.signal await Session.update(input.sessionID, (draft) => { @@ -113,7 +124,6 @@ export namespace SessionCompaction { role: "assistant", parentID: toSummarize.findLast((m) => m.info.role === "user")?.info.id!, sessionID: input.sessionID, - system, mode: "build", path: { cwd: Instance.directory, @@ -150,7 +160,11 @@ export namespace SessionCompaction { // set to 0, we handle loop maxRetries: 0, model: model.language, - providerOptions: ProviderTransform.providerOptions(model.npm, model.providerID, model.info.options), + providerOptions: ProviderTransform.providerOptions( + model.npm, + model.providerID, + model.info.options, + ), headers: model.info.headers, abortSignal: signal, onError(error) { @@ -230,7 +244,11 @@ export namespace SessionCompaction { error: e, }) const error = MessageV2.fromError(e, { providerID: input.providerID }) - if (retries.count < retries.max && MessageV2.APIError.isInstance(error) && error.data.isRetryable) { + if ( + retries.count < retries.max && + MessageV2.APIError.isInstance(error) && + error.data.isRetryable + ) { shouldRetry = true await Session.updatePart({ id: Identifier.ascending("part"), diff --git a/packages/opencode/src/session/lock.ts b/packages/opencode/src/session/lock.ts index ed024edab..22eb8187c 100644 --- a/packages/opencode/src/session/lock.ts +++ b/packages/opencode/src/session/lock.ts @@ -50,7 +50,10 @@ export namespace SessionLock { export function acquire(input: { sessionID: string }) { const lock = get(input.sessionID) if (lock) { - throw new LockedError({ sessionID: input.sessionID, message: `Session ${input.sessionID} is locked` }) + throw new LockedError({ + sessionID: input.sessionID, + message: `Session ${input.sessionID} is locked`, + }) } const controller = new AbortController() state().locks.set(input.sessionID, { diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 3b28afe0f..f35735b71 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -2,14 +2,23 @@ import z from "zod" import { Bus } from "../bus" import { NamedError } from "../util/error" import { Message } from "./message" -import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai" +import { + APICallError, + convertToModelMessages, + LoadAPIKeyError, + type ModelMessage, + type UIMessage, +} from "ai" import { Identifier } from "../id/id" import { LSP } from "../lsp" import { Snapshot } from "@/snapshot" export namespace MessageV2 { export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({})) - export const AbortedError = NamedError.create("MessageAbortedError", z.object({ message: z.string() })) + export const AbortedError = NamedError.create( + "MessageAbortedError", + z.object({ message: z.string() }), + ) export const AuthError = NamedError.create( "ProviderAuthError", z.object({ @@ -242,7 +251,12 @@ export namespace MessageV2 { export type ToolStateError = z.infer export const ToolState = z - .discriminatedUnion("status", [ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]) + .discriminatedUnion("status", [ + ToolStatePending, + ToolStateRunning, + ToolStateCompleted, + ToolStateError, + ]) .meta({ ref: "ToolState", }) @@ -313,7 +327,6 @@ export namespace MessageV2 { APIError.Schema, ]) .optional(), - system: z.string().array(), parentID: z.string(), modelID: z.string(), providerID: z.string(), @@ -397,7 +410,6 @@ export namespace MessageV2 { tokens: v1.metadata.assistant!.tokens, modelID: v1.metadata.assistant!.modelID, providerID: v1.metadata.assistant!.providerID, - system: v1.metadata.assistant!.system, mode: "build", error: v1.metadata.error, } @@ -440,7 +452,8 @@ export namespace MessageV2 { } } - const { title, time, ...metadata } = v1.metadata.tool[part.toolInvocation.toolCallId] ?? {} + const { title, time, ...metadata } = + v1.metadata.tool[part.toolInvocation.toolCallId] ?? {} if (part.toolInvocation.state === "call") { return { status: "running", @@ -541,7 +554,11 @@ export namespace MessageV2 { }, ] // text/plain and directory files are converted into text parts, ignore them - if (part.type === "file" && part.mime !== "text/plain" && part.mime !== "application/x-directory") + if ( + part.type === "file" && + part.mime !== "text/plain" && + part.mime !== "application/x-directory" + ) return [ { type: "file", @@ -600,7 +617,9 @@ export namespace MessageV2 { state: "output-available", toolCallId: part.callID, input: part.state.input, - output: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output, + output: part.state.time.compacted + ? "[Old tool result content cleared]" + : part.state.output, callProviderMetadata: part.metadata, }, ] diff --git a/packages/opencode/src/session/message.ts b/packages/opencode/src/session/message.ts index 4471f9235..baa8c00f1 100644 --- a/packages/opencode/src/session/message.ts +++ b/packages/opencode/src/session/message.ts @@ -51,9 +51,11 @@ export namespace Message { }) export type ToolResult = z.infer - export const ToolInvocation = z.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult]).meta({ - ref: "ToolInvocation", - }) + export const ToolInvocation = z + .discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult]) + .meta({ + ref: "ToolInvocation", + }) export type ToolInvocation = z.infer export const TextPart = z @@ -122,7 +124,14 @@ export namespace Message { export type StepStartPart = z.infer export const MessagePart = z - .discriminatedUnion("type", [TextPart, ReasoningPart, ToolInvocationPart, SourceUrlPart, FilePart, StepStartPart]) + .discriminatedUnion("type", [ + TextPart, + ReasoningPart, + ToolInvocationPart, + SourceUrlPart, + FilePart, + StepStartPart, + ]) .meta({ ref: "MessagePart", }) @@ -140,7 +149,11 @@ export namespace Message { completed: z.number().optional(), }), error: z - .discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema]) + .discriminatedUnion("name", [ + AuthError.Schema, + NamedError.Unknown.Schema, + OutputLengthError.Schema, + ]) .optional(), sessionID: z.string(), tool: z.record( diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index a70045349..9072135f6 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -961,7 +961,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), parentID, role: "assistant", - system: input.system, mode: input.agent, path: { cwd: Instance.directory, @@ -1412,7 +1411,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), sessionID: input.sessionID, parentID: userMsg.id, - system: [], mode: input.agent, cost: 0, path: { @@ -1709,7 +1707,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), sessionID: input.sessionID, parentID: userMsg.id, - system: [], mode: agentName, cost: 0, path: { diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts index a88b5f08f..7439d59cf 100644 --- a/packages/opencode/src/session/revert.ts +++ b/packages/opencode/src/session/revert.ts @@ -45,7 +45,9 @@ export namespace SessionRevert { if (!revert) { if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) { // if no useful parts left in message, same as reverting whole message - const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined + const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) + ? input.partID + : undefined revert = { messageID: !partID && lastUser ? lastUser.id : msg.info.id, partID, diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 3173dcac5..3bff0772b 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -24,7 +24,8 @@ export namespace SystemPrompt { export function provider(modelID: string) { if (modelID.includes("gpt-5")) return [PROMPT_CODEX] - if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST] + if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) + return [PROMPT_BEAST] if (modelID.includes("gemini-")) return [PROMPT_GEMINI] if (modelID.includes("claude")) return [PROMPT_ANTHROPIC] return [PROMPT_ANTHROPIC_WITHOUT_TODO] @@ -99,7 +100,11 @@ export namespace SystemPrompt { }), ).catch(() => []) } else { - matches = await Filesystem.globUp(instruction, Instance.directory, Instance.worktree).catch(() => []) + matches = await Filesystem.globUp( + instruction, + Instance.directory, + Instance.worktree, + ).catch(() => []) } matches.forEach((path) => paths.add(path)) } diff --git a/packages/opencode/src/session/todo.ts b/packages/opencode/src/session/todo.ts index d52087739..4d9a2650d 100644 --- a/packages/opencode/src/session/todo.ts +++ b/packages/opencode/src/session/todo.ts @@ -6,7 +6,9 @@ export namespace Todo { export const Info = z .object({ content: z.string().describe("Brief description of the task"), - status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"), + status: z + .string() + .describe("Current status of the task: pending, in_progress, completed, cancelled"), priority: z.string().describe("Priority level of the task: high, medium, low"), id: z.string().describe("Unique identifier for the todo item"), }) diff --git a/packages/opencode/src/share/share.ts b/packages/opencode/src/share/share.ts index 1006b23d5..d48d76f89 100644 --- a/packages/opencode/src/share/share.ts +++ b/packages/opencode/src/share/share.ts @@ -50,7 +50,10 @@ export namespace Share { await sync("session/info/" + evt.properties.info.id, evt.properties.info) }) Bus.subscribe(MessageV2.Event.Updated, async (evt) => { - await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info) + await sync( + "session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, + evt.properties.info, + ) }) Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => { await sync( @@ -67,7 +70,9 @@ export namespace Share { export const URL = process.env["OPENCODE_API"] ?? - (Installation.isPreview() || Installation.isLocal() ? "https://api.dev.opencode.ai" : "https://api.opencode.ai") + (Installation.isPreview() || Installation.isLocal() + ? "https://api.dev.opencode.ai" + : "https://api.opencode.ai") export async function create(sessionID: string) { return fetch(`${URL}/share_create`, { diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 98b316804..b4deee97c 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -27,7 +27,11 @@ export namespace Snapshot { log.info("initialized") } await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow().text() + const hash = await $`git --git-dir ${git} write-tree` + .quiet() + .cwd(Instance.directory) + .nothrow() + .text() log.info("tracking", { hash, cwd: Instance.directory, git }) return hash.trim() } @@ -41,7 +45,10 @@ export namespace Snapshot { export async function patch(hash: string): Promise { const git = gitdir() await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.quiet().cwd(Instance.directory).nothrow() + const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .` + .quiet() + .cwd(Instance.directory) + .nothrow() // If git diff fails, return empty patch if (result.exitCode !== 0) { @@ -64,10 +71,11 @@ export namespace Snapshot { export async function restore(snapshot: string) { log.info("restore", { commit: snapshot }) const git = gitdir() - const result = await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` - .quiet() - .cwd(Instance.worktree) - .nothrow() + const result = + await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.error("failed to restore snapshot", { @@ -113,7 +121,10 @@ export namespace Snapshot { export async function diff(hash: string) { const git = gitdir() await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).nothrow() + const result = await $`git --git-dir=${git} diff ${hash} -- .` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.warn("failed to get diff", { diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index ba3d2c0bf..057fb9e8f 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -23,8 +23,13 @@ export const EditTool = Tool.define("edit", { parameters: z.object({ filePath: z.string().describe("The absolute path to the file to modify"), oldString: z.string().describe("The text to replace"), - newString: z.string().describe("The text to replace it with (must be different from oldString)"), - replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"), + newString: z + .string() + .describe("The text to replace it with (must be different from oldString)"), + replaceAll: z + .boolean() + .optional() + .describe("Replace all occurrences of oldString (default false)"), }), async execute(params, ctx) { if (!params.filePath) { @@ -35,7 +40,9 @@ export const EditTool = Tool.define("edit", { throw new Error("oldString and newString must be different") } - const filePath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath) + const filePath = path.isAbsolute(params.filePath) + ? params.filePath + : path.join(Instance.directory, params.filePath) if (!Filesystem.contains(Instance.directory, filePath)) { const parentDir = path.dirname(filePath) await Permission.ask({ @@ -172,7 +179,11 @@ function levenshtein(a: string, b: string): number { for (let i = 1; i <= a.length; i++) { for (let j = 1; j <= b.length; j++) { const cost = a[i - 1] === b[j - 1] ? 0 : 1 - matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost) + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j - 1] + cost, + ) } } return matrix[a.length][b.length] @@ -374,7 +385,9 @@ export const WhitespaceNormalizedReplacer: Replacer = function* (content, find) // Find the actual substring in the original line that matches const words = find.trim().split(/\s+/) if (words.length > 0) { - const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+") + const pattern = words + .map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join("\\s+") try { const regex = new RegExp(pattern) const match = line.match(regex) @@ -612,7 +625,12 @@ export function trimDiff(diff: string): string { return trimmedLines.join("\n") } -export function replace(content: string, oldString: string, newString: string, replaceAll = false): string { +export function replace( + content: string, + oldString: string, + newString: string, + replaceAll = false, +): string { if (oldString === newString) { throw new Error("oldString and newString must be different") } diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 5390be21a..9f7d04ea1 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -9,8 +9,14 @@ export const GrepTool = Tool.define("grep", { description: DESCRIPTION, parameters: z.object({ pattern: z.string().describe("The regex pattern to search for in file contents"), - path: z.string().optional().describe("The directory to search in. Defaults to the current working directory."), - include: z.string().optional().describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'), + path: z + .string() + .optional() + .describe("The directory to search in. Defaults to the current working directory."), + include: z + .string() + .optional() + .describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'), }), async execute(params) { if (!params.pattern) { diff --git a/packages/opencode/src/tool/ls.ts b/packages/opencode/src/tool/ls.ts index 95c36e745..4fcfcaa8c 100644 --- a/packages/opencode/src/tool/ls.ts +++ b/packages/opencode/src/tool/ls.ts @@ -37,13 +37,18 @@ const LIMIT = 100 export const ListTool = Tool.define("list", { description: DESCRIPTION, parameters: z.object({ - path: z.string().describe("The absolute path to the directory to list (must be absolute, not relative)").optional(), + path: z + .string() + .describe("The absolute path to the directory to list (must be absolute, not relative)") + .optional(), ignore: z.array(z.string()).describe("List of glob patterns to ignore").optional(), }), async execute(params) { const searchPath = path.resolve(Instance.directory, params.path || ".") - const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || []) + const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat( + params.ignore?.map((p) => `!${p}`) || [], + ) const files = [] for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs })) { files.push(file) diff --git a/packages/opencode/src/tool/lsp-diagnostics.ts b/packages/opencode/src/tool/lsp-diagnostics.ts index 18a6868b6..78c8c3cae 100644 --- a/packages/opencode/src/tool/lsp-diagnostics.ts +++ b/packages/opencode/src/tool/lsp-diagnostics.ts @@ -11,7 +11,9 @@ export const LspDiagnosticTool = Tool.define("lsp_diagnostics", { path: z.string().describe("The path to the file to get diagnostics."), }), execute: async (args) => { - const normalized = path.isAbsolute(args.path) ? args.path : path.join(Instance.directory, args.path) + const normalized = path.isAbsolute(args.path) + ? args.path + : path.join(Instance.directory, args.path) await LSP.touchFile(normalized, true) const diagnostics = await LSP.diagnostics() const file = diagnostics[normalized] diff --git a/packages/opencode/src/tool/multiedit.ts b/packages/opencode/src/tool/multiedit.ts index 7f562f473..f3a657354 100644 --- a/packages/opencode/src/tool/multiedit.ts +++ b/packages/opencode/src/tool/multiedit.ts @@ -14,8 +14,13 @@ export const MultiEditTool = Tool.define("multiedit", { z.object({ filePath: z.string().describe("The absolute path to the file to modify"), oldString: z.string().describe("The text to replace"), - newString: z.string().describe("The text to replace it with (must be different from oldString)"), - replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"), + newString: z + .string() + .describe("The text to replace it with (must be different from oldString)"), + replaceAll: z + .boolean() + .optional() + .describe("Replace all occurrences of oldString (default false)"), }), ) .describe("Array of edit operations to perform sequentially on the file"), diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 963636fd1..56a67bb03 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -18,7 +18,10 @@ export const ReadTool = Tool.define("read", { description: DESCRIPTION, parameters: z.object({ filePath: z.string().describe("The path to the file to read"), - offset: z.coerce.number().describe("The line number to start reading from (0-based)").optional(), + offset: z.coerce + .number() + .describe("The line number to start reading from (0-based)") + .optional(), limit: z.coerce.number().describe("The number of lines to read (defaults to 2000)").optional(), }), async execute(params, ctx) { @@ -53,13 +56,16 @@ export const ReadTool = Tool.define("read", { const suggestions = dirEntries .filter( (entry) => - entry.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(entry.toLowerCase()), + entry.toLowerCase().includes(base.toLowerCase()) || + base.toLowerCase().includes(entry.toLowerCase()), ) .map((entry) => path.join(dir, entry)) .slice(0, 3) if (suggestions.length > 0) { - throw new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`) + throw new Error( + `File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`, + ) } throw new Error(`File not found: ${filepath}`) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index c4d54597d..6234a4e61 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -24,7 +24,12 @@ export namespace ToolRegistry { const glob = new Bun.Glob("tool/*.{js,ts}") for (const dir of await Config.directories()) { - for await (const match of glob.scan({ cwd: dir, absolute: true, followSymlinks: true, dot: true })) { + for await (const match of glob.scan({ + cwd: dir, + absolute: true, + followSymlinks: true, + dot: true, + })) { const namespace = path.basename(match, path.extname(match)) const mod = await import(match) for (const [id, def] of Object.entries(mod)) { diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 342645c30..ac0b204ba 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -14,7 +14,10 @@ export const TaskTool = Tool.define("task", async () => { const description = DESCRIPTION.replace( "{agents}", agents - .map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`) + .map( + (a) => + `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`, + ) .join("\n"), ) return { @@ -26,7 +29,8 @@ export const TaskTool = Tool.define("task", async () => { }), async execute(params, ctx) { const agent = await Agent.get(params.subagent_type) - if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`) + if (!agent) + throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`) const session = await Session.create({ parentID: ctx.sessionID, title: params.description + ` (@${agent.name} subagent)`, @@ -91,7 +95,9 @@ export const TaskTool = Tool.define("task", async () => { let all all = await Session.messages(session.id) all = all.filter((x) => x.info.role === "assistant") - all = all.flatMap((msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[]) + all = all.flatMap( + (msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[], + ) return { title: params.description, metadata: { diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index 0333bb018..d85351ffe 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -48,13 +48,15 @@ export const WebFetchTool = Tool.define("webfetch", { let acceptHeader = "*/*" switch (params.format) { case "markdown": - acceptHeader = "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1" + acceptHeader = + "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1" break case "text": acceptHeader = "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1" break case "html": - acceptHeader = "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1" + acceptHeader = + "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1" break default: acceptHeader = @@ -158,7 +160,9 @@ async function extractTextFromHTML(html: string) { .on("*", { element(element) { // Reset skip flag when entering other elements - if (!["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName)) { + if ( + !["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName) + ) { skipContent = false } }, diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts index acaa12392..c7b998160 100644 --- a/packages/opencode/src/tool/write.ts +++ b/packages/opencode/src/tool/write.ts @@ -15,10 +15,14 @@ export const WriteTool = Tool.define("write", { description: DESCRIPTION, parameters: z.object({ content: z.string().describe("The content to write to the file"), - filePath: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"), + filePath: z + .string() + .describe("The absolute path to the file to write (must be absolute, not relative)"), }), async execute(params, ctx) { - const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath) + const filepath = path.isAbsolute(params.filePath) + ? params.filePath + : path.join(Instance.directory, params.filePath) if (!Filesystem.contains(Instance.directory, filepath)) { const parentDir = path.dirname(filepath) await Permission.ask({ diff --git a/packages/opencode/src/util/binary.ts b/packages/opencode/src/util/binary.ts index 3d8f61851..d72cc85d4 100644 --- a/packages/opencode/src/util/binary.ts +++ b/packages/opencode/src/util/binary.ts @@ -1,5 +1,9 @@ export namespace Binary { - export function search(array: T[], id: string, compare: (item: T) => string): { found: boolean; index: number } { + export function search( + array: T[], + id: string, + compare: (item: T) => string, + ): { found: boolean; index: number } { let left = 0 let right = array.length - 1 diff --git a/packages/opencode/src/util/defer.ts b/packages/opencode/src/util/defer.ts index 8de21528c..69b5c1788 100644 --- a/packages/opencode/src/util/defer.ts +++ b/packages/opencode/src/util/defer.ts @@ -1,6 +1,8 @@ export function defer void | Promise>( fn: T, -): T extends () => Promise ? { [Symbol.asyncDispose]: () => Promise } : { [Symbol.dispose]: () => void } { +): T extends () => Promise + ? { [Symbol.asyncDispose]: () => Promise } + : { [Symbol.dispose]: () => void } { return { [Symbol.dispose]() { fn() diff --git a/packages/opencode/src/util/eventloop.ts b/packages/opencode/src/util/eventloop.ts index 87f6eef41..f7096d383 100644 --- a/packages/opencode/src/util/eventloop.ts +++ b/packages/opencode/src/util/eventloop.ts @@ -4,11 +4,17 @@ export namespace EventLoop { export async function wait() { return new Promise((resolve) => { const check = () => { - const active = [...(process as any)._getActiveHandles(), ...(process as any)._getActiveRequests()] + const active = [ + ...(process as any)._getActiveHandles(), + ...(process as any)._getActiveRequests(), + ] Log.Default.info("eventloop", { active, }) - if ((process as any)._getActiveHandles().length === 0 && (process as any)._getActiveRequests().length === 0) { + if ( + (process as any)._getActiveHandles().length === 0 && + (process as any)._getActiveRequests().length === 0 + ) { resolve() } else { setImmediate(check) diff --git a/packages/opencode/src/util/lock.ts b/packages/opencode/src/util/lock.ts index 3aea64394..c503ddd33 100644 --- a/packages/opencode/src/util/lock.ts +++ b/packages/opencode/src/util/lock.ts @@ -39,7 +39,12 @@ export namespace Lock { } // Clean up empty locks - if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) { + if ( + lock.readers === 0 && + !lock.writer && + lock.waitingReaders.length === 0 && + lock.waitingWriters.length === 0 + ) { locks.delete(key) } } diff --git a/packages/opencode/src/util/rpc.ts b/packages/opencode/src/util/rpc.ts index 57c695c48..981cc0714 100644 --- a/packages/opencode/src/util/rpc.ts +++ b/packages/opencode/src/util/rpc.ts @@ -30,7 +30,10 @@ export namespace Rpc { } } return { - call(method: Method, input: Parameters[0]): Promise> { + call( + method: Method, + input: Parameters[0], + ): Promise> { const requestId = id++ return new Promise((resolve) => { pending.set(requestId, resolve) diff --git a/packages/opencode/src/util/wildcard.ts b/packages/opencode/src/util/wildcard.ts index 9b595a0a9..feda96961 100644 --- a/packages/opencode/src/util/wildcard.ts +++ b/packages/opencode/src/util/wildcard.ts @@ -15,7 +15,11 @@ export namespace Wildcard { } export function all(input: string, patterns: Record) { - const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + const sorted = pipe( + patterns, + Object.entries, + sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]), + ) let result = undefined for (const [pattern, value] of sorted) { if (match(input, pattern)) { @@ -26,8 +30,15 @@ export namespace Wildcard { return result } - export function allStructured(input: { head: string; tail: string[] }, patterns: Record) { - const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + export function allStructured( + input: { head: string; tail: string[] }, + patterns: Record, + ) { + const sorted = pipe( + patterns, + Object.entries, + sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]), + ) let result = undefined for (const [pattern, value] of sorted) { const parts = pattern.split(/\s+/) diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/opencode/sst-env.d.ts +++ b/packages/opencode/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/opencode/test/patch/patch.test.ts b/packages/opencode/test/patch/patch.test.ts index 51076c34f..020253bfe 100644 --- a/packages/opencode/test/patch/patch.test.ts +++ b/packages/opencode/test/patch/patch.test.ts @@ -6,23 +6,23 @@ import { tmpdir } from "os" describe("Patch namespace", () => { let tempDir: string - + beforeEach(async () => { tempDir = await fs.mkdtemp(path.join(tmpdir(), "patch-test-")) }) - + afterEach(async () => { // Clean up temp directory await fs.rm(tempDir, { recursive: true, force: true }) }) - + describe("parsePatch", () => { test("should parse simple add file patch", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Hello World *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) expect(result.hunks[0]).toEqual({ @@ -31,19 +31,19 @@ describe("Patch namespace", () => { contents: "Hello World", }) }) - + test("should parse delete file patch", () => { const patchText = `*** Begin Patch *** Delete File: old.txt *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) const hunk = result.hunks[0] expect(hunk.type).toBe("delete") expect(hunk.path).toBe("old.txt") }) - + test("should parse patch with multiple hunks", () => { const patchText = `*** Begin Patch *** Add File: new.txt @@ -54,13 +54,13 @@ describe("Patch namespace", () => { -new line +updated line *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(2) expect(result.hunks[0].type).toBe("add") expect(result.hunks[1].type).toBe("update") }) - + test("should parse file move operation", () => { const patchText = `*** Begin Patch *** Update File: old-name.txt @@ -69,7 +69,7 @@ describe("Patch namespace", () => { -Old content +New content *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) const hunk = result.hunks[0] @@ -79,21 +79,21 @@ describe("Patch namespace", () => { expect(hunk.move_path).toBe("new-name.txt") } }) - + test("should throw error for invalid patch format", () => { const invalidPatch = `This is not a valid patch` - + expect(() => Patch.parsePatch(invalidPatch)).toThrow("Invalid patch format") }) }) - + describe("maybeParseApplyPatch", () => { test("should parse direct apply_patch command", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Content *** End Patch` - + const result = Patch.maybeParseApplyPatch(["apply_patch", patchText]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) if (result.type === Patch.MaybeApplyPatch.Body) { @@ -101,17 +101,17 @@ describe("Patch namespace", () => { expect(result.args.hunks).toHaveLength(1) } }) - + test("should parse applypatch command", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Content *** End Patch` - + const result = Patch.maybeParseApplyPatch(["applypatch", patchText]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) }) - + test("should handle bash heredoc format", () => { const script = `apply_patch <<'PATCH' *** Begin Patch @@ -119,20 +119,20 @@ describe("Patch namespace", () => { +Content *** End Patch PATCH` - + const result = Patch.maybeParseApplyPatch(["bash", "-lc", script]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) if (result.type === Patch.MaybeApplyPatch.Body) { expect(result.args.hunks).toHaveLength(1) } }) - + test("should return NotApplyPatch for non-patch commands", () => { const result = Patch.maybeParseApplyPatch(["echo", "hello"]) expect(result.type).toBe(Patch.MaybeApplyPatch.NotApplyPatch) }) }) - + describe("applyPatch", () => { test("should add a new file", async () => { const patchText = `*** Begin Patch @@ -140,36 +140,39 @@ PATCH` +Hello World +This is a new file *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.modified).toHaveLength(0) expect(result.deleted).toHaveLength(0) - + const content = await fs.readFile(result.added[0], "utf-8") expect(content).toBe("Hello World\nThis is a new file") }) - + test("should delete an existing file", async () => { const filePath = path.join(tempDir, "to-delete.txt") await fs.writeFile(filePath, "This file will be deleted") - + const patchText = `*** Begin Patch *** Delete File: ${filePath} *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.deleted).toHaveLength(1) expect(result.deleted[0]).toBe(filePath) - - const exists = await fs.access(filePath).then(() => true).catch(() => false) + + const exists = await fs + .access(filePath) + .then(() => true) + .catch(() => false) expect(exists).toBe(false) }) - + test("should update an existing file", async () => { const filePath = path.join(tempDir, "to-update.txt") await fs.writeFile(filePath, "line 1\nline 2\nline 3\n") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ @@ -178,20 +181,20 @@ PATCH` +line 2 updated line 3 *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) expect(result.modified[0]).toBe(filePath) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("line 1\nline 2 updated\nline 3\n") }) - + test("should move and update a file", async () => { const oldPath = path.join(tempDir, "old-name.txt") const newPath = path.join(tempDir, "new-name.txt") await fs.writeFile(oldPath, "old content\n") - + const patchText = `*** Begin Patch *** Update File: ${oldPath} *** Move to: ${newPath} @@ -199,26 +202,29 @@ PATCH` -old content +new content *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) expect(result.modified[0]).toBe(newPath) - - const oldExists = await fs.access(oldPath).then(() => true).catch(() => false) + + const oldExists = await fs + .access(oldPath) + .then(() => true) + .catch(() => false) expect(oldExists).toBe(false) - + const newContent = await fs.readFile(newPath, "utf-8") expect(newContent).toBe("new content\n") }) - + test("should handle multiple operations in one patch", async () => { const file1 = path.join(tempDir, "file1.txt") const file2 = path.join(tempDir, "file2.txt") const file3 = path.join(tempDir, "file3.txt") - + await fs.writeFile(file1, "content 1") await fs.writeFile(file2, "content 2") - + const patchText = `*** Begin Patch *** Add File: ${file3} +new file content @@ -228,95 +234,98 @@ PATCH` +updated content 1 *** Delete File: ${file2} *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.modified).toHaveLength(1) expect(result.deleted).toHaveLength(1) }) - + test("should create parent directories when adding files", async () => { const nestedPath = path.join(tempDir, "deep", "nested", "file.txt") - + const patchText = `*** Begin Patch *** Add File: ${nestedPath} +Deep nested content *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.added[0]).toBe(nestedPath) - - const exists = await fs.access(nestedPath).then(() => true).catch(() => false) + + const exists = await fs + .access(nestedPath) + .then(() => true) + .catch(() => false) expect(exists).toBe(true) }) }) - + describe("error handling", () => { test("should throw error when updating non-existent file", async () => { const nonExistent = path.join(tempDir, "does-not-exist.txt") - + const patchText = `*** Begin Patch *** Update File: ${nonExistent} @@ -old line +new line *** End Patch` - + await expect(Patch.applyPatch(patchText)).rejects.toThrow() }) - + test("should throw error when deleting non-existent file", async () => { const nonExistent = path.join(tempDir, "does-not-exist.txt") - + const patchText = `*** Begin Patch *** Delete File: ${nonExistent} *** End Patch` - + await expect(Patch.applyPatch(patchText)).rejects.toThrow() }) }) - + describe("edge cases", () => { test("should handle empty files", async () => { const emptyFile = path.join(tempDir, "empty.txt") await fs.writeFile(emptyFile, "") - + const patchText = `*** Begin Patch *** Update File: ${emptyFile} @@ +First line *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(emptyFile, "utf-8") expect(content).toBe("First line\n") }) - + test("should handle files with no trailing newline", async () => { const filePath = path.join(tempDir, "no-newline.txt") await fs.writeFile(filePath, "no newline") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ -no newline +has newline now *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("has newline now\n") }) - + test("should handle multiple update chunks in single file", async () => { const filePath = path.join(tempDir, "multi-chunk.txt") await fs.writeFile(filePath, "line 1\nline 2\nline 3\nline 4\n") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ @@ -328,12 +337,12 @@ PATCH` -line 4 +LINE 4 *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("line 1\nLINE 2\nline 3\nLINE 4\n") }) }) -}) \ No newline at end of file +}) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index edce412c2..ebcee80df 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -13,7 +13,9 @@ function apiError(headers?: Record): MessageV2.APIError { describe("session.retry.getRetryDelayInMs", () => { test("doubles delay on each attempt when headers missing", () => { const error = apiError() - const delays = Array.from({ length: 7 }, (_, index) => SessionRetry.getRetryDelayInMs(error, index + 1)) + const delays = Array.from({ length: 7 }, (_, index) => + SessionRetry.getRetryDelayInMs(error, index + 1), + ) expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 32000, 64000, 128000]) }) diff --git a/packages/opencode/test/util/wildcard.test.ts b/packages/opencode/test/util/wildcard.test.ts index f7f1e1545..968b4f288 100644 --- a/packages/opencode/test/util/wildcard.test.ts +++ b/packages/opencode/test/util/wildcard.test.ts @@ -24,9 +24,12 @@ test("allStructured matches command sequences", () => { "git status*": "allow", } expect(Wildcard.allStructured({ head: "git", tail: ["status", "--short"] }, rules)).toBe("allow") - expect(Wildcard.allStructured({ head: "npm", tail: ["run", "build", "--watch"] }, { "npm run *": "allow" })).toBe( - "allow", - ) + expect( + Wildcard.allStructured( + { head: "npm", tail: ["run", "build", "--watch"] }, + { "npm run *": "allow" }, + ), + ).toBe("allow") expect(Wildcard.allStructured({ head: "ls", tail: ["-la"] }, rules)).toBeUndefined() }) @@ -51,5 +54,7 @@ test("allStructured handles sed flags", () => { expect(Wildcard.allStructured({ head: "sed", tail: ["-i", "file"] }, rules)).toBe("ask") expect(Wildcard.allStructured({ head: "sed", tail: ["-i.bak", "file"] }, rules)).toBe("ask") expect(Wildcard.allStructured({ head: "sed", tail: ["-n", "1p", "file"] }, rules)).toBe("allow") - expect(Wildcard.allStructured({ head: "sed", tail: ["-i", "-n", "/./p", "myfile.txt"] }, rules)).toBe("ask") + expect( + Wildcard.allStructured({ head: "sed", tail: ["-i", "-n", "/./p", "myfile.txt"] }, rules), + ).toBe("ask") }) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index c510b519b..9206efe1e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index f103749bd..1c8c6d2a1 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -151,7 +151,10 @@ export interface Hooks { input: { model: Model; provider: Provider; message: UserMessage }, output: { temperature: number; topP: number; options: Record }, ) => Promise - "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise + "permission.ask"?: ( + input: Permission, + output: { status: "ask" | "deny" | "allow" }, + ) => Promise "tool.execute.before"?: ( input: { tool: string; sessionID: string; callID: string }, output: { args: any }, diff --git a/packages/plugin/sst-env.d.ts b/packages/plugin/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/plugin/sst-env.d.ts +++ b/packages/plugin/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/script/sst-env.d.ts b/packages/script/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/script/sst-env.d.ts +++ b/packages/script/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/sdk/go/.github/workflows/ci.yml b/packages/sdk/go/.github/workflows/ci.yml index 4bf1e907c..0f5d45dc2 100644 --- a/packages/sdk/go/.github/workflows/ci.yml +++ b/packages/sdk/go/.github/workflows/ci.yml @@ -2,15 +2,15 @@ name: CI on: push: branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "generated" + - "codegen/**" + - "integrated/**" + - "stl-preview-head/**" + - "stl-preview-base/**" pull_request: branches-ignore: - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "stl-preview-head/**" + - "stl-preview-base/**" jobs: lint: diff --git a/packages/sdk/go/.release-please-manifest.json b/packages/sdk/go/.release-please-manifest.json index 4ad3fef33..5e39b9417 100644 --- a/packages/sdk/go/.release-please-manifest.json +++ b/packages/sdk/go/.release-please-manifest.json @@ -1,3 +1,3 @@ { ".": "0.18.0" -} \ No newline at end of file +} diff --git a/packages/sdk/go/CHANGELOG.md b/packages/sdk/go/CHANGELOG.md index 498a78029..937fbfdd0 100644 --- a/packages/sdk/go/CHANGELOG.md +++ b/packages/sdk/go/CHANGELOG.md @@ -6,7 +6,7 @@ Full Changelog: [v0.17.0...v0.18.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([0a7f5e7](https://github.com/sst/opencode-sdk-go/commit/0a7f5e710911506512a132ba39e0593c412beb77)) +- **api:** api update ([0a7f5e7](https://github.com/sst/opencode-sdk-go/commit/0a7f5e710911506512a132ba39e0593c412beb77)) ## 0.17.0 (2025-10-07) @@ -14,7 +14,7 @@ Full Changelog: [v0.16.2...v0.17.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([84a3df5](https://github.com/sst/opencode-sdk-go/commit/84a3df50a7ff3d87e5593e4f29dfb5d561f71cc3)) +- **api:** api update ([84a3df5](https://github.com/sst/opencode-sdk-go/commit/84a3df50a7ff3d87e5593e4f29dfb5d561f71cc3)) ## 0.16.2 (2025-09-26) @@ -22,7 +22,7 @@ Full Changelog: [v0.16.1...v0.16.2](https://github.com/sst/opencode-sdk-go/compa ### Bug Fixes -* bugfix for setting JSON keys with special characters ([ac9a36f](https://github.com/sst/opencode-sdk-go/commit/ac9a36feb1c185ebf766d76909d0b86ac805e8a6)) +- bugfix for setting JSON keys with special characters ([ac9a36f](https://github.com/sst/opencode-sdk-go/commit/ac9a36feb1c185ebf766d76909d0b86ac805e8a6)) ## 0.16.1 (2025-09-20) @@ -30,14 +30,13 @@ Full Changelog: [v0.16.0...v0.16.1](https://github.com/sst/opencode-sdk-go/compa ### Bug Fixes -* use slices.Concat instead of sometimes modifying r.Options ([12e8b40](https://github.com/sst/opencode-sdk-go/commit/12e8b40809071095b0abb9b8031686353c8ac149)) - +- use slices.Concat instead of sometimes modifying r.Options ([12e8b40](https://github.com/sst/opencode-sdk-go/commit/12e8b40809071095b0abb9b8031686353c8ac149)) ### Chores -* bump minimum go version to 1.22 ([1a61c5c](https://github.com/sst/opencode-sdk-go/commit/1a61c5cc7e8f68cc1b0c219738cab530cb6aa3a2)) -* do not install brew dependencies in ./scripts/bootstrap by default ([f6d3eaf](https://github.com/sst/opencode-sdk-go/commit/f6d3eafffc20e124bbfae6ac5ddc1b1122ad3e27)) -* update more docs for 1.22 ([a3d0b0f](https://github.com/sst/opencode-sdk-go/commit/a3d0b0f26ed92ce1a6433f5bcf37a6436d268ba5)) +- bump minimum go version to 1.22 ([1a61c5c](https://github.com/sst/opencode-sdk-go/commit/1a61c5cc7e8f68cc1b0c219738cab530cb6aa3a2)) +- do not install brew dependencies in ./scripts/bootstrap by default ([f6d3eaf](https://github.com/sst/opencode-sdk-go/commit/f6d3eafffc20e124bbfae6ac5ddc1b1122ad3e27)) +- update more docs for 1.22 ([a3d0b0f](https://github.com/sst/opencode-sdk-go/commit/a3d0b0f26ed92ce1a6433f5bcf37a6436d268ba5)) ## 0.16.0 (2025-09-17) @@ -45,7 +44,7 @@ Full Changelog: [v0.15.0...v0.16.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([46e978e](https://github.com/sst/opencode-sdk-go/commit/46e978e43aee733d5c1c09dc5be6d8ac2a752427)) +- **api:** api update ([46e978e](https://github.com/sst/opencode-sdk-go/commit/46e978e43aee733d5c1c09dc5be6d8ac2a752427)) ## 0.15.0 (2025-09-16) @@ -53,7 +52,7 @@ Full Changelog: [v0.14.0...v0.15.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f)) +- **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f)) ## 0.14.0 (2025-09-14) @@ -61,7 +60,7 @@ Full Changelog: [v0.13.0...v0.14.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77)) +- **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77)) ## 0.13.0 (2025-09-14) @@ -69,7 +68,7 @@ Full Changelog: [v0.12.0...v0.13.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c)) +- **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c)) ## 0.12.0 (2025-09-14) @@ -77,7 +76,7 @@ Full Changelog: [v0.11.0...v0.12.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2)) +- **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2)) ## 0.11.0 (2025-09-14) @@ -85,7 +84,7 @@ Full Changelog: [v0.10.0...v0.11.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2)) +- **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2)) ## 0.10.0 (2025-09-11) @@ -93,7 +92,7 @@ Full Changelog: [v0.9.0...v0.10.0](https://github.com/sst/opencode-sdk-go/compar ### Features -* **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50)) +- **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50)) ## 0.9.0 (2025-09-10) @@ -101,7 +100,7 @@ Full Changelog: [v0.8.0...v0.9.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c)) +- **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c)) ## 0.8.0 (2025-09-01) @@ -109,7 +108,7 @@ Full Changelog: [v0.7.0...v0.8.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d)) +- **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d)) ## 0.7.0 (2025-09-01) @@ -117,7 +116,7 @@ Full Changelog: [v0.6.0...v0.7.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4)) +- **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4)) ## 0.6.0 (2025-09-01) @@ -125,7 +124,7 @@ Full Changelog: [v0.5.0...v0.6.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8)) +- **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8)) ## 0.5.0 (2025-08-31) @@ -133,7 +132,7 @@ Full Changelog: [v0.4.0...v0.5.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604)) +- **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604)) ## 0.4.0 (2025-08-31) @@ -141,7 +140,7 @@ Full Changelog: [v0.3.0...v0.4.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559)) +- **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559)) ## 0.3.0 (2025-08-31) @@ -149,7 +148,7 @@ Full Changelog: [v0.2.0...v0.3.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da)) +- **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da)) ## 0.2.0 (2025-08-31) @@ -157,7 +156,7 @@ Full Changelog: [v0.1.0...v0.2.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c)) +- **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c)) ## 0.1.0 (2025-08-31) @@ -165,61 +164,59 @@ Full Changelog: [v0.0.1...v0.1.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6)) -* **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182)) -* **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6)) -* **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77)) -* **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b)) -* **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42)) -* **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f)) -* **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039)) -* **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e)) -* **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011)) -* **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088)) -* **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841)) -* **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065)) -* **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f)) -* **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7)) -* **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5)) -* **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e)) -* **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c)) -* **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96)) -* **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25)) -* **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743)) -* **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560)) -* **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b)) -* **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956)) -* **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2)) -* **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9)) -* **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10)) -* **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775)) -* **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf)) -* **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e)) -* **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52)) -* **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d)) -* **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed)) -* **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675)) -* **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397)) -* **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844)) -* **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3)) -* **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd)) - +- **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6)) +- **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182)) +- **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6)) +- **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77)) +- **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b)) +- **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42)) +- **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f)) +- **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039)) +- **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e)) +- **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011)) +- **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088)) +- **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841)) +- **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065)) +- **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f)) +- **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7)) +- **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5)) +- **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e)) +- **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c)) +- **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96)) +- **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25)) +- **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743)) +- **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560)) +- **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b)) +- **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956)) +- **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2)) +- **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9)) +- **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10)) +- **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775)) +- **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf)) +- **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e)) +- **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52)) +- **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d)) +- **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed)) +- **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675)) +- **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397)) +- **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844)) +- **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3)) +- **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd)) ### Bug Fixes -* **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f)) -* **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7)) -* close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5)) -* don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a)) - +- **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f)) +- **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7)) +- close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5)) +- don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a)) ### Chores -* **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d)) -* **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a)) -* **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797)) -* **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474)) -* lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff)) -* lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611)) -* sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3)) -* update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a)) +- **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d)) +- **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a)) +- **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797)) +- **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474)) +- lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff)) +- lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611)) +- sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3)) +- update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a)) diff --git a/packages/sdk/go/release-please-config.json b/packages/sdk/go/release-please-config.json index a38198eca..32960ce27 100644 --- a/packages/sdk/go/release-please-config.json +++ b/packages/sdk/go/release-please-config.json @@ -60,8 +60,5 @@ } ], "release-type": "go", - "extra-files": [ - "internal/version.go", - "README.md" - ] -} \ No newline at end of file + "extra-files": ["internal/version.go", "README.md"] +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 97830ba1b..bff4866a0 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/src/server.ts b/packages/sdk/js/src/server.ts index a09e14ab2..151477457 100644 --- a/packages/sdk/js/src/server.ts +++ b/packages/sdk/js/src/server.ts @@ -28,13 +28,17 @@ export async function createOpencodeServer(options?: ServerOptions) { options ?? {}, ) - const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], { - signal: options.signal, - env: { - ...process.env, - OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}), + const proc = spawn( + `opencode`, + [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], + { + signal: options.signal, + env: { + ...process.env, + OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}), + }, }, - }) + ) const url = await new Promise((resolve, reject) => { const id = setTimeout(() => { diff --git a/packages/sdk/js/sst-env.d.ts b/packages/sdk/js/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/sdk/js/sst-env.d.ts +++ b/packages/sdk/js/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/sdk/python/README.md b/packages/sdk/python/README.md index a17c36ab3..5d54434af 100644 --- a/packages/sdk/python/README.md +++ b/packages/sdk/python/README.md @@ -2,50 +2,59 @@ This package provides a Python SDK for the Opencode API. It is generated using openapi-python-client (not Stainless). - Documentation + - Full docs: see `mkdocs` site under `packages/sdk/python/docs/` - Preview locally: + ```bash uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml ``` Badges + - PyPI: https://img.shields.io/pypi/v/opencode-ai?style=flat-square Requirements + - Python 3.8+ - uv (recommended) -> https://docs.astral.sh/uv/ - openapi-python-client (invoked via `uvx`) Install uv + ```bash # macOS/Linux curl -LsSf https://astral.sh/uv/install.sh | sh ``` Set up the environment (from this directory) + ```bash uv sync --dev ``` Generate client code (from CLI-generated spec) + ```bash # From repository root OR from this directory uv run python packages/sdk/python/scripts/generate.py --source cli ``` Alternatively, fetch spec from a running server + ```bash uv run python packages/sdk/python/scripts/generate.py --source server --server-url http://localhost:4096/doc ``` This will: -1) Produce an OpenAPI spec from the local CLI or a running server -2) Run openapi-python-client (via `uvx`) to generate client code -3) Copy the generated Python package into src/opencode_ai + +1. Produce an OpenAPI spec from the local CLI or a running server +2. Run openapi-python-client (via `uvx`) to generate client code +3. Copy the generated Python package into src/opencode_ai Usage (after generation) + ```python from opencode_ai import OpenCodeClient @@ -77,6 +86,7 @@ client = OpenCodeClient(retries=2, backoff_factor=0.1) ``` Notes + - We intentionally do not use Stainless for the Python SDK. - The generator targets OpenAPI 3.1 emitted by the opencode server at /doc. - See scripts/generate.py for details and customization points. diff --git a/packages/sdk/python/docs/generation.md b/packages/sdk/python/docs/generation.md index f949760a1..2151e3709 100644 --- a/packages/sdk/python/docs/generation.md +++ b/packages/sdk/python/docs/generation.md @@ -3,10 +3,12 @@ The SDK is generated from the Opencode server's OpenAPI 3.1 spec. Two source modes are supported: + - CLI (default): runs `bun dev generate` to emit the OpenAPI JSON - Server: fetches `http://localhost:4096/doc` from a running server Generator command + ```bash # From repo root uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli @@ -15,5 +17,6 @@ uv run --project packages/sdk/python python packages/sdk/python/scripts/generate ``` Post-generation + - The generator injects `extras.py` (OpenCodeClient) and patches `__init__.py` to export it - Code is formatted with `ruff` (imports) and `black` diff --git a/packages/sdk/python/docs/index.md b/packages/sdk/python/docs/index.md index bc7b550f9..e0cfe5d5d 100644 --- a/packages/sdk/python/docs/index.md +++ b/packages/sdk/python/docs/index.md @@ -3,6 +3,7 @@ The official Python client for the Opencode API, generated from the OpenAPI spec and extended with ergonomic helpers. Highlights + - Provider-agnostic client generated from OpenAPI 3.1 - Thin convenience wrapper (OpenCodeClient) for common tasks - Sync and async SSE streaming for live event feeds diff --git a/packages/sdk/python/docs/installation.md b/packages/sdk/python/docs/installation.md index f66e217ae..fe48f5189 100644 --- a/packages/sdk/python/docs/installation.md +++ b/packages/sdk/python/docs/installation.md @@ -1,26 +1,31 @@ # Installation Requirements + - Python 3.8+ - uv (recommended) -> https://docs.astral.sh/uv/ Install uv + ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` Project setup + ```bash # From repo root or this directory uv sync --dev --project packages/sdk/python ``` Using pip (alternative) + ```bash pip install opencode-ai ``` Preview docs locally + ```bash # From repo root uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml diff --git a/packages/sdk/python/docs/publishing.md b/packages/sdk/python/docs/publishing.md index c598baa88..c05a12a33 100644 --- a/packages/sdk/python/docs/publishing.md +++ b/packages/sdk/python/docs/publishing.md @@ -3,6 +3,7 @@ Automated publishing runs on GitHub Releases. Workflow + - Create a new Release (the tag value becomes the package version) - The `publish-python-sdk` workflow will: - Generate the SDK from OpenAPI (CLI path) @@ -10,9 +11,11 @@ Workflow - Build wheel/sdist and upload to PyPI Prerequisites + - Repository secret: `PYPI_API_TOKEN` Manual publish + ```bash # TestPyPI REPOSITORY=testpypi PYPI_TOKEN=$TEST_PYPI_API_TOKEN \ diff --git a/packages/sdk/python/docs/testing.md b/packages/sdk/python/docs/testing.md index 3119035d0..e2c777e31 100644 --- a/packages/sdk/python/docs/testing.md +++ b/packages/sdk/python/docs/testing.md @@ -11,5 +11,6 @@ uv run --project packages/sdk/python pytest -q ``` Notes + - Integration test starts a headless opencode server via Bun in a subprocess - SSE behavior is validated using real streaming from the server diff --git a/packages/sdk/python/mkdocs.yml b/packages/sdk/python/mkdocs.yml index 25de28fb3..565eb7701 100644 --- a/packages/sdk/python/mkdocs.yml +++ b/packages/sdk/python/mkdocs.yml @@ -3,7 +3,7 @@ site_description: Official Python SDK for the Opencode API site_url: https://opencode.ai repo_url: https://github.com/sst/opencode repo_name: sst/opencode -edit_uri: '' +edit_uri: "" theme: name: material features: diff --git a/packages/slack/src/index.ts b/packages/slack/src/index.ts index 046f069e2..2a952b63e 100644 --- a/packages/slack/src/index.ts +++ b/packages/slack/src/index.ts @@ -19,7 +19,10 @@ const opencode = await createOpencode({ }) console.log("✅ Opencode server ready") -const sessions = new Map() +const sessions = new Map< + string, + { client: any; server: any; sessionId: string; channel: string; thread: string } +>() ;(async () => { const events = await opencode.client.event.subscribe() for await (const event of events.stream) { @@ -81,7 +84,10 @@ app.message(async ({ message, say }) => { if (createResult.error) { console.error("❌ Failed to create session:", createResult.error) - await say({ text: "Sorry, I had trouble creating a session. Please try again.", thread_ts: thread }) + await say({ + text: "Sorry, I had trouble creating a session. Please try again.", + thread_ts: thread, + }) return } @@ -108,7 +114,10 @@ app.message(async ({ message, say }) => { if (result.error) { console.error("❌ Failed to send message:", result.error) - await say({ text: "Sorry, I had trouble processing your message. Please try again.", thread_ts: thread }) + await say({ + text: "Sorry, I had trouble processing your message. Please try again.", + thread_ts: thread, + }) return } diff --git a/packages/slack/sst-env.d.ts b/packages/slack/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/slack/sst-env.d.ts +++ b/packages/slack/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/ui/src/assets/favicon/site.webmanifest b/packages/ui/src/assets/favicon/site.webmanifest index f7522f8f3..41290e840 100644 --- a/packages/ui/src/assets/favicon/site.webmanifest +++ b/packages/ui/src/assets/favicon/site.webmanifest @@ -18,4 +18,4 @@ "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" -} \ No newline at end of file +} diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx index 2009a430b..ac9abfdab 100644 --- a/packages/ui/src/components/checkbox.tsx +++ b/packages/ui/src/components/checkbox.tsx @@ -9,7 +9,14 @@ export interface CheckboxProps extends ParentProps local.children) return ( @@ -35,7 +42,9 @@ export function Checkbox(props: CheckboxProps) { - {local.description} + + {local.description} +
    diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx index ce7a4b3ac..fdbb9022d 100644 --- a/packages/ui/src/components/dialog.tsx +++ b/packages/ui/src/components/dialog.tsx @@ -38,7 +38,11 @@ export function DialogRoot(props: DialogProps) { return ( - + {props.trigger} diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index b80398e1d..02b473238 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -269,8 +269,8 @@ textarea, crash when using `color-mix(…)` with `currentcolor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194) */ -@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) - /* Safari 17+ */ { +@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or + (contain-intrinsic-size: 1px) /* Safari 17+ */ { ::placeholder { color: color-mix(in oklab, currentcolor 50%, transparent); } diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index 527c43109..a5f982d6f 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/packages/ui/src/styles/tailwind/colors.css @@ -232,4 +232,4 @@ --color-border-weaker-focus: var(--border-weaker-focus); --color-button-ghost-hover: var(--button-ghost-hover); --color-button-ghost-hover2: var(--button-ghost-hover2); -} \ No newline at end of file +} diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 20c43b10e..6187eef9d 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -56,23 +56,18 @@ 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12); --shadow-xs-border: - 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), - 0 1px 2px -1px rgba(19, 16, 16, 0.04), - 0 1px 2px 0 rgba(19, 16, 16, 0.06), - 0 1px 3px 0 rgba(19, 16, 16, 0.08); + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.04), + 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); --shadow-xs-border-select: 0 0 0 3px var(--border-weak-selected, rgba(1, 103, 255, 0.29)), 0 0 0 1px var(--border-selected, rgba(0, 74, 255, 0.99)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12); --shadow-xs-border-focus: - 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), - 0 1px 2px -1px rgba(19, 16, 16, 0.25), - 0 1px 2px 0 rgba(19, 16, 16, 0.08), - 0 1px 3px 0 rgba(19, 16, 16, 0.12), - 0 0 0 2px var(--background-weak, #F1F0F0), + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), + 0 0 0 2px var(--background-weak, #f1f0f0), 0 0 0 3px var(--border-selected, rgba(0, 74, 255, 0.99)); - --text-mix-blend-mode: multiply; } @@ -413,7 +408,7 @@ --text-on-brand-weaker: var(--smoke-dark-alpha-8); --text-on-brand-strong: var(--smoke-dark-alpha-12); --button-secondary-base: var(--smoke-dark-4); - --button-secondary-hover: #2A2727; + --button-secondary-hover: #2a2727; --border-base: var(--smoke-dark-alpha-7); --border-hover: var(--smoke-dark-alpha-8); --border-active: var(--smoke-dark-alpha-9); diff --git a/packages/ui/sst-env.d.ts b/packages/ui/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/ui/sst-env.d.ts +++ b/packages/ui/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 440aa8f9b..c35314407 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -13,16 +13,9 @@ "module": "ESNext", "moduleResolution": "bundler", "noEmit": true, - "lib": [ - "es2022", - "dom", - "dom.iterable" - ], + "lib": ["es2022", "dom", "dom.iterable"], // Type Checking & Safety "strict": true, - "types": [ - "vite/client", - "bun" - ] + "types": ["vite/client", "bun"] } } diff --git a/packages/web/config.mjs b/packages/web/config.mjs index 5e2c8d3e4..7adb147cd 100644 --- a/packages/web/config.mjs +++ b/packages/web/config.mjs @@ -2,7 +2,8 @@ const stage = process.env.SST_STAGE || "dev" export default { url: stage === "production" ? "https://opencode.ai" : `https://${stage}.opencode.ai`, - console: stage === "production" ? "https://opencode.ai/auth" : `https://${stage}.opencode.ai/auth`, + console: + stage === "production" ? "https://opencode.ai/auth" : `https://${stage}.opencode.ai/auth`, email: "contact@anoma.ly", socialCard: "https://social-cards.sst.dev", github: "https://github.com/sst/opencode", diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 062449712..486b6e44b 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,4 +1,14 @@ -import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" +import { + For, + Show, + onMount, + Suspense, + onCleanup, + createMemo, + createSignal, + SuspenseList, + createEffect, +} from "solid-js" import { DateTime } from "luxon" import { createStore, reconcile, unwrap } from "solid-js/store" import { IconArrowDown } from "./icons" @@ -66,8 +76,13 @@ export default function Share(props: { id: string; api: string; info: Session.In }, messages: {}, }) - const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) - const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) + const messages = createMemo(() => + Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id)), + ) + const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>([ + "disconnected", + "Disconnected", + ]) createEffect(() => { console.log(unwrap(store)) }) @@ -330,7 +345,9 @@ export default function Share(props: { id: string; api: string; info: Session.In
    {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)}
    @@ -352,7 +369,10 @@ export default function Share(props: { id: string; api: string; info: Session.In if (x.type === "text" && x.synthetic === true) return false if (x.type === "tool" && x.tool === "todoread") return false if (x.type === "text" && !x.text) return false - if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) + if ( + x.type === "tool" && + (x.state.status === "pending" || x.state.status === "running") + ) return false return true }), @@ -364,7 +384,8 @@ export default function Share(props: { id: string; api: string; info: Session.In {(part, partIndex) => { const last = createMemo( () => - data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + data().messages.length === msgIndex() + 1 && + filteredParts().length === partIndex() + 1, ) onMount(() => { @@ -381,7 +402,9 @@ export default function Share(props: { id: string; api: string; info: Session.In } }) - return + return ( + + ) }} @@ -406,7 +429,11 @@ export default function Share(props: { id: string; api: string; info: Session.In
  • Input Tokens - {data().tokens.input ? {data().tokens.input} : } + {data().tokens.input ? ( + {data().tokens.input} + ) : ( + + )}
  • Output Tokens @@ -560,7 +587,8 @@ export function fromV1(v1: Message.Info): MessageWithParts { } } - const { title, time, ...metadata } = v1.metadata.tool[part.toolInvocation.toolCallId] + const { title, time, ...metadata } = + v1.metadata.tool[part.toolInvocation.toolCallId] if (part.toolInvocation.state === "call") { return { status: "running", diff --git a/packages/web/src/components/icons/index.tsx b/packages/web/src/components/icons/index.tsx index 62445611f..9aef7a927 100644 --- a/packages/web/src/components/icons/index.tsx +++ b/packages/web/src/components/icons/index.tsx @@ -4418,7 +4418,14 @@ export function IconMultiSelect(props: JSX.SvgSVGAttributes) { } export function IconSettings(props: JSX.SvgSVGAttributes) { return ( - + +
    { diff --git a/packages/web/src/components/share/content-code.tsx b/packages/web/src/components/share/content-code.tsx index 2f383b8be..0f9d28fa3 100644 --- a/packages/web/src/components/share/content-code.tsx +++ b/packages/web/src/components/share/content-code.tsx @@ -26,7 +26,11 @@ export function ContentCode(props: Props) { ) return ( -
    +
    ) } diff --git a/packages/web/src/components/share/content-diff.tsx b/packages/web/src/components/share/content-diff.tsx index 9ccd554d0..79c2723cb 100644 --- a/packages/web/src/components/share/content-diff.tsx +++ b/packages/web/src/components/share/content-diff.tsx @@ -124,7 +124,9 @@ export function ContentDiff(props: Props) { // Collect consecutive modified/removed/added rows while ( i < currentRows.length && - (currentRows[i].type === "modified" || currentRows[i].type === "removed" || currentRows[i].type === "added") + (currentRows[i].type === "modified" || + currentRows[i].type === "removed" || + currentRows[i].type === "added") ) { const row = currentRows[i] if (row.left && (row.type === "removed" || row.type === "modified")) { @@ -162,10 +164,16 @@ export function ContentDiff(props: Props) {
    {rows().map((r) => (
    -
    +
    -
    +
    @@ -176,7 +184,11 @@ export function ContentDiff(props: Props) { {mobileRows().map((block) => (
    {block.lines.map((line) => ( -
    +
    ))} diff --git a/packages/web/src/components/share/copy-button.tsx b/packages/web/src/components/share/copy-button.tsx index 892d5553f..6f51bb4d8 100644 --- a/packages/web/src/components/share/copy-button.tsx +++ b/packages/web/src/components/share/copy-button.tsx @@ -21,7 +21,11 @@ export function CopyButton(props: CopyButtonProps) { return (
    ) diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index f7a6a9304..6ad18273b 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -1,6 +1,15 @@ import map from "lang-map" import { DateTime } from "luxon" -import { For, Show, Match, Switch, type JSX, createMemo, createSignal, type ParentProps } from "solid-js" +import { + For, + Show, + Match, + Switch, + type JSX, + createMemo, + createSignal, + type ParentProps, +} from "solid-js" import { IconHashtag, IconSparkles, @@ -19,7 +28,14 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic, IconBrain } from "../icons/custom" +import { + IconMeta, + IconRobot, + IconOpenAI, + IconGemini, + IconAnthropic, + IconBrain, +} from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -79,7 +95,11 @@ export function Part(props: PartProps) { {(model) => } @@ -147,7 +167,9 @@ export function Part(props: PartProps) { DateTime.DATETIME_FULL_WITH_SECONDS, )} > - {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} + {DateTime.fromMillis(props.message.time.completed).toLocaleString( + DateTime.DATETIME_MED, + )} )}
    @@ -343,7 +365,10 @@ function getShikiLang(filename: string) { return type ? (overrides[type] ?? type) : "plaintext" } -function getDiagnostics(diagnosticsByFile: Record, currentFile: string): JSX.Element[] { +function getDiagnostics( + diagnosticsByFile: Record, + currentFile: string, +): JSX.Element[] { const result: JSX.Element[] = [] if (diagnosticsByFile === undefined || diagnosticsByFile[currentFile] === undefined) return result @@ -397,7 +422,9 @@ export function TodoWriteTool(props: ToolProps) { completed: 2, } const todos = createMemo(() => - ((props.state.input?.todos ?? []) as Todo[]).slice().sort((a, b) => priority[a.status] - priority[b.status]), + ((props.state.input?.todos ?? []) as Todo[]) + .slice() + .sort((a, b) => priority[a.status] - priority[b.status]), ) const starting = () => todos().every((t: Todo) => t.status === "pending") const finished = () => todos().every((t: Todo) => t.status === "completed") @@ -439,13 +466,23 @@ export function GrepTool(props: ToolProps) { 0}> - +
    @@ -505,7 +542,9 @@ export function WebFetchTool(props: ToolProps) { } export function ReadTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd), + ) return ( <> @@ -522,7 +561,10 @@ export function ReadTool(props: ToolProps) { - + @@ -537,8 +579,12 @@ export function ReadTool(props: ToolProps) { } export function WriteTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd)) - const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd), + ) + const diagnostics = createMemo(() => + getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath), + ) return ( <> @@ -558,7 +604,10 @@ export function WriteTool(props: ToolProps) { - + @@ -568,8 +617,12 @@ export function WriteTool(props: ToolProps) { } export function EditTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input.filePath, props.message.path.cwd)) - const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input.filePath, props.message.path.cwd), + ) + const diagnostics = createMemo(() => + getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath), + ) return ( <> @@ -586,7 +639,10 @@ export function EditTool(props: ToolProps) {
    - +
    @@ -619,7 +675,11 @@ export function GlobTool(props: ToolProps) { 0}>
    @@ -642,7 +702,12 @@ function ResultsButton(props: ResultsButtonProps) { return ( <> -
    -
    - -
    New session
    -
    - -
    - {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
    -
    -
    - -
    - Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
    + +
    New session
    +
    + +
    + {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)}
    - } - > - {(_) => { - return ( -
    -
    +
    + +
    + Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} + +
    +
    +
    + } + > + {(_) => { + return ( +
    +
    +
    + + + +
    +
    1}>
    + } + > + +
    + ) + }} + - {/* */} + + +
    +
    +
    +
    +
    All changes
    +
    + + + {(diff) => ( + + + +
    +
    + +
    + + {getDirectory(diff.file)}‎ + + {getFilename(diff.file)} +
    +
    +
    + + +
    +
    +
    +
    + + + +
    + )} +
    +
    +
    +
    +
    +
    {(tab) => { const [file] = createResource( diff --git a/packages/ui/src/components/accordion.tsx b/packages/ui/src/components/accordion.tsx index 535d38e3d..02f00b7be 100644 --- a/packages/ui/src/components/accordion.tsx +++ b/packages/ui/src/components/accordion.tsx @@ -1,9 +1,11 @@ import { Accordion as Kobalte } from "@kobalte/core/accordion" -import { splitProps } from "solid-js" +import { createSignal, splitProps } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" export interface AccordionProps extends ComponentProps {} -export interface AccordionItemProps extends ComponentProps {} +export interface AccordionItemProps extends ComponentProps { + defaultOpen?: boolean +} export interface AccordionHeaderProps extends ComponentProps {} export interface AccordionTriggerProps extends ComponentProps {} export interface AccordionContentProps extends ComponentProps {} @@ -23,11 +25,14 @@ function AccordionRoot(props: AccordionProps) { } function AccordionItem(props: AccordionItemProps) { - const [split, rest] = splitProps(props, ["class", "classList"]) + const [split, rest] = splitProps(props, ["class", "classList", "defaultOpen"]) + const [open, setOpen] = createSignal(split.defaultOpen ?? false) return ( 0 : true}> -
    +
    diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 082bbea90..b61a54fe1 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -153,7 +153,10 @@ const newIcons = { stop: ``, enter: ``, "layout-left": ``, + "layout-right": ``, "speech-bubble": ``, + "align-right": ``, + expand: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx index c3d1947d3..e3784ed8e 100644 --- a/packages/ui/src/components/tooltip.tsx +++ b/packages/ui/src/components/tooltip.tsx @@ -29,7 +29,7 @@ export function Tooltip(props: TooltipProps) { }) return ( - + {c()} From 957c43aa09a2ceea0a9544fde4d0e2d21d91863f Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:34:47 -0600 Subject: [PATCH 092/218] fix(desktop): review tab padding --- packages/desktop/src/pages/session.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 9f72191bc..22f9c47f5 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -728,7 +728,7 @@ export default function Page() { "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true, }} > -
    +
    All changes
    From 61c4747fbe6b5368b0b752b973c0da3c4ee9f5f1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:58:45 -0600 Subject: [PATCH 093/218] fix(desktop): diff highlight rendering --- bun.lock | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 68ac9692f..cf47b5efe 100644 --- a/bun.lock +++ b/bun.lock @@ -365,7 +365,7 @@ "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.4.2", + "@pierre/precision-diffs": "0.4.4", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "@tsconfig/bun": "1.0.9", @@ -1034,7 +1034,7 @@ "@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="], - "@pierre/precision-diffs": ["@pierre/precision-diffs@0.4.2", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/transformers": "3.14.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.14.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-C6LbruH24BCp4awI47D5iMtVaZZD6GkzqBoDw+Sfu7DB3hC9y/rZr1C2BD7AUzAKwByTfFnh16Zp11ipfPqLKw=="], + "@pierre/precision-diffs": ["@pierre/precision-diffs@0.4.4", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/transformers": "3.14.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.14.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-9bhWs+hsz1i0/SMIrzce+fFrSec8aLIFrJYTGHATlynmQovngIWz1Gc+XwGigvY4+zSMksrGPzO5HiaNlvRqtQ=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], diff --git a/package.json b/package.json index e0bf476c6..a5e1630f9 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@tsconfig/bun": "1.0.9", "@cloudflare/workers-types": "4.20251008.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.4.2", + "@pierre/precision-diffs": "0.4.4", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "diff": "8.0.2", From 21957406ffb6f6bad3528b9d9b894e3e3b726a79 Mon Sep 17 00:00:00 2001 From: Jay V Date: Thu, 6 Nov 2025 17:08:48 -0500 Subject: [PATCH 094/218] docs: add Deep Infra provider documentation --- packages/web/src/content/docs/providers.mdx | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/web/src/content/docs/providers.mdx b/packages/web/src/content/docs/providers.mdx index 6409acee1..6a7520e56 100644 --- a/packages/web/src/content/docs/providers.mdx +++ b/packages/web/src/content/docs/providers.mdx @@ -301,6 +301,42 @@ Or if you already have an API key, you can select **Manually enter API Key** and --- +### Deep Infra + +1. Head over to the [Deep Infra dashboard](https://deepinfra.com/dash), create an account, and generate an API key. + +2. Run `opencode auth login` and select **Deep Infra**. + + ```bash + $ opencode auth login + + ┌ Add credential + │ + ◆ Select provider + │ ● Deep Infra + │ ... + └ + ``` + +3. Enter your Deep Infra API key. + + ```bash + $ opencode auth login + + ┌ Add credential + │ + ◇ Select provider + │ Deep Infra + │ + ◇ Enter your API key + │ _ + └ + ``` + +4. Run the `/models` command to select a model. + +--- + ### Fireworks AI 1. Head over to the [Fireworks AI console](https://app.fireworks.ai/), create an account, and click **Create API Key**. From a2ab01931703aaac325a73e3948187c24c529207 Mon Sep 17 00:00:00 2001 From: Nicolai van der Smagt Date: Thu, 6 Nov 2025 23:53:02 +0100 Subject: [PATCH 095/218] fix: resolve Mistral API compatibility issues (#2440) Co-authored-by: Aiden Cline --- packages/opencode/src/provider/transform.ts | 86 +++++++++++++++++---- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 9e095f5bf..9a955f5c6 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -3,21 +3,77 @@ import { unique } from "remeda" import type { JSONSchema } from "zod/v4/core" export namespace ProviderTransform { - function normalizeToolCallIds(msgs: ModelMessage[]): ModelMessage[] { - return msgs.map((msg) => { - if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { - msg.content = msg.content.map((part) => { - if ((part.type === "tool-call" || part.type === "tool-result") && "toolCallId" in part) { - return { - ...part, - toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), + function normalizeMessages( + msgs: ModelMessage[], + providerID: string, + modelID: string, + ): ModelMessage[] { + if (modelID.includes("claude")) { + return msgs.map((msg) => { + if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { + msg.content = msg.content.map((part) => { + if ( + (part.type === "tool-call" || part.type === "tool-result") && + "toolCallId" in part + ) { + return { + ...part, + toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), + } } - } - return part - }) + return part + }) + } + return msg + }) + } + if (providerID === "mistral" || modelID.toLowerCase().includes("mistral")) { + const result: ModelMessage[] = [] + for (let i = 0; i < msgs.length; i++) { + const msg = msgs[i] + const prevMsg = msgs[i - 1] + const nextMsg = msgs[i + 1] + + if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { + msg.content = msg.content.map((part) => { + if ( + (part.type === "tool-call" || part.type === "tool-result") && + "toolCallId" in part + ) { + // Mistral requires alphanumeric tool call IDs with exactly 9 characters + const normalizedId = part.toolCallId + .replace(/[^a-zA-Z0-9]/g, "") // Remove non-alphanumeric characters + .substring(0, 9) // Take first 9 characters + .padEnd(9, "0") // Pad with zeros if less than 9 characters + + return { + ...part, + toolCallId: normalizedId, + } + } + return part + }) + } + + result.push(msg) + + // Fix message sequence: tool messages cannot be followed by user messages + if (msg.role === "tool" && nextMsg?.role === "user") { + result.push({ + role: "assistant", + content: [ + { + type: "text", + text: "Done.", + }, + ], + }) + } } - return msg - }) + return result + } + + return msgs } function applyCaching(msgs: ModelMessage[], providerID: string): ModelMessage[] { @@ -64,9 +120,7 @@ export namespace ProviderTransform { } export function message(msgs: ModelMessage[], providerID: string, modelID: string) { - if (modelID.includes("claude")) { - msgs = normalizeToolCallIds(msgs) - } + msgs = normalizeMessages(msgs, providerID, modelID) if (providerID === "anthropic" || modelID.includes("anthropic") || modelID.includes("claude")) { msgs = applyCaching(msgs, providerID) } From 3ba7e243d0771982b23d3d0d54fcbe3e02cb86de Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 6 Nov 2025 18:00:09 -0500 Subject: [PATCH 096/218] system theme (#4010) --- bun.lock | 20 +- packages/opencode/package.json | 4 +- .../cmd/tui/component/dialog-theme-list.tsx | 12 +- .../src/cli/cmd/tui/context/theme.tsx | 1223 ++++++++++------- .../src/cli/cmd/tui/routes/session/index.tsx | 1 + .../opencode/src/cli/cmd/tui/util/terminal.ts | 114 ++ 6 files changed, 843 insertions(+), 531 deletions(-) create mode 100644 packages/opencode/src/cli/cmd/tui/util/terminal.ts diff --git a/bun.lock b/bun.lock index cf47b5efe..226cf3624 100644 --- a/bun.lock +++ b/bun.lock @@ -185,8 +185,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.36", - "@opentui/solid": "0.1.36", + "@opentui/core": "0.0.0-20251106-788e97e4", + "@opentui/solid": "0.0.0-20251106-788e97e4", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -962,21 +962,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.1.36", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.36", "@opentui/core-darwin-x64": "0.1.36", "@opentui/core-linux-arm64": "0.1.36", "@opentui/core-linux-x64": "0.1.36", "@opentui/core-win32-arm64": "0.1.36", "@opentui/core-win32-x64": "0.1.36", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-urDrj33udJ0dJGkZv+T5U0mCFBOOvUt9Tvqkrj8aRvi6kN0Bc5d2COuWcpAKo0TO9/PvjSwHC+CMnw2Sr46/ug=="], + "@opentui/core": ["@opentui/core@0.0.0-20251106-788e97e4", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-darwin-x64": "0.0.0-20251106-788e97e4", "@opentui/core-linux-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-linux-x64": "0.0.0-20251106-788e97e4", "@opentui/core-win32-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-win32-x64": "0.0.0-20251106-788e97e4", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-Es2Oe7/J/yb58e0jjq/04pV9Mekx6hM4go4C5uTiZksX3asfIGWk553cuf5WlWj0PDlVnC+s7Nnayi/NbLJ5jQ=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.36", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/fb0k1H0CeTroVt2UoEAcVrEx1cIYy4B2zfX0MrwUkIfXi36aoIBnisBeYvyCpsQfxFAkyLYCCA3NzaYEyC5hg=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251106-788e97e4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EOO8SSIYJBIh+Sd9bgVTiQmt+TEJmfg65/oym54J4zfDtCYlAqSaLcRnDe4TzB+4hejV9of8etrG3ZZACBJT+A=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.36", "", { "os": "darwin", "cpu": "x64" }, "sha512-PZMydJbSDUoEWqZsyEV8+FSwMT+r7mWFL0ABgdALI3AOrSr7Z8dMcRnFWl8LhriuHS589THvETJEN28L4q/E2Q=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251106-788e97e4", "", { "os": "darwin", "cpu": "x64" }, "sha512-MUTt7CDbzL2afNGK8gJ4jUZd+AHiOUJEO0eJGDSfWU8DUs0zv8XoLZfaI5PPbkUPEL/7CEBMARAAiwfRtoG/4A=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.36", "", { "os": "linux", "cpu": "arm64" }, "sha512-ATR+vdtraZEC/gHR1mQa/NYPlqFNBpsnnJAGepQmcxm85VceLYM701QaaIgNAwyYXiP6RQN1ZCv06MD1Ph1m4w=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251106-788e97e4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Zi1EzLCzooRfYoQnN/Dz8OxzrpRXByny8SJqhdO9ZP2mYX72yJ3AhUUW1Sl6YSzVi0H+QIKj7g+RX2KfsXIGFg=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.36", "", { "os": "linux", "cpu": "x64" }, "sha512-INsnPtcZVx68C+0Vd0L9+akDwNbWblUDqLmY9CftfmeLFubzvJXNRYTBvr7lX68fcst6Ho+0beUxyUoClKc0rg=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251106-788e97e4", "", { "os": "linux", "cpu": "x64" }, "sha512-/E0XEBVzO4JEEhJGzfURF2tPxDE2oTODxlgNYYB1QbAuOsLcV69uSrwAjo1TxuIn4P78tBR+ZOlmONjroPqfbQ=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.36", "", { "os": "win32", "cpu": "arm64" }, "sha512-x9lDZTL+xE8jsG1hP4pdsqCsZBu77JNR/ze5F7ZQkYQEC6Zl/XJtL1YT08nUlWOu4NMSws2xXV0lS/sJkbEgPA=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251106-788e97e4", "", { "os": "win32", "cpu": "arm64" }, "sha512-En/29cgpYVvzlrQ7fAoP+EUdzmczgMzBIGGM0RuLi2hmCmCqyMtOJ0EJUh9UXa5jYIXNGOP49sIP6bUBbvXt7g=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.36", "", { "os": "win32", "cpu": "x64" }, "sha512-WVU+qtAfJe8ikPWbw8Hfli15GuQTMKiceTkF5lql5AQYy7PKYtGTzWszxOZKeUU1/eEd2X4REi8Bn0TprEMxYw=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251106-788e97e4", "", { "os": "win32", "cpu": "x64" }, "sha512-2lu0bgEi+k/1c9VHQFg3wjVxMgQnuZhs/6sDDpxk9eNS3fuHEJfZi0PFJQk2J4IFQL61nzukOvJKgYDWQvKB1g=="], - "@opentui/solid": ["@opentui/solid@0.1.36", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.36", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-oHI01kZgyNecvXRFyQKJEDC5TCcsvfTPxHCa/XjbcZzH2qE2rfYMUF0mpwlLqoY9b3pm3w7Tpa8upzi1euBGJg=="], + "@opentui/solid": ["@opentui/solid@0.0.0-20251106-788e97e4", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251106-788e97e4", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-82rFS6BB60rJZU5Ad8Wf58V6HaMSkpnjciizkv3vsjJc9hvIAwLRNYqPypQB+etypuELhYMzzaVqt+wUsPHSqQ=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 34c9081c5..b9e2c307b 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -54,8 +54,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.1.36", - "@opentui/solid": "0.1.36", + "@opentui/core": "0.0.0-20251106-788e97e4", + "@opentui/solid": "0.0.0-20251106-788e97e4", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-theme-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-theme-list.tsx index 60411e562..5240603f8 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-theme-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-theme-list.tsx @@ -1,26 +1,24 @@ import { DialogSelect, type DialogSelectRef } from "../ui/dialog-select" -import { THEMES, useTheme } from "../context/theme" +import { useTheme } from "../context/theme" import { useDialog } from "../ui/dialog" import { onCleanup, onMount } from "solid-js" export function DialogThemeList() { const theme = useTheme() - const options = Object.keys(THEMES).map((value) => ({ + const options = Object.keys(theme.all()).map((value) => ({ title: value, - value: value as keyof typeof THEMES, + value: value, })) const dialog = useDialog() let confirmed = false - let ref: DialogSelectRef + let ref: DialogSelectRef const initial = theme.selected onMount(() => { - // highlight the first theme in the list when we open it for UX - theme.set(Object.keys(THEMES)[0] as keyof typeof THEMES) + theme.set(Object.keys(theme.all())[0]) }) onCleanup(() => { - // if we close the dialog without confirming, reset back to the initial theme if (!confirmed) theme.set(initial) }) diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx index 93eae6c21..a609bcc94 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx @@ -1,5 +1,5 @@ -import { SyntaxStyle, RGBA } from "@opentui/core" -import { createMemo, createSignal } from "solid-js" +import { SyntaxStyle, RGBA, type TerminalColors } from "@opentui/core" +import { createMemo } from "solid-js" import { useSync } from "@tui/context/sync" import { createSimpleContext } from "./helper" import aura from "./theme/aura.json" with { type: "json" } @@ -26,6 +26,8 @@ import tokyonight from "./theme/tokyonight.json" with { type: "json" } import vesper from "./theme/vesper.json" with { type: "json" } import zenburn from "./theme/zenburn.json" with { type: "json" } import { useKV } from "./kv" +import { useRenderer } from "@opentui/solid" +import { createStore } from "solid-js/store" type Theme = { primary: RGBA @@ -86,14 +88,14 @@ type Variant = { dark: HexColor | RefName light: HexColor | RefName } -type ColorValue = HexColor | RefName | Variant +type ColorValue = HexColor | RefName | Variant | RGBA type ThemeJson = { $schema?: string defs?: Record theme: Record } -export const THEMES: Record = { +export const DEFAULT_THEMES: Record = { aura, ayu, catppuccin, @@ -122,6 +124,7 @@ export const THEMES: Record = { function resolveTheme(theme: ThemeJson, mode: "dark" | "light") { const defs = theme.defs ?? {} function resolveColor(c: ColorValue): RGBA { + if (c instanceof RGBA) return c if (typeof c === "string") return c.startsWith("#") ? RGBA.fromHex(c) : resolveColor(defs[c]) return resolveColor(c[mode]) } @@ -137,514 +140,27 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ init: (props: { mode: "dark" | "light" }) => { const sync = useSync() const kv = useKV() + const [store, setStore] = createStore({ + themes: DEFAULT_THEMES, + mode: props.mode, + active: (sync.data.config.theme ?? kv.get("theme", "opencode")) as string, + }) - const [theme, setTheme] = createSignal(sync.data.config.theme ?? kv.get("theme", "opencode")) - const [mode, setMode] = createSignal(props.mode) + const renderer = useRenderer() + renderer + .getPalette({ + size: 16, + }) + .then((colors) => { + if (!colors.palette[0]) return + setStore("themes", "system", generateSystem(colors, store.mode)) + }) const values = createMemo(() => { - return resolveTheme(THEMES[theme()] ?? THEMES.opencode, mode()) + return resolveTheme(store.themes[store.active] ?? store.themes.opencode, store.mode) }) - const syntax = createMemo(() => { - return SyntaxStyle.fromTheme([ - { - scope: ["prompt"], - style: { - foreground: values().accent, - }, - }, - { - scope: ["extmark.file"], - style: { - foreground: values().warning, - bold: true, - }, - }, - { - scope: ["extmark.agent"], - style: { - foreground: values().secondary, - bold: true, - }, - }, - { - scope: ["extmark.paste"], - style: { - foreground: values().background, - background: values().warning, - bold: true, - }, - }, - { - scope: ["comment"], - style: { - foreground: values().syntaxComment, - italic: true, - }, - }, - { - scope: ["comment.documentation"], - style: { - foreground: values().syntaxComment, - italic: true, - }, - }, - { - scope: ["string", "symbol"], - style: { - foreground: values().syntaxString, - }, - }, - { - scope: ["number", "boolean"], - style: { - foreground: values().syntaxNumber, - }, - }, - { - scope: ["character.special"], - style: { - foreground: values().syntaxString, - }, - }, - { - scope: ["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"], - style: { - foreground: values().syntaxKeyword, - italic: true, - }, - }, - { - scope: ["keyword.type"], - style: { - foreground: values().syntaxType, - bold: true, - italic: true, - }, - }, - { - scope: ["keyword.function", "function.method"], - style: { - foreground: values().syntaxFunction, - }, - }, - { - scope: ["keyword"], - style: { - foreground: values().syntaxKeyword, - italic: true, - }, - }, - { - scope: ["keyword.import"], - style: { - foreground: values().syntaxKeyword, - }, - }, - { - scope: ["operator", "keyword.operator", "punctuation.delimiter"], - style: { - foreground: values().syntaxOperator, - }, - }, - { - scope: ["keyword.conditional.ternary"], - style: { - foreground: values().syntaxOperator, - }, - }, - { - scope: ["variable", "variable.parameter", "function.method.call", "function.call"], - style: { - foreground: values().syntaxVariable, - }, - }, - { - scope: ["variable.member", "function", "constructor"], - style: { - foreground: values().syntaxFunction, - }, - }, - { - scope: ["type", "module"], - style: { - foreground: values().syntaxType, - }, - }, - { - scope: ["constant"], - style: { - foreground: values().syntaxNumber, - }, - }, - { - scope: ["property"], - style: { - foreground: values().syntaxVariable, - }, - }, - { - scope: ["class"], - style: { - foreground: values().syntaxType, - }, - }, - { - scope: ["parameter"], - style: { - foreground: values().syntaxVariable, - }, - }, - { - scope: ["punctuation", "punctuation.bracket"], - style: { - foreground: values().syntaxPunctuation, - }, - }, - { - scope: [ - "variable.builtin", - "type.builtin", - "function.builtin", - "module.builtin", - "constant.builtin", - ], - style: { - foreground: values().error, - }, - }, - { - scope: ["variable.super"], - style: { - foreground: values().error, - }, - }, - { - scope: ["string.escape", "string.regexp"], - style: { - foreground: values().syntaxKeyword, - }, - }, - { - scope: ["keyword.directive"], - style: { - foreground: values().syntaxKeyword, - italic: true, - }, - }, - { - scope: ["punctuation.special"], - style: { - foreground: values().syntaxOperator, - }, - }, - { - scope: ["keyword.modifier"], - style: { - foreground: values().syntaxKeyword, - italic: true, - }, - }, - { - scope: ["keyword.exception"], - style: { - foreground: values().syntaxKeyword, - italic: true, - }, - }, - // Markdown specific styles - { - scope: ["markup.heading"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.1"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.2"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.3"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.4"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.5"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.heading.6"], - style: { - foreground: values().markdownHeading, - bold: true, - }, - }, - { - scope: ["markup.bold", "markup.strong"], - style: { - foreground: values().markdownStrong, - bold: true, - }, - }, - { - scope: ["markup.italic"], - style: { - foreground: values().markdownEmph, - italic: true, - }, - }, - { - scope: ["markup.list"], - style: { - foreground: values().markdownListItem, - }, - }, - { - scope: ["markup.quote"], - style: { - foreground: values().markdownBlockQuote, - italic: true, - }, - }, - { - scope: ["markup.raw", "markup.raw.block"], - style: { - foreground: values().markdownCode, - }, - }, - { - scope: ["markup.raw.inline"], - style: { - foreground: values().markdownCode, - background: values().background, - }, - }, - { - scope: ["markup.link"], - style: { - foreground: values().markdownLink, - underline: true, - }, - }, - { - scope: ["markup.link.label"], - style: { - foreground: values().markdownLinkText, - underline: true, - }, - }, - { - scope: ["markup.link.url"], - style: { - foreground: values().markdownLink, - underline: true, - }, - }, - { - scope: ["label"], - style: { - foreground: values().markdownLinkText, - }, - }, - { - scope: ["spell", "nospell"], - style: { - foreground: values().text, - }, - }, - { - scope: ["conceal"], - style: { - foreground: values().textMuted, - }, - }, - // Additional common highlight groups - { - scope: ["string.special", "string.special.url"], - style: { - foreground: values().markdownLink, - underline: true, - }, - }, - { - scope: ["character"], - style: { - foreground: values().syntaxString, - }, - }, - { - scope: ["float"], - style: { - foreground: values().syntaxNumber, - }, - }, - { - scope: ["comment.error"], - style: { - foreground: values().error, - italic: true, - bold: true, - }, - }, - { - scope: ["comment.warning"], - style: { - foreground: values().warning, - italic: true, - bold: true, - }, - }, - { - scope: ["comment.todo", "comment.note"], - style: { - foreground: values().info, - italic: true, - bold: true, - }, - }, - { - scope: ["namespace"], - style: { - foreground: values().syntaxType, - }, - }, - { - scope: ["field"], - style: { - foreground: values().syntaxVariable, - }, - }, - { - scope: ["type.definition"], - style: { - foreground: values().syntaxType, - bold: true, - }, - }, - { - scope: ["keyword.export"], - style: { - foreground: values().syntaxKeyword, - }, - }, - { - scope: ["attribute", "annotation"], - style: { - foreground: values().warning, - }, - }, - { - scope: ["tag"], - style: { - foreground: values().error, - }, - }, - { - scope: ["tag.attribute"], - style: { - foreground: values().syntaxKeyword, - }, - }, - { - scope: ["tag.delimiter"], - style: { - foreground: values().syntaxOperator, - }, - }, - { - scope: ["markup.strikethrough"], - style: { - foreground: values().textMuted, - }, - }, - { - scope: ["markup.underline"], - style: { - foreground: values().text, - underline: true, - }, - }, - { - scope: ["markup.list.checked"], - style: { - foreground: values().success, - }, - }, - { - scope: ["markup.list.unchecked"], - style: { - foreground: values().textMuted, - }, - }, - { - scope: ["diff.plus"], - style: { - foreground: values().diffAdded, - }, - }, - { - scope: ["diff.minus"], - style: { - foreground: values().diffRemoved, - }, - }, - { - scope: ["diff.delta"], - style: { - foreground: values().diffContext, - }, - }, - { - scope: ["error"], - style: { - foreground: values().error, - bold: true, - }, - }, - { - scope: ["warning"], - style: { - foreground: values().warning, - bold: true, - }, - }, - { - scope: ["info"], - style: { - foreground: values().info, - }, - }, - { - scope: ["debug"], - style: { - foreground: values().textMuted, - }, - }, - ]) - }) + const syntax = createMemo(() => generateSyntax(values())) return { theme: new Proxy(values(), { @@ -654,16 +170,20 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ }, }), get selected() { - return theme() + return store.active + }, + all() { + return store.themes }, syntax, - mode, + mode() { + return store.mode + }, setMode(mode: "dark" | "light") { - setMode(mode) + setStore("mode", mode) }, set(theme: string) { - if (!THEMES[theme]) return - setTheme(theme) + setStore("active", theme) kv.set("theme", theme) }, get ready() { @@ -672,3 +192,682 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ } }, }) + +function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson { + const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!) + const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!) + const palette = colors.palette.map((x) => RGBA.fromHex(x!)) + const isDark = mode == "dark" + + // Generate gray scale based on terminal background + const grays = generateGrayScale(bg, isDark) + const textMuted = generateMutedTextColor(bg, isDark) + + // ANSI color references + const ansiColors = { + black: palette[0], + red: palette[1], + green: palette[2], + yellow: palette[3], + blue: palette[4], + magenta: palette[5], + cyan: palette[6], + white: palette[7], + } + + return { + theme: { + // Primary colors using ANSI + primary: ansiColors.cyan, + secondary: ansiColors.magenta, + accent: ansiColors.cyan, + + // Status colors using ANSI + error: ansiColors.red, + warning: ansiColors.yellow, + success: ansiColors.green, + info: ansiColors.cyan, + + // Text colors + text: fg, + textMuted, + + // Background colors + background: bg, + backgroundPanel: grays[2], + backgroundElement: grays[3], + + // Border colors + borderSubtle: grays[6], + border: grays[7], + borderActive: grays[8], + + // Diff colors + diffAdded: ansiColors.green, + diffRemoved: ansiColors.red, + diffContext: grays[7], + diffHunkHeader: grays[7], + diffHighlightAdded: ansiColors.green, + diffHighlightRemoved: ansiColors.red, + diffAddedBg: grays[2], + diffRemovedBg: grays[2], + diffContextBg: grays[1], + diffLineNumber: grays[6], + diffAddedLineNumberBg: grays[3], + diffRemovedLineNumberBg: grays[3], + + // Markdown colors + markdownText: fg, + markdownHeading: fg, + markdownLink: ansiColors.blue, + markdownLinkText: ansiColors.cyan, + markdownCode: ansiColors.green, + markdownBlockQuote: ansiColors.yellow, + markdownEmph: ansiColors.yellow, + markdownStrong: fg, + markdownHorizontalRule: grays[7], + markdownListItem: ansiColors.blue, + markdownListEnumeration: ansiColors.cyan, + markdownImage: ansiColors.blue, + markdownImageText: ansiColors.cyan, + markdownCodeBlock: fg, + + // Syntax colors + syntaxComment: textMuted, + syntaxKeyword: ansiColors.magenta, + syntaxFunction: ansiColors.blue, + syntaxVariable: fg, + syntaxString: ansiColors.green, + syntaxNumber: ansiColors.yellow, + syntaxType: ansiColors.cyan, + syntaxOperator: ansiColors.cyan, + syntaxPunctuation: fg, + }, + } +} + +function generateGrayScale(bg: RGBA, isDark: boolean): Record { + const grays: Record = {} + + // RGBA stores floats in range 0-1, convert to 0-255 + const bgR = bg.r * 255 + const bgG = bg.g * 255 + const bgB = bg.b * 255 + + const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB + + for (let i = 1; i <= 12; i++) { + const factor = i / 12.0 + + let grayValue: number + let newR: number + let newG: number + let newB: number + + if (isDark) { + if (luminance < 10) { + grayValue = Math.floor(factor * 0.4 * 255) + newR = grayValue + newG = grayValue + newB = grayValue + } else { + const newLum = luminance + (255 - luminance) * factor * 0.4 + + const ratio = newLum / luminance + newR = Math.min(bgR * ratio, 255) + newG = Math.min(bgG * ratio, 255) + newB = Math.min(bgB * ratio, 255) + } + } else { + if (luminance > 245) { + grayValue = Math.floor(255 - factor * 0.4 * 255) + newR = grayValue + newG = grayValue + newB = grayValue + } else { + const newLum = luminance * (1 - factor * 0.4) + + const ratio = newLum / luminance + newR = Math.max(bgR * ratio, 0) + newG = Math.max(bgG * ratio, 0) + newB = Math.max(bgB * ratio, 0) + } + } + + grays[i] = RGBA.fromInts(Math.floor(newR), Math.floor(newG), Math.floor(newB)) + } + + return grays +} + +function generateMutedTextColor(bg: RGBA, isDark: boolean): RGBA { + // RGBA stores floats in range 0-1, convert to 0-255 + const bgR = bg.r * 255 + const bgG = bg.g * 255 + const bgB = bg.b * 255 + + const bgLum = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB + + let grayValue: number + + if (isDark) { + if (bgLum < 10) { + // Very dark/black background + grayValue = 180 // #b4b4b4 + } else { + // Scale up for lighter dark backgrounds + grayValue = Math.min(Math.floor(160 + bgLum * 0.3), 200) + } + } else { + if (bgLum > 245) { + // Very light/white background + grayValue = 75 // #4b4b4b + } else { + // Scale down for darker light backgrounds + grayValue = Math.max(Math.floor(100 - (255 - bgLum) * 0.2), 60) + } + } + + return RGBA.fromInts(grayValue, grayValue, grayValue) +} + +function generateSyntax(theme: Theme) { + return SyntaxStyle.fromTheme([ + { + scope: ["prompt"], + style: { + foreground: theme.accent, + }, + }, + { + scope: ["extmark.file"], + style: { + foreground: theme.warning, + bold: true, + }, + }, + { + scope: ["extmark.agent"], + style: { + foreground: theme.secondary, + bold: true, + }, + }, + { + scope: ["extmark.paste"], + style: { + foreground: theme.background, + background: theme.warning, + bold: true, + }, + }, + { + scope: ["comment"], + style: { + foreground: theme.syntaxComment, + italic: true, + }, + }, + { + scope: ["comment.documentation"], + style: { + foreground: theme.syntaxComment, + italic: true, + }, + }, + { + scope: ["string", "symbol"], + style: { + foreground: theme.syntaxString, + }, + }, + { + scope: ["number", "boolean"], + style: { + foreground: theme.syntaxNumber, + }, + }, + { + scope: ["character.special"], + style: { + foreground: theme.syntaxString, + }, + }, + { + scope: ["keyword.return", "keyword.conditional", "keyword.repeat", "keyword.coroutine"], + style: { + foreground: theme.syntaxKeyword, + italic: true, + }, + }, + { + scope: ["keyword.type"], + style: { + foreground: theme.syntaxType, + bold: true, + italic: true, + }, + }, + { + scope: ["keyword.function", "function.method"], + style: { + foreground: theme.syntaxFunction, + }, + }, + { + scope: ["keyword"], + style: { + foreground: theme.syntaxKeyword, + italic: true, + }, + }, + { + scope: ["keyword.import"], + style: { + foreground: theme.syntaxKeyword, + }, + }, + { + scope: ["operator", "keyword.operator", "punctuation.delimiter"], + style: { + foreground: theme.syntaxOperator, + }, + }, + { + scope: ["keyword.conditional.ternary"], + style: { + foreground: theme.syntaxOperator, + }, + }, + { + scope: ["variable", "variable.parameter", "function.method.call", "function.call"], + style: { + foreground: theme.syntaxVariable, + }, + }, + { + scope: ["variable.member", "function", "constructor"], + style: { + foreground: theme.syntaxFunction, + }, + }, + { + scope: ["type", "module"], + style: { + foreground: theme.syntaxType, + }, + }, + { + scope: ["constant"], + style: { + foreground: theme.syntaxNumber, + }, + }, + { + scope: ["property"], + style: { + foreground: theme.syntaxVariable, + }, + }, + { + scope: ["class"], + style: { + foreground: theme.syntaxType, + }, + }, + { + scope: ["parameter"], + style: { + foreground: theme.syntaxVariable, + }, + }, + { + scope: ["punctuation", "punctuation.bracket"], + style: { + foreground: theme.syntaxPunctuation, + }, + }, + { + scope: [ + "variable.builtin", + "type.builtin", + "function.builtin", + "module.builtin", + "constant.builtin", + ], + style: { + foreground: theme.error, + }, + }, + { + scope: ["variable.super"], + style: { + foreground: theme.error, + }, + }, + { + scope: ["string.escape", "string.regexp"], + style: { + foreground: theme.syntaxKeyword, + }, + }, + { + scope: ["keyword.directive"], + style: { + foreground: theme.syntaxKeyword, + italic: true, + }, + }, + { + scope: ["punctuation.special"], + style: { + foreground: theme.syntaxOperator, + }, + }, + { + scope: ["keyword.modifier"], + style: { + foreground: theme.syntaxKeyword, + italic: true, + }, + }, + { + scope: ["keyword.exception"], + style: { + foreground: theme.syntaxKeyword, + italic: true, + }, + }, + // Markdown specific styles + { + scope: ["markup.heading"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.1"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.2"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.3"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.4"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.5"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.heading.6"], + style: { + foreground: theme.markdownHeading, + bold: true, + }, + }, + { + scope: ["markup.bold", "markup.strong"], + style: { + foreground: theme.markdownStrong, + bold: true, + }, + }, + { + scope: ["markup.italic"], + style: { + foreground: theme.markdownEmph, + italic: true, + }, + }, + { + scope: ["markup.list"], + style: { + foreground: theme.markdownListItem, + }, + }, + { + scope: ["markup.quote"], + style: { + foreground: theme.markdownBlockQuote, + italic: true, + }, + }, + { + scope: ["markup.raw", "markup.raw.block"], + style: { + foreground: theme.markdownCode, + }, + }, + { + scope: ["markup.raw.inline"], + style: { + foreground: theme.markdownCode, + background: theme.background, + }, + }, + { + scope: ["markup.link"], + style: { + foreground: theme.markdownLink, + underline: true, + }, + }, + { + scope: ["markup.link.label"], + style: { + foreground: theme.markdownLinkText, + underline: true, + }, + }, + { + scope: ["markup.link.url"], + style: { + foreground: theme.markdownLink, + underline: true, + }, + }, + { + scope: ["label"], + style: { + foreground: theme.markdownLinkText, + }, + }, + { + scope: ["spell", "nospell"], + style: { + foreground: theme.text, + }, + }, + { + scope: ["conceal"], + style: { + foreground: theme.textMuted, + }, + }, + // Additional common highlight groups + { + scope: ["string.special", "string.special.url"], + style: { + foreground: theme.markdownLink, + underline: true, + }, + }, + { + scope: ["character"], + style: { + foreground: theme.syntaxString, + }, + }, + { + scope: ["float"], + style: { + foreground: theme.syntaxNumber, + }, + }, + { + scope: ["comment.error"], + style: { + foreground: theme.error, + italic: true, + bold: true, + }, + }, + { + scope: ["comment.warning"], + style: { + foreground: theme.warning, + italic: true, + bold: true, + }, + }, + { + scope: ["comment.todo", "comment.note"], + style: { + foreground: theme.info, + italic: true, + bold: true, + }, + }, + { + scope: ["namespace"], + style: { + foreground: theme.syntaxType, + }, + }, + { + scope: ["field"], + style: { + foreground: theme.syntaxVariable, + }, + }, + { + scope: ["type.definition"], + style: { + foreground: theme.syntaxType, + bold: true, + }, + }, + { + scope: ["keyword.export"], + style: { + foreground: theme.syntaxKeyword, + }, + }, + { + scope: ["attribute", "annotation"], + style: { + foreground: theme.warning, + }, + }, + { + scope: ["tag"], + style: { + foreground: theme.error, + }, + }, + { + scope: ["tag.attribute"], + style: { + foreground: theme.syntaxKeyword, + }, + }, + { + scope: ["tag.delimiter"], + style: { + foreground: theme.syntaxOperator, + }, + }, + { + scope: ["markup.strikethrough"], + style: { + foreground: theme.textMuted, + }, + }, + { + scope: ["markup.underline"], + style: { + foreground: theme.text, + underline: true, + }, + }, + { + scope: ["markup.list.checked"], + style: { + foreground: theme.success, + }, + }, + { + scope: ["markup.list.unchecked"], + style: { + foreground: theme.textMuted, + }, + }, + { + scope: ["diff.plus"], + style: { + foreground: theme.diffAdded, + }, + }, + { + scope: ["diff.minus"], + style: { + foreground: theme.diffRemoved, + }, + }, + { + scope: ["diff.delta"], + style: { + foreground: theme.diffContext, + }, + }, + { + scope: ["error"], + style: { + foreground: theme.error, + bold: true, + }, + }, + { + scope: ["warning"], + style: { + foreground: theme.warning, + bold: true, + }, + }, + { + scope: ["info"], + style: { + foreground: theme.info, + }, + }, + { + scope: ["debug"], + style: { + foreground: theme.textMuted, + }, + }, + ]) +} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index b0d98b088..7e0ccdaec 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -683,6 +683,7 @@ export function Session() { (scroll = r)} scrollbarOptions={{ + paddingLeft: 2, trackOptions: { backgroundColor: theme.backgroundElement, foregroundColor: theme.border, diff --git a/packages/opencode/src/cli/cmd/tui/util/terminal.ts b/packages/opencode/src/cli/cmd/tui/util/terminal.ts new file mode 100644 index 000000000..2b81068b3 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/util/terminal.ts @@ -0,0 +1,114 @@ +import { RGBA } from "@opentui/core" + +export namespace Terminal { + export type Colors = Awaited> + /** + * Query terminal colors including background, foreground, and palette (0-15). + * Uses OSC escape sequences to retrieve actual terminal color values. + * + * Note: OSC 4 (palette) queries may not work through tmux as responses are filtered. + * OSC 10/11 (foreground/background) typically work in most environments. + * + * Returns an object with background, foreground, and colors array. + * Any query that fails will be null/empty. + */ + export async function colors(): Promise<{ + background: RGBA | null + foreground: RGBA | null + colors: RGBA[] + }> { + if (!process.stdin.isTTY) return { background: null, foreground: null, colors: [] } + + return new Promise((resolve) => { + let background: RGBA | null = null + let foreground: RGBA | null = null + const paletteColors: RGBA[] = [] + let timeout: NodeJS.Timeout + + const cleanup = () => { + process.stdin.setRawMode(false) + process.stdin.removeListener("data", handler) + clearTimeout(timeout) + } + + const parseColor = (colorStr: string): RGBA | null => { + if (colorStr.startsWith("rgb:")) { + const parts = colorStr.substring(4).split("/") + return RGBA.fromInts( + parseInt(parts[0], 16) >> 8, // Convert 16-bit to 8-bit + parseInt(parts[1], 16) >> 8, + parseInt(parts[2], 16) >> 8, + 255, + ) + } + if (colorStr.startsWith("#")) { + return RGBA.fromHex(colorStr) + } + if (colorStr.startsWith("rgb(")) { + const parts = colorStr.substring(4, colorStr.length - 1).split(",") + return RGBA.fromInts(parseInt(parts[0]), parseInt(parts[1]), parseInt(parts[2]), 255) + } + return null + } + + const handler = (data: Buffer) => { + const str = data.toString() + + // Match OSC 11 (background color) + const bgMatch = str.match(/\x1b]11;([^\x07\x1b]+)/) + if (bgMatch) { + background = parseColor(bgMatch[1]) + } + + // Match OSC 10 (foreground color) + const fgMatch = str.match(/\x1b]10;([^\x07\x1b]+)/) + if (fgMatch) { + foreground = parseColor(fgMatch[1]) + } + + // Match OSC 4 (palette colors) + const paletteMatches = str.matchAll(/\x1b]4;(\d+);([^\x07\x1b]+)/g) + for (const match of paletteMatches) { + const index = parseInt(match[1]) + const color = parseColor(match[2]) + if (color) paletteColors[index] = color + } + + // Return immediately if we have all 16 palette colors + if (paletteColors.filter((c) => c !== undefined).length === 16) { + cleanup() + resolve({ background, foreground, colors: paletteColors }) + } + } + + process.stdin.setRawMode(true) + process.stdin.on("data", handler) + + // Query background (OSC 11) + process.stdout.write("\x1b]11;?\x07") + // Query foreground (OSC 10) + process.stdout.write("\x1b]10;?\x07") + // Query palette colors 0-15 (OSC 4) + for (let i = 0; i < 16; i++) { + process.stdout.write(`\x1b]4;${i};?\x07`) + } + + timeout = setTimeout(() => { + cleanup() + resolve({ background, foreground, colors: paletteColors }) + }, 1000) + }) + } + + export async function getTerminalBackgroundColor(): Promise<"dark" | "light"> { + const result = await colors() + if (!result.background) return "dark" + + const { r, g, b } = result.background + // Calculate luminance using relative luminance formula + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + + // Determine if dark or light based on luminance threshold + return luminance > 0.5 ? "light" : "dark" + } +} From 11a6f0886e0a436c02f4989da17cecaf93eec641 Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 6 Nov 2025 23:14:32 +0000 Subject: [PATCH 097/218] release: v1.0.37 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/bun.lock b/bun.lock index 226cf3624..338f7545f 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.36", + "version": "1.0.37", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.36", + "version": "1.0.37", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 4dccc20df..c73dd2f3a 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.36" + "version": "1.0.37" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ba4423e07..92a4e827d 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.36", + "version": "1.0.37", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index eda2b012f..dd18991d2 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.36", + "version": "1.0.37", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 38f43ee3a..b50c0c9e9 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.36", + "version": "1.0.37", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index bc69118ca..b5e0c4fdc 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.36", + "version": "1.0.37", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index e4a6f7336..ee9ba368e 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.36", + "version": "1.0.37", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b9e2c307b..bfd4b1625 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.36", + "version": "1.0.37", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f4f7fbfb0..4b256a605 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.36", + "version": "1.0.37", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c931dd44a..0c33d18aa 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.36", + "version": "1.0.37", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index 7b01f5ef3..df26d9643 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.36", + "version": "1.0.37", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 797258a63..7f2188859 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.36", + "version": "1.0.37", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index a5b5bae9f..27e62353e 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.36", + "version": "1.0.37", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index a2d98db39..fa0d40dbf 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.36", + "version": "1.0.37", "publisher": "sst-dev", "repository": { "type": "git", From 25f31f30969c13d3f4dcea671f6f0a6f827a34a2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 6 Nov 2025 19:14:01 -0500 Subject: [PATCH 098/218] codex tweaks --- packages/opencode/src/provider/transform.ts | 94 +++++---------------- 1 file changed, 20 insertions(+), 74 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 9a955f5c6..94d2861c8 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -3,77 +3,21 @@ import { unique } from "remeda" import type { JSONSchema } from "zod/v4/core" export namespace ProviderTransform { - function normalizeMessages( - msgs: ModelMessage[], - providerID: string, - modelID: string, - ): ModelMessage[] { - if (modelID.includes("claude")) { - return msgs.map((msg) => { - if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { - msg.content = msg.content.map((part) => { - if ( - (part.type === "tool-call" || part.type === "tool-result") && - "toolCallId" in part - ) { - return { - ...part, - toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), - } + function normalizeToolCallIds(msgs: ModelMessage[]): ModelMessage[] { + return msgs.map((msg) => { + if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { + msg.content = msg.content.map((part) => { + if ((part.type === "tool-call" || part.type === "tool-result") && "toolCallId" in part) { + return { + ...part, + toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), } - return part - }) - } - return msg - }) - } - if (providerID === "mistral" || modelID.toLowerCase().includes("mistral")) { - const result: ModelMessage[] = [] - for (let i = 0; i < msgs.length; i++) { - const msg = msgs[i] - const prevMsg = msgs[i - 1] - const nextMsg = msgs[i + 1] - - if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { - msg.content = msg.content.map((part) => { - if ( - (part.type === "tool-call" || part.type === "tool-result") && - "toolCallId" in part - ) { - // Mistral requires alphanumeric tool call IDs with exactly 9 characters - const normalizedId = part.toolCallId - .replace(/[^a-zA-Z0-9]/g, "") // Remove non-alphanumeric characters - .substring(0, 9) // Take first 9 characters - .padEnd(9, "0") // Pad with zeros if less than 9 characters - - return { - ...part, - toolCallId: normalizedId, - } - } - return part - }) - } - - result.push(msg) - - // Fix message sequence: tool messages cannot be followed by user messages - if (msg.role === "tool" && nextMsg?.role === "user") { - result.push({ - role: "assistant", - content: [ - { - type: "text", - text: "Done.", - }, - ], - }) - } + } + return part + }) } - return result - } - - return msgs + return msg + }) } function applyCaching(msgs: ModelMessage[], providerID: string): ModelMessage[] { @@ -120,7 +64,9 @@ export namespace ProviderTransform { } export function message(msgs: ModelMessage[], providerID: string, modelID: string) { - msgs = normalizeMessages(msgs, providerID, modelID) + if (modelID.includes("claude")) { + msgs = normalizeToolCallIds(msgs) + } if (providerID === "anthropic" || modelID.includes("anthropic") || modelID.includes("claude")) { msgs = applyCaching(msgs, providerID) } @@ -151,12 +97,12 @@ export namespace ProviderTransform { } if (modelID.includes("gpt-5") && !modelID.includes("gpt-5-chat")) { - if (!modelID.includes("codex") && !modelID.includes("gpt-5-pro")) { - result["reasoningEffort"] = "medium" + if (modelID.includes("codex")) { + result["store"] = false } - if (providerID !== "azure") { - result["textVerbosity"] = modelID.includes("codex") ? "medium" : "low" + if (!modelID.includes("codex") && !modelID.includes("gpt-5-pro")) { + result["reasoningEffort"] = "medium" } if (providerID === "opencode") { From d1962ca5a7e65c50235e9c389772331e7cd7a0fe Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 00:14:59 +0000 Subject: [PATCH 099/218] chore: format code --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 4b256a605..45c2b6b16 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 0c33d18aa..c2aa9a20b 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From b25d4f9dfb217442637d63167e31cf29617476bf Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 6 Nov 2025 19:48:10 -0500 Subject: [PATCH 100/218] fix issue with input randomly breaking --- packages/opencode/src/cli/cmd/tui/thread.ts | 3 --- packages/opencode/src/cli/cmd/tui/worker.ts | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index b05bb5829..c530c8884 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -2,7 +2,6 @@ import { cmd } from "@/cli/cmd/cmd" import { tui } from "./app" import { Rpc } from "@/util/rpc" import { type rpc } from "./worker" -import { upgrade } from "@/cli/upgrade" import { Session } from "@/session" import { bootstrap } from "@/cli/bootstrap" import path from "path" @@ -78,8 +77,6 @@ export const TuiThreadCommand = cmd({ } await bootstrap(cwd, async () => { - upgrade() - const sessionID = await (async () => { if (args.continue) { const it = Session.list() diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts index d268449c1..32cd5562a 100644 --- a/packages/opencode/src/cli/cmd/tui/worker.ts +++ b/packages/opencode/src/cli/cmd/tui/worker.ts @@ -3,6 +3,7 @@ import { Server } from "@/server/server" import { Log } from "@/util/log" import { Instance } from "@/project/instance" import { Rpc } from "@/util/rpc" +import { upgrade } from "@/cli/upgrade" await Log.init({ print: process.argv.includes("--print-logs"), @@ -25,6 +26,8 @@ process.on("uncaughtException", (e) => { }) }) +upgrade() + let server: Bun.Server export const rpc = { async server(input: { port: number; hostname: string }) { From 6bfccace0cdd61545aee652eba28b21a4ff715c3 Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 7 Nov 2025 01:04:05 +0000 Subject: [PATCH 101/218] release: v1.0.39 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/bun.lock b/bun.lock index 338f7545f..e7542fb57 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.37", + "version": "1.0.39", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.37", + "version": "1.0.39", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index c73dd2f3a..431dc4d68 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.37" + "version": "1.0.39" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 92a4e827d..5b0e05bd4 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.37", + "version": "1.0.39", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index dd18991d2..4912352c5 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.37", + "version": "1.0.39", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index b50c0c9e9..98a623979 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.37", + "version": "1.0.39", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index b5e0c4fdc..4f479e2e4 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.37", + "version": "1.0.39", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index ee9ba368e..b85a38d80 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.37", + "version": "1.0.39", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index bfd4b1625..d6ddb5356 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.37", + "version": "1.0.39", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 45c2b6b16..e368732fe 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.37", + "version": "1.0.39", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c2aa9a20b..96036d294 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.37", + "version": "1.0.39", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index df26d9643..4ffe5442a 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.37", + "version": "1.0.39", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 7f2188859..16725c1c1 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.37", + "version": "1.0.39", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 27e62353e..8a9d8ef7c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.37", + "version": "1.0.39", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index fa0d40dbf..8ae59a2a8 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.37", + "version": "1.0.39", "publisher": "sst-dev", "repository": { "type": "git", From 9e04ff013cc549f581ace9f9dbe0c5b318178a90 Mon Sep 17 00:00:00 2001 From: Nicolai van der Smagt Date: Thu, 6 Nov 2025 23:53:02 +0100 Subject: [PATCH 102/218] fix: resolve Mistral API compatibility issues (#2440) Co-authored-by: Aiden Cline --- packages/opencode/src/provider/transform.ts | 86 +++++++++++++++++---- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 94d2861c8..a81c4dd0b 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -3,21 +3,77 @@ import { unique } from "remeda" import type { JSONSchema } from "zod/v4/core" export namespace ProviderTransform { - function normalizeToolCallIds(msgs: ModelMessage[]): ModelMessage[] { - return msgs.map((msg) => { - if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { - msg.content = msg.content.map((part) => { - if ((part.type === "tool-call" || part.type === "tool-result") && "toolCallId" in part) { - return { - ...part, - toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), + function normalizeMessages( + msgs: ModelMessage[], + providerID: string, + modelID: string, + ): ModelMessage[] { + if (modelID.includes("claude")) { + return msgs.map((msg) => { + if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { + msg.content = msg.content.map((part) => { + if ( + (part.type === "tool-call" || part.type === "tool-result") && + "toolCallId" in part + ) { + return { + ...part, + toolCallId: part.toolCallId.replace(/[^a-zA-Z0-9_-]/g, "_"), + } } - } - return part - }) + return part + }) + } + return msg + }) + } + if (providerID === "mistral" || modelID.toLowerCase().includes("mistral")) { + const result: ModelMessage[] = [] + for (let i = 0; i < msgs.length; i++) { + const msg = msgs[i] + const prevMsg = msgs[i - 1] + const nextMsg = msgs[i + 1] + + if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) { + msg.content = msg.content.map((part) => { + if ( + (part.type === "tool-call" || part.type === "tool-result") && + "toolCallId" in part + ) { + // Mistral requires alphanumeric tool call IDs with exactly 9 characters + const normalizedId = part.toolCallId + .replace(/[^a-zA-Z0-9]/g, "") // Remove non-alphanumeric characters + .substring(0, 9) // Take first 9 characters + .padEnd(9, "0") // Pad with zeros if less than 9 characters + + return { + ...part, + toolCallId: normalizedId, + } + } + return part + }) + } + + result.push(msg) + + // Fix message sequence: tool messages cannot be followed by user messages + if (msg.role === "tool" && nextMsg?.role === "user") { + result.push({ + role: "assistant", + content: [ + { + type: "text", + text: "Done.", + }, + ], + }) + } } - return msg - }) + return result + } + + return msgs } function applyCaching(msgs: ModelMessage[], providerID: string): ModelMessage[] { @@ -64,9 +120,7 @@ export namespace ProviderTransform { } export function message(msgs: ModelMessage[], providerID: string, modelID: string) { - if (modelID.includes("claude")) { - msgs = normalizeToolCallIds(msgs) - } + msgs = normalizeMessages(msgs, providerID, modelID) if (providerID === "anthropic" || modelID.includes("anthropic") || modelID.includes("claude")) { msgs = applyCaching(msgs, providerID) } From da51c9dfac1f01907186b4e18c981ec45c870e23 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 01:06:38 +0000 Subject: [PATCH 103/218] chore: format code --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index e368732fe..a90fb4c84 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 96036d294..d0df17fc7 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 9f603e39a6ed863a4edaddeeb5444804fc0b8998 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 19:07:27 -0600 Subject: [PATCH 104/218] Fixed ACP to respect user's default model config. (#4006) Co-authored-by: opencode-agent[bot] Co-authored-by: rekram1-node Co-authored-by: GitHub Action Co-authored-by: Aiden Cline --- packages/opencode/src/acp/agent.ts | 38 +++++++++++++++++++++------- packages/opencode/src/cli/cmd/acp.ts | 3 ++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 4eaf76cfe..046c8262d 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -32,7 +32,7 @@ import { Command } from "@/command" import { Agent as Agents } from "@/agent/agent" import { Permission } from "@/permission" import { SessionCompaction } from "@/session/compaction" -import type { Config } from "@/config/config" +import { Config } from "@/config/config" import { MCP } from "@/mcp" import { Todo } from "@/session/todo" import { z } from "zod" @@ -41,6 +41,18 @@ import { LoadAPIKeyError } from "ai" export namespace ACP { const log = Log.create({ service: "acp-agent" }) + export async function init() { + const model = await defaultModel({}) + return { + create: (connection: AgentSideConnection, config: ACPConfig) => { + if (!config.defaultModel) { + config.defaultModel = model + } + return new Agent(connection, config) + }, + } + } + export class Agent implements ACPAgent { private sessionManager = new ACPSessionManager() private connection: AgentSideConnection @@ -48,13 +60,6 @@ export namespace ACP { constructor(connection: AgentSideConnection, config: ACPConfig = {}) { this.connection = connection - if (!config.defaultModel) { - // default to big pickle - config.defaultModel = { - providerID: "opencode", - modelID: "big-pickle", - } - } this.config = config this.setupEventSubscriptions() } @@ -685,7 +690,22 @@ export namespace ACP { async function defaultModel(config: ACPConfig) { const configured = config.defaultModel if (configured) return configured - return Provider.defaultModel() + + const model = await Config.get() + .then((cfg) => { + if (!cfg.model) return undefined + const parsed = Provider.parseModel(cfg.model) + return { + providerID: parsed.providerID, + modelID: parsed.modelID, + } + }) + .catch((error) => { + log.error("failed to load user config for default model", { error }) + return undefined + }) + + return model ?? { providerID: "opencode", modelID: "big-pickle" } } function parseUri( diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts index de461e170..77ef0c60c 100644 --- a/packages/opencode/src/cli/cmd/acp.ts +++ b/packages/opencode/src/cli/cmd/acp.ts @@ -50,9 +50,10 @@ export const AcpCommand = cmd({ }) const stream = ndJsonStream(input, output) + const agent = await ACP.init() new AgentSideConnection((conn) => { - return new ACP.Agent(conn) + return agent.create(conn, {}) }, stream) log.info("setup connection") From d0f5c825bd2bb83378545c996dc566de25df6f1c Mon Sep 17 00:00:00 2001 From: Charles David Mupende Date: Fri, 7 Nov 2025 02:37:23 +0100 Subject: [PATCH 105/218] feat: implement network IP retrieval for remote access in web command (#3945) --- packages/opencode/src/cli/cmd/web.ts | 59 +++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/web.ts b/packages/opencode/src/cli/cmd/web.ts index 8fc8a9915..9d3a42539 100644 --- a/packages/opencode/src/cli/cmd/web.ts +++ b/packages/opencode/src/cli/cmd/web.ts @@ -2,6 +2,29 @@ import { Server } from "../../server/server" import { UI } from "../ui" import { cmd } from "./cmd" import open from "open" +import { networkInterfaces } from "os" + +function getNetworkIPs() { + const nets = networkInterfaces() + const results: string[] = [] + + for (const name of Object.keys(nets)) { + const net = nets[name] + if (!net) continue + + for (const netInfo of net) { + // Skip internal and non-IPv4 addresses + if (netInfo.internal || netInfo.family !== "IPv4") continue + + // Skip Docker bridge networks (typically 172.x.x.x) + if (netInfo.address.startsWith("172.")) continue + + results.push(netInfo.address) + } + } + + return results +} export const WebCommand = cmd({ command: "web", @@ -29,12 +52,36 @@ export const WebCommand = cmd({ UI.empty() UI.println(UI.logo(" ")) UI.empty() - UI.println( - UI.Style.TEXT_INFO_BOLD + " Web interface: ", - UI.Style.TEXT_NORMAL, - server.url.toString(), - ) - open(server.url.toString()).catch(() => {}) + + if (hostname === "0.0.0.0") { + // Show localhost for local access + const localhostUrl = `http://localhost:${server.port}` + UI.println( + UI.Style.TEXT_INFO_BOLD + " Local access: ", + UI.Style.TEXT_NORMAL, + localhostUrl, + ) + + // Show network IPs for remote access + const networkIPs = getNetworkIPs() + if (networkIPs.length > 0) { + for (const ip of networkIPs) { + UI.println( + UI.Style.TEXT_INFO_BOLD + " Network access: ", + UI.Style.TEXT_NORMAL, + `http://${ip}:${server.port}`, + ) + } + } + + // Open localhost in browser + open(localhostUrl.toString()).catch(() => {}) + } else { + const displayUrl = server.url.toString() + UI.println(UI.Style.TEXT_INFO_BOLD + " Web interface: ", UI.Style.TEXT_NORMAL, displayUrl) + open(displayUrl).catch(() => {}) + } + await new Promise(() => {}) await server.stop() }, From 9554abb56ef2998d13a31adc6ec85cd92cc71bf6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 01:11:47 -0500 Subject: [PATCH 106/218] message storage performance improvements --- .../cli/cmd/tui/component/prompt/index.tsx | 1 - .../opencode/src/cli/cmd/tui/context/sync.tsx | 1 + packages/opencode/src/server/server.ts | 2 +- packages/opencode/src/session/compaction.ts | 4 +--- packages/opencode/src/session/index.ts | 24 ++++++++++++------- packages/opencode/src/session/message-v2.ts | 12 ++++++---- packages/opencode/src/session/prompt.ts | 4 +--- packages/opencode/src/session/summary.ts | 12 +--------- 8 files changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index c977f7318..b9e406598 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -3,7 +3,6 @@ import { BoxRenderable, TextareaRenderable, MouseEvent, - KeyEvent, PasteEvent, t, dim, diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 3fe1a4f5a..5c8d31468 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -270,6 +270,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string) { const now = Date.now() + if (store.message[sessionID]) return console.log("syncing", sessionID) const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index c72650060..bb9f065e0 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -756,7 +756,7 @@ export namespace Server { validator( "query", z.object({ - limit: z.coerce.number().optional(), + limit: z.coerce.number(), }), ), async (c) => { diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 115628ccd..4924a35dd 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -111,9 +111,7 @@ export namespace SessionCompaction { draft.time.compacting = undefined }) }) - const toSummarize = await Session.messages({ sessionID: input.sessionID }).then( - MessageV2.filterCompacted, - ) + const toSummarize = await MessageV2.filterCompacted(Session.messageStream(input.sessionID)) const model = await Provider.getModel(input.providerID, input.modelID) const system = [ ...SystemPrompt.summarize(model.providerID), diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index dc04f1830..0971531fe 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -273,6 +273,17 @@ export namespace Session { return diffs ?? [] }) + export const messageStream = fn(Identifier.schema("session"), async function* (sessionID) { + const list = await Array.fromAsync(await Storage.list(["message", sessionID])) + for (let i = list.length - 1; i >= 0; i--) { + const read = await Storage.read(list[i]) + yield { + info: read, + parts: await getParts(read.id), + } + } + }) + export const messages = fn( z.object({ sessionID: Identifier.schema("session"), @@ -280,16 +291,11 @@ export namespace Session { }), async (input) => { const result = [] as MessageV2.WithParts[] - const list = (await Array.fromAsync(await Storage.list(["message", input.sessionID]))) - .toSorted((a, b) => a.at(-1)!.localeCompare(b.at(-1)!)) - .slice(-1 * (input.limit ?? 1_000_000)) - for (const p of list) { - const read = await Storage.read(p) - result.push({ - info: read, - parts: await getParts(read.id), - }) + for await (const msg of messageStream(input.sessionID)) { + if (input.limit && result.length >= input.limit) break + result.push(msg) } + result.reverse() return result }, ) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index f35735b71..66d293192 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -655,10 +655,14 @@ export namespace MessageV2 { return convertToModelMessages(result) } - export function filterCompacted(msgs: { info: MessageV2.Info; parts: MessageV2.Part[] }[]) { - const i = msgs.findLastIndex((m) => m.info.role === "assistant" && !!m.info.summary) - if (i === -1) return msgs.slice() - return msgs.slice(i) + export async function filterCompacted(stream: AsyncIterable) { + const result = [] as MessageV2.WithParts[] + for await (const msg of stream) { + result.push(msg) + if (msg.info.role === "assistant" && msg.info.summary === true) break + } + result.reverse() + return result } export function fromError(e: unknown, ctx: { providerID: string }) { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 06b065133..cf11b1290 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -434,9 +434,7 @@ export namespace SessionPrompt { providerID: string signal: AbortSignal }) { - let msgs = await Session.messages({ sessionID: input.sessionID }).then( - MessageV2.filterCompacted, - ) + let msgs = await MessageV2.filterCompacted(Session.messageStream(input.sessionID)) const lastAssistant = msgs.findLast((msg) => msg.info.role === "assistant") if ( lastAssistant?.info.role === "assistant" && diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 8783a36ee..de3e22bd8 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -151,17 +151,7 @@ export namespace SessionSummary { messageID: Identifier.schema("message").optional(), }), async (input) => { - let all = await Session.messages({ sessionID: input.sessionID }) - if (input.messageID) - all = all.filter( - (x) => - x.info.id === input.messageID || - (x.info.role === "assistant" && x.info.parentID === input.messageID), - ) - - return computeDiff({ - messages: all, - }) + return Storage.read(["session_diff", input.sessionID]) ?? [] }, ) From ce7b73170f0de6dcf6ea38ca2264caedbde78850 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 12:04:25 +0000 Subject: [PATCH 107/218] ignore: update download stats 2025-11-07 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 56d6d39fd..424bf568e 100644 --- a/STATS.md +++ b/STATS.md @@ -132,3 +132,4 @@ | 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) | | 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) | | 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) | +| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) | From e5804f64f96a3b2fbcbb4dd9ad10c6ca40a6e083 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 06:26:26 -0600 Subject: [PATCH 108/218] fix(desktop): layout quirks --- packages/desktop/src/pages/session.tsx | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 22f9c47f5..30b8eab91 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -358,12 +358,12 @@ export default function Page() { return (
    -
    +
    @@ -382,7 +382,7 @@ export default function Page() { role="list" classList={{ "mr-8 shrink-0 flex flex-col items-start": true, - "absolute right-full w-60 @7xl:gap-2": local.layout.review.state() !== "open", + "absolute right-full w-60 @7xl:gap-2": true, // local.layout.review.state() !== "open", "": local.layout.review.state() === "open", }} > @@ -498,7 +498,7 @@ export default function Page() {
    {/* Title */}
    @@ -645,6 +645,14 @@ export default function Page() { }}
    + +
    + { + inputRef = el + }} + /> +
    @@ -826,13 +834,6 @@ export default function Page() { -
    - { - inputRef = el - }} - /> -
    From 14397651b5e2a673aa069fb82eedb690c17eba4c Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:05:09 -0600 Subject: [PATCH 109/218] ignore: test file --- theme-test.tsx | 285 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 theme-test.tsx diff --git a/theme-test.tsx b/theme-test.tsx new file mode 100644 index 000000000..16559bf70 --- /dev/null +++ b/theme-test.tsx @@ -0,0 +1,285 @@ +import React, { useState, useEffect, useCallback, useMemo } from "react" +import { ComponentType, FC, ReactNode, JSX } from "react" + +// Interface definitions +interface User { + id: number + name: string + email?: string + readonly createdAt: Date + active: boolean +} + +type Theme = "light" | "dark" | "auto" +type Status = "pending" | "loading" | "success" | "error" + +// Generic function with constraints +function createRepository(items: T[]): Repository { + return new Repository(items) +} + +// Class definition +class Repository { + private items: T[] + protected cache: Map + + constructor(items: T[] = []) { + this.items = items + this.cache = new Map() + } + + public find(id: number): T | undefined { + return this.items.find((item) => item.id === id) + } + + public async findAll(): Promise { + return this.items + } + + get count(): number { + return this.items.length + } +} + +// Enum definition +enum LogLevel { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, +} + +// Constants +const API_URL = "https://api.example.com" +const MAX_RETRIES = 3 +const DEFAULT_TIMEOUT = 5000 + +// Regular expressions +const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ +const PHONE_REGEX = /^\+?1?-?\.?\s?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})$/ + +// Template literals +const greeting = `Hello, ${user.name}!` +const sql = ` + SELECT * FROM users + WHERE active = true + AND created_at > '${new Date().toISOString()}' +` + +// Arrow functions +const debounce = any>( + func: T, + wait: number, +): ((...args: Parameters) => void) => { + let timeout: NodeJS.Timeout + return (...args: Parameters) => { + clearTimeout(timeout) + timeout = setTimeout(() => func(...args), wait) + } +} + +// Async function +async function fetchUserData(userId: number): Promise { + try { + const response = await fetch(`${API_URL}/users/${userId}`) + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + return await response.json() + } catch (error) { + console.error("Failed to fetch user data:", error) + throw error + } +} + +// React component with various patterns +const ThemeProvider: FC<{ children: ReactNode; theme?: Theme }> = ({ + children, + theme = "auto", +}) => { + const [currentTheme, setCurrentTheme] = useState(theme) + const [status, setStatus] = useState("pending") + + useEffect(() => { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") + const handleChange = (e: MediaQueryListEvent) => { + setCurrentTheme(e.matches ? "dark" : "light") + } + + mediaQuery.addEventListener("change", handleChange) + return () => mediaQuery.removeEventListener("change", handleChange) + }, []) + + const contextValue = useMemo( + () => ({ + theme: currentTheme, + setTheme: setCurrentTheme, + toggleTheme: () => setCurrentTheme((prev) => (prev === "light" ? "dark" : "light")), + }), + [currentTheme], + ) + + return ( + +
    + {children} +
    +
    + ) +} + +// Higher-order component +function withLogging

    (Component: ComponentType

    ): ComponentType

    { + return function LoggedComponent(props: P) { + console.log("Rendering component:", Component.name) + return + } +} + +// Custom hook +function useLocalStorage(key: string, initialValue: T): [T, (value: T) => void] { + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key) + return item ? JSON.parse(item) : initialValue + } catch (error) { + console.warn(`Error reading localStorage key "${key}":`, error) + return initialValue + } + }) + + const setValue = useCallback( + (value: T) => { + try { + setStoredValue(value) + window.localStorage.setItem(key, JSON.stringify(value)) + } catch (error) { + console.warn(`Error setting localStorage key "${key}":`, error) + } + }, + [key], + ) + + return [storedValue, setValue] +} + +// JSX component with various elements +const UserProfile: FC<{ user: User; onUpdate?: (user: User) => void }> = ({ user, onUpdate }) => { + const [isEditing, setIsEditing] = useState(false) + const [formData, setFormData] = useState(user) + const [errors, setErrors] = useState>({}) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + + // Validation + if (!formData.name.trim()) { + setErrors({ name: "Name is required" }) + return + } + + if (formData.email && !EMAIL_REGEX.test(formData.email)) { + setErrors({ email: "Invalid email format" }) + return + } + + try { + setStatus("loading") + await onUpdate?.(formData) + setStatus("success") + setIsEditing(false) + } catch (error) { + setStatus("error") + setErrors({ submit: "Failed to update profile" }) + } + } + + return ( +

    +
    +

    {user.name}

    + + {user.active ? "Active" : "Inactive"} + +
    + + {isEditing ? ( + +
    + + setFormData({ ...formData, name: e.target.value })} + className={errors.name ? "error" : ""} + placeholder="Enter your name" + required + /> + {errors.name && {errors.name}} +
    + +
    + + setFormData({ ...formData, email: e.target.value })} + className={errors.email ? "error" : ""} + placeholder="user@example.com" + /> + {errors.email && {errors.email}} +
    + +
    + + +
    + + {errors.submit &&
    {errors.submit}
    } + + ) : ( +
    +

    + ID: {user.id} +

    +

    + Email: {user.email || "Not provided"} +

    +

    + Member since: {user.createdAt.toLocaleDateString()} +

    + + +
    + )} +
    + ) +} + +// Export statements +export { + User, + Theme, + Status, + LogLevel, + Repository, + ThemeProvider, + UserProfile, + fetchUserData, + useLocalStorage, + withLogging, + debounce, +} + +export default ThemeProvider + +// Type exports +export type { User as UserType, ComponentType as ReactComponentType } From afb831c93cdc20f25280630e3bd7e2387fe6e52d Mon Sep 17 00:00:00 2001 From: Jinhyeok Lee Date: Sat, 8 Nov 2025 01:31:16 +0900 Subject: [PATCH 110/218] vscode: Add VS Code Insiders support (#4019) --- packages/opencode/src/ide/index.ts | 6 +- packages/opencode/test/ide/ide.test.ts | 86 ++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 packages/opencode/test/ide/ide.test.ts diff --git a/packages/opencode/src/ide/index.ts b/packages/opencode/src/ide/index.ts index ac80dac3e..a6a997e11 100644 --- a/packages/opencode/src/ide/index.ts +++ b/packages/opencode/src/ide/index.ts @@ -6,6 +6,7 @@ import { Bus } from "../bus" const SUPPORTED_IDES = [ { name: "Windsurf" as const, cmd: "windsurf" }, + { name: "Visual Studio Code - Insiders" as const, cmd: "code-insiders" }, { name: "Visual Studio Code" as const, cmd: "code" }, { name: "Cursor" as const, cmd: "cursor" }, { name: "VSCodium" as const, cmd: "codium" }, @@ -43,7 +44,10 @@ export namespace Ide { } export function alreadyInstalled() { - return process.env["OPENCODE_CALLER"] === "vscode" + return ( + process.env["OPENCODE_CALLER"] === "vscode" || + process.env["OPENCODE_CALLER"] === "vscode-insiders" + ) } export async function install(ide: (typeof SUPPORTED_IDES)[number]["name"]) { diff --git a/packages/opencode/test/ide/ide.test.ts b/packages/opencode/test/ide/ide.test.ts new file mode 100644 index 000000000..9678aa90e --- /dev/null +++ b/packages/opencode/test/ide/ide.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, test, afterEach } from "bun:test" +import { Ide } from "../../src/ide" + +describe("ide", () => { + const original = structuredClone(process.env) + + afterEach(() => { + Object.keys(process.env).forEach((key) => { + delete process.env[key] + }) + Object.assign(process.env, original) + }) + + test("should detect Visual Studio Code", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = + "/path/to/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("Visual Studio Code") + }) + + test("should detect Visual Studio Code Insiders", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = + "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("Visual Studio Code - Insiders") + }) + + test("should detect Cursor", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = + "/path/to/Cursor.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("Cursor") + }) + + test("should detect VSCodium", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = + "/path/to/VSCodium.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("VSCodium") + }) + + test("should detect Windsurf", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = + "/path/to/Windsurf.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("Windsurf") + }) + + test("should return unknown when TERM_PROGRAM is not vscode", () => { + process.env["TERM_PROGRAM"] = "iTerm2" + process.env["GIT_ASKPASS"] = + "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions/git/dist/askpass.sh" + + expect(Ide.ide()).toBe("unknown") + }) + + test("should return unknown when GIT_ASKPASS does not contain IDE name", () => { + process.env["TERM_PROGRAM"] = "vscode" + process.env["GIT_ASKPASS"] = "/path/to/unknown/askpass.sh" + + expect(Ide.ide()).toBe("unknown") + }) + + test("should recognize vscode-insiders OPENCODE_CALLER", () => { + process.env["OPENCODE_CALLER"] = "vscode-insiders" + + expect(Ide.alreadyInstalled()).toBe(true) + }) + + test("should recognize vscode OPENCODE_CALLER", () => { + process.env["OPENCODE_CALLER"] = "vscode" + + expect(Ide.alreadyInstalled()).toBe(true) + }) + + test("should return false for unknown OPENCODE_CALLER", () => { + process.env["OPENCODE_CALLER"] = "unknown" + + expect(Ide.alreadyInstalled()).toBe(false) + }) +}) From b708d0ecec8cbfc3d60e4d209fcd218fd628ae7f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 12:31:28 -0500 Subject: [PATCH 111/218] disable scrollbar temporarily because of text wrap issues --- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 7e0ccdaec..4365b5ed7 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -684,6 +684,7 @@ export function Session() { ref={(r) => (scroll = r)} scrollbarOptions={{ paddingLeft: 2, + visible: false, trackOptions: { backgroundColor: theme.backgroundElement, foregroundColor: theme.border, From d55f4f3322c857567d5327bfe928159a1b6a205f Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 7 Nov 2025 17:37:31 +0000 Subject: [PATCH 112/218] release: v1.0.40 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/sdk/js/src/gen/types.gen.ts | 4 ++-- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 15 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bun.lock b/bun.lock index e7542fb57..0dd38f165 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.39", + "version": "1.0.40", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.39", + "version": "1.0.40", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 431dc4d68..9fa571eb7 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.39" + "version": "1.0.40" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 5b0e05bd4..e30a14975 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.39", + "version": "1.0.40", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 4912352c5..bd6fbd184 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.39", + "version": "1.0.40", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 98a623979..88b1c7bde 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.39", + "version": "1.0.40", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 4f479e2e4..a97f53050 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.39", + "version": "1.0.40", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index b85a38d80..e1fa87768 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.39", + "version": "1.0.40", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d6ddb5356..c4134b7db 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.39", + "version": "1.0.40", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index a90fb4c84..097b627a1 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.39", + "version": "1.0.40", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index d0df17fc7..8583c0588 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.39", + "version": "1.0.40", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 5f565df58..939589594 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1979,9 +1979,9 @@ export type SessionMessagesData = { */ id: string } - query?: { + query: { directory?: string - limit?: number + limit: number } url: "/session/{id}/message" } diff --git a/packages/slack/package.json b/packages/slack/package.json index 4ffe5442a..0894ccbd4 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.39", + "version": "1.0.40", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 16725c1c1..7ce20e3c5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.39", + "version": "1.0.40", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 8a9d8ef7c..b40e421a6 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.39", + "version": "1.0.40", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 8ae59a2a8..2bf8f1c12 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.39", + "version": "1.0.40", "publisher": "sst-dev", "repository": { "type": "git", From 287855336d23cab1e05e3088b42bfb70d883255b Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 13:03:54 -0500 Subject: [PATCH 113/218] allow not specifying a limit on messages endpoint --- packages/opencode/src/server/server.ts | 2 +- packages/sdk/js/src/gen/types.gen.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index bb9f065e0..c72650060 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -756,7 +756,7 @@ export namespace Server { validator( "query", z.object({ - limit: z.coerce.number(), + limit: z.coerce.number().optional(), }), ), async (c) => { diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 939589594..5f565df58 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1979,9 +1979,9 @@ export type SessionMessagesData = { */ id: string } - query: { + query?: { directory?: string - limit: number + limit?: number } url: "/session/{id}/message" } From e317e7e481e60afbb8d9a1bd980650804aaf5d4d Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 7 Nov 2025 18:11:39 +0000 Subject: [PATCH 114/218] release: v1.0.41 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index 0dd38f165..c55d2821a 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.40", + "version": "1.0.41", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.40", + "version": "1.0.41", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 9fa571eb7..e830cdbcc 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.40" + "version": "1.0.41" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index e30a14975..25dc25f96 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.40", + "version": "1.0.41", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index bd6fbd184..cf3ea4260 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.40", + "version": "1.0.41", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 88b1c7bde..19205a33f 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.40", + "version": "1.0.41", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a97f53050..cebbe8934 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.40", + "version": "1.0.41", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index e1fa87768..57f0a8768 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.40", + "version": "1.0.41", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index c4134b7db..e5b2a1387 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.40", + "version": "1.0.41", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 097b627a1..97e3e77b1 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.40", + "version": "1.0.41", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 8583c0588..97725754b 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.40", + "version": "1.0.41", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/slack/package.json b/packages/slack/package.json index 0894ccbd4..633050cd0 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.40", + "version": "1.0.41", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 7ce20e3c5..1bf2253c6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.40", + "version": "1.0.41", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index b40e421a6..0381033a3 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.40", + "version": "1.0.41", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 2bf8f1c12..baf51e575 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.40", + "version": "1.0.41", "publisher": "sst-dev", "repository": { "type": "git", From c6eea0343d2667a9f6850163665e2a0d90000925 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 18:05:59 +0000 Subject: [PATCH 115/218] chore: format code --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 97e3e77b1..f9d15e491 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 97725754b..d96c1a4ca 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 4463d319c97ab167022b056aa199396bd97762e4 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 13:22:03 -0500 Subject: [PATCH 116/218] fix scroll when no session exists --- .../cmd/tui/component/dialog-session-list.tsx | 6 +++++- .../src/cli/cmd/tui/routes/session/index.tsx | 18 +++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index dc770ce28..e6f7efbe3 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -2,7 +2,7 @@ import { useDialog } from "@tui/ui/dialog" import { DialogSelect } from "@tui/ui/dialog-select" import { useRoute } from "@tui/context/route" import { useSync } from "@tui/context/sync" -import { createMemo, createSignal, onMount } from "solid-js" +import { createEffect, createMemo, createSignal, onMount } from "solid-js" import { Locale } from "@/util/locale" import { Keybind } from "@/util/keybind" import { useTheme } from "../context/theme" @@ -45,6 +45,10 @@ export function DialogSessionList() { }) }) + createEffect(() => { + console.log("session count", sync.data.session.length) + }) + onMount(() => { dialog.setSize("large") }) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 4365b5ed7..830d475f8 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -107,14 +107,18 @@ export function Session() { const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4) createEffect(async () => { - await sync.session.sync(route.sessionID).catch(() => { - toast.show({ - message: `Session not found: ${route.sessionID}`, - variant: "error", + await sync.session + .sync(route.sessionID) + .then(() => { + scroll.scrollBy(100_000) + }) + .catch(() => { + toast.show({ + message: `Session not found: ${route.sessionID}`, + variant: "error", + }) + return navigate({ type: "home" }) }) - return navigate({ type: "home" }) - }) - scroll.scrollBy(100_000) }) const toast = useToast() From 3a1d1a6284b66aaf385c3c97bb842a8e1d8985cb Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:48:12 -0600 Subject: [PATCH 117/218] feat(desktop): custom syntax colors --- packages/desktop/src/context/sync.tsx | 8 +- packages/desktop/src/pages/session.tsx | 3 +- packages/ui/src/components/code.tsx | 3 +- packages/ui/src/components/diff.tsx | 3937 ++------------------ packages/ui/src/styles/tailwind/colors.css | 5 + packages/ui/src/styles/theme.css | 59 +- theme-test.java | 461 +++ theme-test.md | 669 ++++ theme-test.tsx | 68 + 9 files changed, 1622 insertions(+), 3591 deletions(-) create mode 100644 theme-test.java create mode 100644 theme-test.md diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index c5b169a38..bc9491fd3 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -188,7 +188,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ async sync(sessionID: string, _isRetry = false) { const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), - sdk.client.session.messages({ path: { id: sessionID } }), + sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }), sdk.client.session.todo({ path: { id: sessionID } }), sdk.client.session.diff({ path: { id: sessionID } }), ]) @@ -211,12 +211,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ draft.session_diff[sessionID] = diff.data ?? [] }), ) - - // If no messages and this might be a new session, retry after a delay - // if (!isRetry && messages.data!.length === 0) { - // setTimeout(() => this.sync(sessionID, true), 500) - // return - // } }, fetch: async (count = 10) => { setStore("limit", (x) => x + count) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 30b8eab91..3dcc24e61 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -736,7 +736,7 @@ export default function Page() { "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true, }} > -
    +
    All changes
    @@ -766,6 +766,7 @@ export default function Page() { (props: CodeProps) { createEffect(() => { const instance = new File({ - theme: { dark: "oc-1-dark", light: "oc-1-light" }, // or any Shiki theme + theme: { dark: "oc-1-dark", light: "oc-1-light" }, + // theme: { dark: "pierre-dark", light: "pierre-light" }, overflow: "wrap", // or 'scroll' themeType: "system", // 'system', 'light', or 'dark' disableFileHeader: true, diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index 6297a6422..09085b44c 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -57,6 +57,7 @@ export function Diff(props: DiffProps) { const instance = new FileDiff({ // theme: "pierre-light", theme: { dark: "oc-1-dark", light: "oc-1-light" }, + // theme: { dark: "pierre-dark", light: "pierre-light" }, // When using the 'themes' prop, 'themeType' allows you to force 'dark' // or 'light' theme, or inherit from the OS ('system') theme. themeType: "system", @@ -180,1788 +181,385 @@ export function Diff(props: DiffProps) { ) } +const colors = { + "editor.background": "transparent", + "editor.foreground": "var(--text-base)", + "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", + "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", + // "gitDecoration.conflictingResourceForeground": "#ffca00", + // "gitDecoration.modifiedResourceForeground": "#1a76d4", + // "gitDecoration.untrackedResourceForeground": "#00cab1", + // "gitDecoration.ignoredResourceForeground": "#84848A", + // "terminal.titleForeground": "#adadb1", + // "terminal.titleInactiveForeground": "#84848A", + // "terminal.background": "#141415", + // "terminal.foreground": "#adadb1", + // "terminal.ansiBlack": "#141415", + // "terminal.ansiRed": "#ff2e3f", + // "terminal.ansiGreen": "#0dbe4e", + // "terminal.ansiYellow": "#ffca00", + // "terminal.ansiBlue": "#008cff", + // "terminal.ansiMagenta": "#c635e4", + // "terminal.ansiCyan": "#08c0ef", + // "terminal.ansiWhite": "#c6c6c8", + // "terminal.ansiBrightBlack": "#141415", + // "terminal.ansiBrightRed": "#ff2e3f", + // "terminal.ansiBrightGreen": "#0dbe4e", + // "terminal.ansiBrightYellow": "#ffca00", + // "terminal.ansiBrightBlue": "#008cff", + // "terminal.ansiBrightMagenta": "#c635e4", + // "terminal.ansiBrightCyan": "#08c0ef", + // "terminal.ansiBrightWhite": "#c6c6c8", +} + +const tokenColors = [ + { + scope: ["comment", "punctuation.definition.comment", "string.comment"], + settings: { + foreground: "var(--syntax-comment)", + }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { + foreground: "var(--syntax-property)", // maybe attribute + }, + }, + { + scope: [ + "constant", + "entity.name.constant", + "variable.other.constant", + "variable.language", + "entity", + ], + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: ["entity.name", "meta.export.default", "meta.definition.variable"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: [ + "variable.parameter.function", + "meta.jsx.children", + "meta.block", + "meta.tag.attributes", + "entity.name.constant", + "meta.object.member", + "meta.embedded.expression", + "meta.template.expression", + "string.other.begin.yaml", + "string.other.end.yaml", + ], + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["entity.name.function", "support.type.primitive"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.class.component"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: "keyword", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: [ + "keyword.operator", + "storage.type.function.arrow", + "punctuation.separator.key-value.css", + "entity.name.tag.yaml", + "punctuation.separator.key-value.mapping.yaml", + ], + settings: { + foreground: "var(--syntax-operator)", + }, + }, + { + scope: ["storage", "storage.type"], + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "string", + "punctuation.definition.string", + "string punctuation.section.embedded source", + "entity.name.tag", + ], + settings: { + foreground: "var(--syntax-string)", + }, + }, + { + scope: "support", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "support.type.object.module", + "variable.other.object", + "support.type.property-name.css", + ], + settings: { + foreground: "var(--syntax-object)", + }, + }, + { + scope: "meta.property-name", + settings: { + foreground: "var(--syntax-property)", + }, + }, + { + scope: "variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "variable.other", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: [ + "invalid.broken", + "invalid.illegal", + "invalid.unimplemented", + "invalid.deprecated", + "message.error", + "markup.deleted", + "meta.diff.header.from-file", + "punctuation.definition.deleted", + "brackethighlighter.unmatched", + "token.error-token", + ], + settings: { + foreground: "var(--syntax-critical)", + }, + }, + { + scope: "carriage-return", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: "string source", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "string variable", + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: [ + "source.regexp", + "string.regexp", + "string.regexp.character-class", + "string.regexp constant.character.escape", + "string.regexp source.ruby.embedded", + "string.regexp string.regexp.arbitrary-repitition", + "string.regexp constant.character.escape", + ], + settings: { + foreground: "var(--syntax-regexp)", + }, + }, + { + scope: "support.constant", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: "support.variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "meta.module-reference", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "punctuation.definition.list.begin.markdown", + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["markup.heading", "markup.heading entity.name"], + settings: { + fontStyle: "bold", + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.quote", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.italic", + settings: { + fontStyle: "italic", + // foreground: "", + }, + }, + { + scope: "markup.bold", + settings: { + fontStyle: "bold", + foreground: "var(--text-strong)", + }, + }, + { + scope: [ + "markup.raw", + "markup.inserted", + "meta.diff.header.to-file", + "punctuation.definition.inserted", + "markup.changed", + "punctuation.definition.changed", + "markup.ignored", + "markup.untracked", + ], + settings: { + foreground: "var(--text-base)", + }, + }, + { + scope: "meta.diff.range", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.diff.header", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.separator", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.output", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.export.default", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: [ + "brackethighlighter.tag", + "brackethighlighter.curly", + "brackethighlighter.round", + "brackethighlighter.square", + "brackethighlighter.angle", + "brackethighlighter.quote", + ], + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: ["constant.other.reference.link", "string.other.link"], + settings: { + fontStyle: "underline", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "token.info-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "token.warn-token", + settings: { + foreground: "var(--syntax-warning)", + }, + }, + { + scope: "token.debug-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, +] + +const semanticTokenColors = { + comment: "var(--syntax-comment)", + string: "var(--syntax-string)", + number: "var(--syntax-constant)", + regexp: "var(--syntax-regexp)", + keyword: "var(--syntax-keyword)", + variable: "var(--syntax-variable)", + parameter: "var(--syntax-variable)", + property: "var(--syntax-property)", + function: "var(--syntax-primitive)", + method: "var(--syntax-primitive)", + type: "var(--syntax-type)", + class: "var(--syntax-type)", + namespace: "var(--syntax-type)", + enumMember: "var(--syntax-primitive)", + "variable.constant": "var(--syntax-constant)", + "variable.defaultLibrary": "var(--syntax-unknown)", +} + registerCustomTheme("oc-1-light", () => { return Promise.resolve({ - name: "oc-1-light", type: "light", - colors: { - "editor.background": "transparent", - "editor.foreground": "#070707", - foreground: "#070707", - focusBorder: "#008cff", - "selection.background": "#dfe7ff", - "editor.selectionBackground": "#008cff2e", - "editor.lineHighlightBackground": "#dfe7ff8c", - "editorCursor.foreground": "#008cff", - "editorLineNumber.foreground": "#84848A", - "editorLineNumber.activeForeground": "#6C6C71", - "editorIndentGuide.background": "#eeeeef", - "editorIndentGuide.activeBackground": "#dbdbdd", - "diffEditor.insertedTextBackground": "#00cab133", - "diffEditor.deletedTextBackground": "#ff2e3f33", - "sideBar.background": "#f8f8f8", - "sideBar.foreground": "#6C6C71", - "sideBar.border": "#eeeeef", - "sideBarTitle.foreground": "#070707", - "sideBarSectionHeader.background": "#f8f8f8", - "sideBarSectionHeader.foreground": "#6C6C71", - "sideBarSectionHeader.border": "#eeeeef", - "activityBar.background": "#f8f8f8", - "activityBar.foreground": "#070707", - "activityBar.border": "#eeeeef", - "activityBar.activeBorder": "#008cff", - "activityBarBadge.background": "#008cff", - "activityBarBadge.foreground": "#ffffff", - "titleBar.activeBackground": "#f8f8f8", - "titleBar.activeForeground": "#070707", - "titleBar.inactiveBackground": "#f8f8f8", - "titleBar.inactiveForeground": "#84848A", - "titleBar.border": "#eeeeef", - "list.activeSelectionBackground": "#dfe7ffcc", - "list.activeSelectionForeground": "#070707", - "list.inactiveSelectionBackground": "#dfe7ff73", - "list.hoverBackground": "#dfe7ff59", - "list.focusOutline": "#008cff", - "tab.activeBackground": "#ffffff", - "tab.activeForeground": "#070707", - "tab.activeBorderTop": "#008cff", - "tab.inactiveBackground": "#f8f8f8", - "tab.inactiveForeground": "#84848A", - "tab.border": "#eeeeef", - "editorGroupHeader.tabsBackground": "#f8f8f8", - "editorGroupHeader.tabsBorder": "#eeeeef", - "panel.background": "#f8f8f8", - "panel.border": "#eeeeef", - "panelTitle.activeBorder": "#008cff", - "panelTitle.activeForeground": "#070707", - "panelTitle.inactiveForeground": "#84848A", - "statusBar.background": "#f8f8f8", - "statusBar.foreground": "#6C6C71", - "statusBar.border": "#eeeeef", - "statusBar.noFolderBackground": "#f8f8f8", - "statusBar.debuggingBackground": "#ffca00", - "statusBar.debuggingForeground": "#ffffff", - "statusBarItem.remoteBackground": "#f8f8f8", - "statusBarItem.remoteForeground": "#6C6C71", - "input.background": "#f2f2f3", - "input.border": "#dbdbdd", - "input.foreground": "#070707", - "input.placeholderForeground": "#8E8E95", - "dropdown.background": "#f2f2f3", - "dropdown.border": "#dbdbdd", - "dropdown.foreground": "#070707", - "button.background": "#008cff", - "button.foreground": "#ffffff", - "button.hoverBackground": "#1a98ff", - "textLink.foreground": "#008cff", - "textLink.activeForeground": "#008cff", - "gitDecoration.addedResourceForeground": "#00cab1", - "gitDecoration.conflictingResourceForeground": "#ffca00", - "gitDecoration.modifiedResourceForeground": "#008cff", - "gitDecoration.deletedResourceForeground": "#ff2e3f", - "gitDecoration.untrackedResourceForeground": "#00cab1", - "gitDecoration.ignoredResourceForeground": "#84848A", - "terminal.titleForeground": "#6C6C71", - "terminal.titleInactiveForeground": "#84848A", - "terminal.background": "#f8f8f8", - "terminal.foreground": "#6C6C71", - "terminal.ansiBlack": "#1F1F21", - "terminal.ansiRed": "#ff2e3f", - "terminal.ansiGreen": "#0dbe4e", - "terminal.ansiYellow": "#ffca00", - "terminal.ansiBlue": "#008cff", - "terminal.ansiMagenta": "#c635e4", - "terminal.ansiCyan": "#08c0ef", - "terminal.ansiWhite": "#c6c6c8", - "terminal.ansiBrightBlack": "#1F1F21", - "terminal.ansiBrightRed": "#ff2e3f", - "terminal.ansiBrightGreen": "#0dbe4e", - "terminal.ansiBrightYellow": "#ffca00", - "terminal.ansiBrightBlue": "#008cff", - "terminal.ansiBrightMagenta": "#c635e4", - "terminal.ansiBrightCyan": "#08c0ef", - "terminal.ansiBrightWhite": "#c6c6c8", - }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment"], - settings: { - foreground: "#84848A", - }, - }, - { - scope: "comment markup.link", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["string", "constant.other.symbol"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["punctuation.definition.string.begin", "punctuation.definition.string.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["constant.numeric", "constant.language.boolean"], - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "variable.other.constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["storage", "storage.type", "storage.modifier"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "token.storage", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression.instanceof", - "keyword.operator.expression.typeof", - "keyword.operator.expression.void", - "keyword.operator.expression.delete", - "keyword.operator.expression.in", - "keyword.operator.expression.of", - "keyword.operator.expression.keyof", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.delete", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["variable", "identifier", "meta.definition.variable"], - settings: { - foreground: "#d47628", - }, - }, - { - scope: [ - "variable.other.readwrite", - "meta.object-literal.key", - "support.variable.property", - "support.variable.object.process", - "support.variable.object.node", - ], - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.parameter.function", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.parameter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "variable.parameter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "variable.parameter.function.language.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.parameter.function.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.function", - "entity.name.function", - "meta.function-call", - "meta.require", - "support.function.any-method", - "variable.function", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "keyword.other.special-method", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.function", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "support.function.console", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["support.type", "entity.name.type", "entity.name.class", "storage.type"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: ["support.class", "entity.name.type.class"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: ["entity.name.class", "variable.other.class.js", "variable.other.class.ts"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.class.identifier.namespace.type", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.type.namespace", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.other.inherited-class", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.namespace", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["keyword.operator.logical", "keyword.operator.bitwise", "keyword.operator.channel"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "keyword.operator.arithmetic", - "keyword.operator.comparison", - "keyword.operator.relational", - "keyword.operator.increment", - "keyword.operator.decrement", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment.compound", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.assignment.compound.js", - "keyword.operator.assignment.compound.ts", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.ternary", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.optional", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "punctuation", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.delimiter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.key-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.terminator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.square", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.round", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.parameters", "punctuation.definition.typeparameters"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.block", "punctuation.definition.tag"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["meta.tag.tsx", "meta.tag.jsx", "meta.tag.js", "meta.tag.ts"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.expression.import", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "keyword.operator.module", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.type.object.console", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["support.module.node", "support.type.object.module", "entity.name.type.module"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.math", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.property.math", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.json", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.type.object.dom", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["support.variable.dom", "support.variable.property.dom"], - settings: { - foreground: "#d47628", - }, - }, - { - scope: "support.variable.property.process", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.property.object", - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.parameter.function.js", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["keyword.other.template.begin", "keyword.other.template.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["keyword.other.substitution.begin", "keyword.other.substitution.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.template.expression", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.section.embedded", - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.interpolation", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["punctuation.section.embedded.begin", "punctuation.section.embedded.end"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "punctuation.quasi.element", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "support.type.primitive.ts", - "support.type.builtin.ts", - "support.type.primitive.tsx", - "support.type.builtin.tsx", - ], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "support.type.type.flowtype", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "support.type.primitive", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "support.variable.magic.python", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "variable.parameter.function.language.special.self.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "punctuation.separator.period.python", - "punctuation.separator.element.python", - "punctuation.parenthesis.begin.python", - "punctuation.parenthesis.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "punctuation.definition.arguments.begin.python", - "punctuation.definition.arguments.end.python", - "punctuation.separator.arguments.python", - "punctuation.definition.list.begin.python", - "punctuation.definition.list.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.python", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.logical.python", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.function-call.generic.python", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "constant.character.format.placeholder.other.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.function.decorator.python", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["support.token.decorator.python", "meta.function.decorator.identifier.python"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "storage.modifier.lifetime.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.function.std.rust", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.lifetime.rust", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.language.rust", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "keyword.operator.misc.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.sigil.rust", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.constant.core.rust", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["meta.function.c", "meta.function.cpp"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.section.block.begin.bracket.curly.cpp", - "punctuation.section.block.end.bracket.curly.cpp", - "punctuation.terminator.statement.c", - "punctuation.section.block.begin.bracket.curly.c", - "punctuation.section.block.end.bracket.curly.c", - "punctuation.section.parens.begin.bracket.round.c", - "punctuation.section.parens.end.bracket.round.c", - "punctuation.section.parameters.begin.bracket.round.c", - "punctuation.section.parameters.end.bracket.round.c", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "keyword.operator.assignment.c", - "keyword.operator.comparison.c", - "keyword.operator.c", - "keyword.operator.increment.c", - "keyword.operator.decrement.c", - "keyword.operator.bitwise.shift.c", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.assignment.cpp", - "keyword.operator.comparison.cpp", - "keyword.operator.cpp", - "keyword.operator.increment.cpp", - "keyword.operator.decrement.cpp", - "keyword.operator.bitwise.shift.cpp", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["punctuation.separator.c", "punctuation.separator.cpp"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["support.type.posix-reserved.c", "support.type.posix-reserved.cpp"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.sizeof.c", "keyword.operator.sizeof.cpp"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "variable.c", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["storage.type.annotation.java", "storage.type.object.array.java"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "source.java", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.section.block.begin.java", - "punctuation.section.block.end.java", - "punctuation.definition.method-parameters.begin.java", - "punctuation.definition.method-parameters.end.java", - "meta.method.identifier.java", - "punctuation.section.method.begin.java", - "punctuation.section.method.end.java", - "punctuation.terminator.java", - "punctuation.section.class.begin.java", - "punctuation.section.class.end.java", - "punctuation.section.inner-class.begin.java", - "punctuation.section.inner-class.end.java", - "meta.method-call.java", - "punctuation.section.class.begin.bracket.curly.java", - "punctuation.section.class.end.bracket.curly.java", - "punctuation.section.method.begin.bracket.curly.java", - "punctuation.section.method.end.bracket.curly.java", - "punctuation.separator.period.java", - "punctuation.bracket.angle.java", - "punctuation.definition.annotation.java", - "meta.method.body.java", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.method.java", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["storage.modifier.import.java", "storage.type.java", "storage.type.generic.java"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.instanceof.java", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.definition.variable.name.java", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "token.variable.parameter.java", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "import.storage.java", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "token.package.keyword", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "token.package", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.storage.type.java", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.assignment.go", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["keyword.operator.arithmetic.go", "keyword.operator.address.go"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "entity.name.package.go", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.other.namespace.use.php", - "support.other.namespace.use-as.php", - "support.other.namespace.php", - "entity.other.alias.php", - "meta.interface.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.error-control.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.type.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["punctuation.section.array.begin.php", "punctuation.section.array.end.php"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "storage.type.php", - "meta.other.type.phpdoc.php", - "keyword.other.type.php", - "keyword.other.array.phpdoc.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "meta.function-call.php", - "meta.function-call.object.php", - "meta.function-call.static.php", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: [ - "punctuation.definition.parameters.begin.bracket.round.php", - "punctuation.definition.parameters.end.bracket.round.php", - "punctuation.separator.delimiter.php", - "punctuation.section.scope.begin.php", - "punctuation.section.scope.end.php", - "punctuation.terminator.expression.php", - "punctuation.definition.arguments.begin.bracket.round.php", - "punctuation.definition.arguments.end.bracket.round.php", - "punctuation.definition.storage-type.begin.bracket.round.php", - "punctuation.definition.storage-type.end.bracket.round.php", - "punctuation.definition.array.begin.bracket.round.php", - "punctuation.definition.array.end.bracket.round.php", - "punctuation.definition.begin.bracket.round.php", - "punctuation.definition.end.bracket.round.php", - "punctuation.definition.begin.bracket.curly.php", - "punctuation.definition.end.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - "punctuation.definition.section.switch-block.start.bracket.curly.php", - "punctuation.definition.section.switch-block.begin.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "support.constant.ext.php", - "support.constant.std.php", - "support.constant.core.php", - "support.constant.parser-token.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["entity.name.goto-label.php", "support.other.php"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: [ - "keyword.operator.logical.php", - "keyword.operator.bitwise.php", - "keyword.operator.arithmetic.php", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.regexp.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.comparison.php", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.heredoc.php", "keyword.operator.nowdoc.php"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "variable.other.class.php", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "invalid.illegal.non-null-typehinted.php", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "variable.other.generic-type.haskell", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "storage.type.haskell", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "storage.type.cs", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.variable.local.cs", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "entity.name.label.cs", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "entity.name.scope-resolution.function.call", - "entity.name.scope-resolution.function.definition", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "punctuation.definition.delayed.unison", - "punctuation.definition.list.begin.unison", - "punctuation.definition.list.end.unison", - "punctuation.definition.ability.begin.unison", - "punctuation.definition.ability.end.unison", - "punctuation.operator.assignment.as.unison", - "punctuation.separator.pipe.unison", - "punctuation.separator.delimiter.unison", - "punctuation.definition.hash.unison", - ], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "support.constant.edge", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.type.prelude.elm", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.constant.elm", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.global.clojure", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.symbol.clojure", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "constant.keyword.clojure", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["meta.arguments.coffee", "variable.parameter.function.coffee"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "storage.modifier.import.groovy", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.method.groovy", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "meta.definition.variable.name.groovy", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "meta.definition.class.inherited.classes.groovy", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "support.variable.semantic.hlsl", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.type.texture.hlsl", - "support.type.sampler.hlsl", - "support.type.object.hlsl", - "support.type.object.rw.hlsl", - "support.type.fx.hlsl", - "support.type.object.hlsl", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["text.variable", "text.bracketed"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["support.type.swift", "support.type.vb.asp"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.scope.prerequisites.makefile", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "source.makefile", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "source.ini", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "constant.language.symbol.ruby", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["function.parameter.ruby", "function.parameter.cs"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "constant.language.symbol.elixir", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "entity.name.function.xi", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.class.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "constant.character.character-class.regexp.xi", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "constant.regexp.xi", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.control.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "invalid.xi", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "beginning.punctuation.definition.quote.markdown.xi", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "constant.character.xi", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "accent.xi", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "wikiword.xi", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.other.color.rgb-value.xi", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "punctuation.definition.tag.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["support.constant.property-value.scss", "support.constant.property-value.css"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["keyword.operator.css", "keyword.operator.scss", "keyword.operator.less"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "support.constant.color.w3c-standard-color-name.css", - "support.constant.color.w3c-standard-color-name.scss", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.separator.list.comma.css", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.vendored.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.property-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.font-name", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.other.attribute-name.class.css", - settings: { - foreground: "#16a994", - fontStyle: "normal", - }, - }, - { - scope: "entity.other.attribute-name.id", - settings: { - foreground: "#7b43f8", - fontStyle: "normal", - }, - }, - { - scope: [ - "entity.other.attribute-name.pseudo-element", - "entity.other.attribute-name.pseudo-class", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "meta.selector", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "selector.sass", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "rgb-value", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "inline-color-decoration rgb-value", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "less rgb-value", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "control.elements", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.less", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "#16a994", - fontStyle: "normal", - }, - }, - { - scope: "constant.character.entity", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "meta.tag", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "invalid.illegal.bad-ampersand.html", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "markup.heading", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.heading punctuation.definition.heading", "entity.name.section"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.section.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.heading.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.heading.setext", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["markup.heading.setext.1.markdown", "markup.heading.setext.2.markdown"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.bold", "todo.bold"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.bold", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.bold.markdown", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["markup.italic", "punctuation.definition.italic", "todo.emphasis"], - settings: { - foreground: "#fc2b73", - fontStyle: "italic", - }, - }, - { - scope: "emphasis md", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "markup.italic.markdown", - settings: { - fontStyle: "italic", - }, - }, - { - scope: ["markup.underline.link.markdown", "markup.underline.link.image.markdown"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["string.other.link.title.markdown", "string.other.link.description.markdown"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "punctuation.definition.metadata.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.inline.raw.markdown", "markup.inline.raw.string.markdown"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.list.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.definition.string.begin.markdown", - "punctuation.definition.string.end.markdown", - ], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.quote.markdown", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.changed.diff", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "meta.diff.header.from-file", - "meta.diff.header.to-file", - "punctuation.definition.from-file.diff", - "punctuation.definition.to-file.diff", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "markup.inserted.diff", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "markup.deleted.diff", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "#17a5af", - }, - }, - { - scope: "constant.other.character-class.regexp", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "source.json meta.structure.dictionary.json > string.quoted.json", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: - "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > value.json > string.quoted.json", - "source.json meta.structure.array.json > value.json > string.quoted.json", - "source.json meta.structure.dictionary.json > value.json > string.quoted.json > punctuation", - "source.json meta.structure.array.json > value.json > string.quoted.json > punctuation", - ], - settings: { - foreground: "#199f43", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > constant.language.json", - "source.json meta.structure.array.json > constant.language.json", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.json", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "support.type.property-name.json punctuation", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.block.sequence.item.yaml", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.end", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.begin", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "token.error-token", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "invalid.illegal", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.broken", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.deprecated", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.unimplemented", - settings: { - foreground: "#ffffff", - }, - }, - ], - semanticTokenColors: { - comment: "#84848A", - string: "#199f43", - number: "#1ca1c7", - regexp: "#17a5af", - keyword: "#fc2b73", - variable: "#d47628", - parameter: "#79797F", - property: "#d47628", - function: "#7b43f8", - method: "#7b43f8", - type: "#c635e4", - class: "#c635e4", - namespace: "#d5a910", - enumMember: "#08c0ef", - "variable.constant": "#d5a910", - "variable.defaultLibrary": "#d5a910", - }, + name: "oc-1-light", + colors, + tokenColors, + semanticTokenColors, } as unknown as ThemeRegistrationResolved) }) @@ -1969,1783 +567,8 @@ registerCustomTheme("oc-1-dark", () => { return Promise.resolve({ name: "oc-1-dark", type: "dark", - colors: { - "editor.background": "transparent", - "editor.foreground": "#fbfbfb", - foreground: "#fbfbfb", - focusBorder: "#1a76d4", - "selection.background": "#19253c", - "editor.selectionBackground": "#1a76d44d", - "editor.lineHighlightBackground": "#19253c8c", - "editorCursor.foreground": "#1a76d4", - "editorLineNumber.foreground": "#84848A", - "editorLineNumber.activeForeground": "#adadb1", - "editorIndentGuide.background": "#39393c", - "editorIndentGuide.activeBackground": "#2e2e30", - "diffEditor.insertedTextBackground": "#00cab11a", - "diffEditor.deletedTextBackground": "#ff2e3f1a", - "sideBar.background": "#141415", - "sideBar.foreground": "#adadb1", - "sideBar.border": "#070707", - "sideBarTitle.foreground": "#fbfbfb", - "sideBarSectionHeader.background": "#141415", - "sideBarSectionHeader.foreground": "#adadb1", - "sideBarSectionHeader.border": "#070707", - "activityBar.background": "#141415", - "activityBar.foreground": "#fbfbfb", - "activityBar.border": "#070707", - "activityBar.activeBorder": "#1a76d4", - "activityBarBadge.background": "#1a76d4", - "activityBarBadge.foreground": "#070707", - "titleBar.activeBackground": "#141415", - "titleBar.activeForeground": "#fbfbfb", - "titleBar.inactiveBackground": "#141415", - "titleBar.inactiveForeground": "#84848A", - "titleBar.border": "#070707", - "list.activeSelectionBackground": "#19253c99", - "list.activeSelectionForeground": "#fbfbfb", - "list.inactiveSelectionBackground": "#19253c73", - "list.hoverBackground": "#19253c59", - "list.focusOutline": "#1a76d4", - "tab.activeBackground": "#070707", - "tab.activeForeground": "#fbfbfb", - "tab.activeBorderTop": "#1a76d4", - "tab.inactiveBackground": "#141415", - "tab.inactiveForeground": "#84848A", - "tab.border": "#070707", - "editorGroupHeader.tabsBackground": "#141415", - "editorGroupHeader.tabsBorder": "#070707", - "panel.background": "#141415", - "panel.border": "#070707", - "panelTitle.activeBorder": "#1a76d4", - "panelTitle.activeForeground": "#fbfbfb", - "panelTitle.inactiveForeground": "#84848A", - "statusBar.background": "#141415", - "statusBar.foreground": "#adadb1", - "statusBar.border": "#070707", - "statusBar.noFolderBackground": "#141415", - "statusBar.debuggingBackground": "#ffca00", - "statusBar.debuggingForeground": "#070707", - "statusBarItem.remoteBackground": "#141415", - "statusBarItem.remoteForeground": "#adadb1", - "input.background": "#1F1F21", - "input.border": "#424245", - "input.foreground": "#fbfbfb", - "input.placeholderForeground": "#79797F", - "dropdown.background": "#1F1F21", - "dropdown.border": "#424245", - "dropdown.foreground": "#fbfbfb", - "button.background": "#1a76d4", - "button.foreground": "#070707", - "button.hoverBackground": "#186bc0", - "textLink.foreground": "#1a76d4", - "textLink.activeForeground": "#1a76d4", - "gitDecoration.addedResourceForeground": "#00cab1", - "gitDecoration.conflictingResourceForeground": "#ffca00", - "gitDecoration.modifiedResourceForeground": "#1a76d4", - "gitDecoration.deletedResourceForeground": "#ff2e3f", - "gitDecoration.untrackedResourceForeground": "#00cab1", - "gitDecoration.ignoredResourceForeground": "#84848A", - "terminal.titleForeground": "#adadb1", - "terminal.titleInactiveForeground": "#84848A", - "terminal.background": "#141415", - "terminal.foreground": "#adadb1", - "terminal.ansiBlack": "#141415", - "terminal.ansiRed": "#ff2e3f", - "terminal.ansiGreen": "#0dbe4e", - "terminal.ansiYellow": "#ffca00", - "terminal.ansiBlue": "#008cff", - "terminal.ansiMagenta": "#c635e4", - "terminal.ansiCyan": "#08c0ef", - "terminal.ansiWhite": "#c6c6c8", - "terminal.ansiBrightBlack": "#141415", - "terminal.ansiBrightRed": "#ff2e3f", - "terminal.ansiBrightGreen": "#0dbe4e", - "terminal.ansiBrightYellow": "#ffca00", - "terminal.ansiBrightBlue": "#008cff", - "terminal.ansiBrightMagenta": "#c635e4", - "terminal.ansiBrightCyan": "#08c0ef", - "terminal.ansiBrightWhite": "#c6c6c8", - }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment"], - settings: { - foreground: "#84848A", - }, - }, - { - scope: "comment markup.link", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["string", "constant.other.symbol"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["punctuation.definition.string.begin", "punctuation.definition.string.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["constant.numeric", "constant.language.boolean"], - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "constant", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.definition.constant", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "variable.other.constant", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["storage", "storage.type", "storage.modifier"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "token.storage", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression.instanceof", - "keyword.operator.expression.typeof", - "keyword.operator.expression.void", - "keyword.operator.expression.delete", - "keyword.operator.expression.in", - "keyword.operator.expression.of", - "keyword.operator.expression.keyof", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.delete", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["variable", "identifier", "meta.definition.variable"], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: [ - "variable.other.readwrite", - "meta.object-literal.key", - "support.variable.property", - "support.variable.object.process", - "support.variable.object.node", - ], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "variable.parameter.function", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "function.parameter", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "variable.parameter", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "variable.parameter.function.language.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "variable.parameter.function.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: [ - "support.function", - "entity.name.function", - "meta.function-call", - "meta.require", - "support.function.any-method", - "variable.function", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "keyword.other.special-method", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.function", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "support.function.console", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["support.type", "entity.name.type", "entity.name.class", "storage.type"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: ["support.class", "entity.name.type.class"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: ["entity.name.class", "variable.other.class.js", "variable.other.class.ts"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.class.identifier.namespace.type", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.type.namespace", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.other.inherited-class", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.namespace", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["keyword.operator.logical", "keyword.operator.bitwise", "keyword.operator.channel"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "keyword.operator.arithmetic", - "keyword.operator.comparison", - "keyword.operator.relational", - "keyword.operator.increment", - "keyword.operator.decrement", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment.compound", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.assignment.compound.js", - "keyword.operator.assignment.compound.ts", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.ternary", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.optional", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "punctuation", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.delimiter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.key-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.terminator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.square", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.round", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.parameters", "punctuation.definition.typeparameters"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.block", "punctuation.definition.tag"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["meta.tag.tsx", "meta.tag.jsx", "meta.tag.js", "meta.tag.ts"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.expression.import", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "keyword.operator.module", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.type.object.console", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["support.module.node", "support.type.object.module", "entity.name.type.module"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "support.constant.math", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "support.constant.property.math", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "support.constant.json", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "support.type.object.dom", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["support.variable.dom", "support.variable.property.dom"], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "support.variable.property.process", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "meta.property.object", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.parameter.function.js", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["keyword.other.template.begin", "keyword.other.template.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["keyword.other.substitution.begin", "keyword.other.substitution.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.template.expression", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.section.embedded", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.interpolation", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["punctuation.section.embedded.begin", "punctuation.section.embedded.end"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "punctuation.quasi.element", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "support.type.primitive.ts", - "support.type.builtin.ts", - "support.type.primitive.tsx", - "support.type.builtin.tsx", - ], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "support.type.type.flowtype", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "support.type.primitive", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "support.variable.magic.python", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "variable.parameter.function.language.special.self.python", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "punctuation.separator.period.python", - "punctuation.separator.element.python", - "punctuation.parenthesis.begin.python", - "punctuation.parenthesis.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "punctuation.definition.arguments.begin.python", - "punctuation.definition.arguments.end.python", - "punctuation.separator.arguments.python", - "punctuation.definition.list.begin.python", - "punctuation.definition.list.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.python", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.logical.python", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.function-call.generic.python", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "constant.character.format.placeholder.other.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "meta.function.decorator.python", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["support.token.decorator.python", "meta.function.decorator.identifier.python"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "storage.modifier.lifetime.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.function.std.rust", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.lifetime.rust", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "variable.language.rust", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "keyword.operator.misc.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.sigil.rust", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.constant.core.rust", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["meta.function.c", "meta.function.cpp"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.section.block.begin.bracket.curly.cpp", - "punctuation.section.block.end.bracket.curly.cpp", - "punctuation.terminator.statement.c", - "punctuation.section.block.begin.bracket.curly.c", - "punctuation.section.block.end.bracket.curly.c", - "punctuation.section.parens.begin.bracket.round.c", - "punctuation.section.parens.end.bracket.round.c", - "punctuation.section.parameters.begin.bracket.round.c", - "punctuation.section.parameters.end.bracket.round.c", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "keyword.operator.assignment.c", - "keyword.operator.comparison.c", - "keyword.operator.c", - "keyword.operator.increment.c", - "keyword.operator.decrement.c", - "keyword.operator.bitwise.shift.c", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.assignment.cpp", - "keyword.operator.comparison.cpp", - "keyword.operator.cpp", - "keyword.operator.increment.cpp", - "keyword.operator.decrement.cpp", - "keyword.operator.bitwise.shift.cpp", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["punctuation.separator.c", "punctuation.separator.cpp"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["support.type.posix-reserved.c", "support.type.posix-reserved.cpp"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.sizeof.c", "keyword.operator.sizeof.cpp"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "variable.c", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["storage.type.annotation.java", "storage.type.object.array.java"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "source.java", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.section.block.begin.java", - "punctuation.section.block.end.java", - "punctuation.definition.method-parameters.begin.java", - "punctuation.definition.method-parameters.end.java", - "meta.method.identifier.java", - "punctuation.section.method.begin.java", - "punctuation.section.method.end.java", - "punctuation.terminator.java", - "punctuation.section.class.begin.java", - "punctuation.section.class.end.java", - "punctuation.section.inner-class.begin.java", - "punctuation.section.inner-class.end.java", - "meta.method-call.java", - "punctuation.section.class.begin.bracket.curly.java", - "punctuation.section.class.end.bracket.curly.java", - "punctuation.section.method.begin.bracket.curly.java", - "punctuation.section.method.end.bracket.curly.java", - "punctuation.separator.period.java", - "punctuation.bracket.angle.java", - "punctuation.definition.annotation.java", - "meta.method.body.java", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.method.java", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["storage.modifier.import.java", "storage.type.java", "storage.type.generic.java"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.instanceof.java", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.definition.variable.name.java", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "token.variable.parameter.java", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "import.storage.java", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "token.package.keyword", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "token.package", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.storage.type.java", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.assignment.go", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: ["keyword.operator.arithmetic.go", "keyword.operator.address.go"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "entity.name.package.go", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "support.other.namespace.use.php", - "support.other.namespace.use-as.php", - "support.other.namespace.php", - "entity.other.alias.php", - "meta.interface.php", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.error-control.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.type.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["punctuation.section.array.begin.php", "punctuation.section.array.end.php"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "storage.type.php", - "meta.other.type.phpdoc.php", - "keyword.other.type.php", - "keyword.other.array.phpdoc.php", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "meta.function-call.php", - "meta.function-call.object.php", - "meta.function-call.static.php", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: [ - "punctuation.definition.parameters.begin.bracket.round.php", - "punctuation.definition.parameters.end.bracket.round.php", - "punctuation.separator.delimiter.php", - "punctuation.section.scope.begin.php", - "punctuation.section.scope.end.php", - "punctuation.terminator.expression.php", - "punctuation.definition.arguments.begin.bracket.round.php", - "punctuation.definition.arguments.end.bracket.round.php", - "punctuation.definition.storage-type.begin.bracket.round.php", - "punctuation.definition.storage-type.end.bracket.round.php", - "punctuation.definition.array.begin.bracket.round.php", - "punctuation.definition.array.end.bracket.round.php", - "punctuation.definition.begin.bracket.round.php", - "punctuation.definition.end.bracket.round.php", - "punctuation.definition.begin.bracket.curly.php", - "punctuation.definition.end.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - "punctuation.definition.section.switch-block.start.bracket.curly.php", - "punctuation.definition.section.switch-block.begin.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "support.constant.ext.php", - "support.constant.std.php", - "support.constant.core.php", - "support.constant.parser-token.php", - ], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["entity.name.goto-label.php", "support.other.php"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: [ - "keyword.operator.logical.php", - "keyword.operator.bitwise.php", - "keyword.operator.arithmetic.php", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.regexp.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.comparison.php", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.heredoc.php", "keyword.operator.nowdoc.php"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "variable.other.class.php", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "invalid.illegal.non-null-typehinted.php", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "variable.other.generic-type.haskell", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "storage.type.haskell", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "storage.type.cs", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.name.variable.local.cs", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "entity.name.label.cs", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "entity.name.scope-resolution.function.call", - "entity.name.scope-resolution.function.definition", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "punctuation.definition.delayed.unison", - "punctuation.definition.list.begin.unison", - "punctuation.definition.list.end.unison", - "punctuation.definition.ability.begin.unison", - "punctuation.definition.ability.end.unison", - "punctuation.operator.assignment.as.unison", - "punctuation.separator.pipe.unison", - "punctuation.separator.delimiter.unison", - "punctuation.definition.hash.unison", - ], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "support.constant.edge", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.type.prelude.elm", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.constant.elm", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.global.clojure", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.symbol.clojure", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "constant.keyword.clojure", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["meta.arguments.coffee", "variable.parameter.function.coffee"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "storage.modifier.import.groovy", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.method.groovy", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "meta.definition.variable.name.groovy", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "meta.definition.class.inherited.classes.groovy", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "support.variable.semantic.hlsl", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "support.type.texture.hlsl", - "support.type.sampler.hlsl", - "support.type.object.hlsl", - "support.type.object.rw.hlsl", - "support.type.fx.hlsl", - "support.type.object.hlsl", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["text.variable", "text.bracketed"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["support.type.swift", "support.type.vb.asp"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.scope.prerequisites.makefile", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "source.makefile", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "source.ini", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "constant.language.symbol.ruby", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["function.parameter.ruby", "function.parameter.cs"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "constant.language.symbol.elixir", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "entity.name.function.xi", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.name.class.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "constant.character.character-class.regexp.xi", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "constant.regexp.xi", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.control.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "invalid.xi", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "beginning.punctuation.definition.quote.markdown.xi", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "constant.character.xi", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "accent.xi", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "wikiword.xi", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.other.color.rgb-value.xi", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "punctuation.definition.tag.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["support.constant.property-value.scss", "support.constant.property-value.css"], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["keyword.operator.css", "keyword.operator.scss", "keyword.operator.less"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "support.constant.color.w3c-standard-color-name.css", - "support.constant.color.w3c-standard-color-name.scss", - ], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.separator.list.comma.css", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.vendored.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.property-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.font-name", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.other.attribute-name.class.css", - settings: { - foreground: "#61d5c0", - fontStyle: "normal", - }, - }, - { - scope: "entity.other.attribute-name.id", - settings: { - foreground: "#9d6afb", - fontStyle: "normal", - }, - }, - { - scope: [ - "entity.other.attribute-name.pseudo-element", - "entity.other.attribute-name.pseudo-class", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "meta.selector", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "selector.sass", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "rgb-value", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "inline-color-decoration rgb-value", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "less rgb-value", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "control.elements", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "keyword.operator.less", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "#61d5c0", - fontStyle: "normal", - }, - }, - { - scope: "constant.character.entity", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "meta.tag", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "invalid.illegal.bad-ampersand.html", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "markup.heading", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.heading punctuation.definition.heading", "entity.name.section"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.section.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.heading.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.heading.setext", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["markup.heading.setext.1.markdown", "markup.heading.setext.2.markdown"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.bold", "todo.bold"], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.definition.bold", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "punctuation.definition.bold.markdown", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["markup.italic", "punctuation.definition.italic", "todo.emphasis"], - settings: { - foreground: "#ff678d", - fontStyle: "italic", - }, - }, - { - scope: "emphasis md", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "markup.italic.markdown", - settings: { - fontStyle: "italic", - }, - }, - { - scope: ["markup.underline.link.markdown", "markup.underline.link.image.markdown"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["string.other.link.title.markdown", "string.other.link.description.markdown"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "punctuation.definition.metadata.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.inline.raw.markdown", "markup.inline.raw.string.markdown"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.list.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.definition.string.begin.markdown", - "punctuation.definition.string.end.markdown", - ], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.quote.markdown", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.changed.diff", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "meta.diff.header.from-file", - "meta.diff.header.to-file", - "punctuation.definition.from-file.diff", - "punctuation.definition.to-file.diff", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "markup.inserted.diff", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "markup.deleted.diff", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "#64d1db", - }, - }, - { - scope: "constant.other.character-class.regexp", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "source.json meta.structure.dictionary.json > string.quoted.json", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: - "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > value.json > string.quoted.json", - "source.json meta.structure.array.json > value.json > string.quoted.json", - "source.json meta.structure.dictionary.json > value.json > string.quoted.json > punctuation", - "source.json meta.structure.array.json > value.json > string.quoted.json > punctuation", - ], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > constant.language.json", - "source.json meta.structure.array.json > constant.language.json", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.json", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "support.type.property-name.json punctuation", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.block.sequence.item.yaml", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.end", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.begin", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "token.error-token", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "invalid.illegal", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.broken", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.deprecated", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.unimplemented", - settings: { - foreground: "#ffffff", - }, - }, - ], - semanticTokenColors: { - comment: "#84848A", - string: "#5ecc71", - number: "#68cdf2", - regexp: "#64d1db", - keyword: "#ff678d", - variable: "#ffa359", - parameter: "#adadb1", - property: "#ffa359", - function: "#9d6afb", - method: "#9d6afb", - type: "#d568ea", - class: "#d568ea", - namespace: "#ffca00", - enumMember: "#08c0ef", - "variable.constant": "#ffd452", - "variable.defaultLibrary": "#ffca00", - }, + colors, + tokenColors, + semanticTokenColors, } as unknown as ThemeRegistrationResolved) }) diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index a5f982d6f..95a497ba9 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/packages/ui/src/styles/tailwind/colors.css @@ -196,15 +196,20 @@ --color-icon-diff-delete-base: var(--icon-diff-delete-base); --color-icon-diff-delete-hover: var(--icon-diff-delete-hover); --color-syntax-comment: var(--syntax-comment); + --color-syntax-regexp: var(--syntax-regexp); --color-syntax-string: var(--syntax-string); --color-syntax-keyword: var(--syntax-keyword); --color-syntax-function: var(--syntax-function); --color-syntax-number: var(--syntax-number); --color-syntax-operator: var(--syntax-operator); --color-syntax-variable: var(--syntax-variable); + --color-syntax-property: var(--syntax-property); + --color-syntax-parameter: var(--syntax-parameter); --color-syntax-type: var(--syntax-type); --color-syntax-constant: var(--syntax-constant); --color-syntax-punctuation: var(--syntax-punctuation); + --color-syntax-namespace: var(--syntax-namespace); + --color-syntax-enum: var(--syntax-enum); --color-syntax-success: var(--syntax-success); --color-syntax-warning: var(--syntax-warning); --color-syntax-critical: var(--syntax-critical); diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 6187eef9d..431d811da 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -268,20 +268,25 @@ --icon-diff-add-active: var(--mint-light-12); --icon-diff-delete-base: var(--ember-light-10); --icon-diff-delete-hover: var(--ember-light-11); - --syntax-comment: #8a8a8a; - --syntax-string: #d68c27; - --syntax-keyword: #3b7dd8; - --syntax-function: #d1383d; - --syntax-number: #3d9a57; - --syntax-operator: #d68c27; - --syntax-variable: #b0851f; - --syntax-type: #318795; - --syntax-constant: #953170; - --syntax-punctuation: #1a1a1a; - --syntax-success: var(--apple-dark-10); + --syntax-comment: var(--text-weaker); + --syntax-regexp: var(--text-base); + --syntax-string: var(--mint-light-11); + --syntax-keyword: var(--text-weak); + --syntax-primitive: var(--ember-light-11); + --syntax-operator: var(--text-weak); + --syntax-variable: var(--text-strong); + --syntax-property: var(--lilac-light-11); + --syntax-type: var(--cobalt-light-11); + --syntax-constant: var(--lilac-light-11); + --syntax-punctuation: var(--text-weak); + --syntax-object: var(--blue-light-11); + --syntax-success: var(--apple-light-10); --syntax-warning: var(--amber-light-10); - --syntax-critical: var(--ember-dark-9); - --syntax-info: var(--lilac-dark-11); + --syntax-critical: var(--ember-light-9); + --syntax-info: var(--lilac-light-11); + --syntax-diff-add: var(--mint-light-11); + --syntax-diff-delete: var(--ember-light-11); + --syntax-unknown: red; --markdown-heading: #d68c27; --markdown-text: #1a1a1a; --markdown-link: #3b7dd8; @@ -503,20 +508,24 @@ --icon-diff-add-active: var(--mint-dark-11); --icon-diff-delete-base: var(--ember-dark-9); --icon-diff-delete-hover: var(--ember-dark-10); - --syntax-comment: #808080; - --syntax-string: #9d7cd8; - --syntax-keyword: #fab283; - --syntax-function: #e06c75; - --syntax-number: #7fd88f; - --syntax-operator: #f5a742; - --syntax-variable: #e5c07b; - --syntax-type: #56b6c2; - --syntax-constant: #c2569a; - --syntax-punctuation: #eeeeee; + --syntax-comment: var(--text-weaker); + --syntax-regexp: var(--text-base); + --syntax-string: var(--mint-dark-11); + --syntax-keyword: var(--text-weak); + --syntax-primitive: var(--ember-dark-11); + --syntax-operator: var(--text-weak); + --syntax-variable: var(--text-strong); + --syntax-property: var(--lilac-dark-11); + --syntax-type: var(--cobalt-dark-11); + --syntax-constant: var(--lilac-dark-11); + --syntax-punctuation: var(--text-weak); + --syntax-object: var(--blue-dark-11); --syntax-success: var(--apple-dark-10); --syntax-warning: var(--amber-dark-10); - --syntax-critical: var(--ember-dark-10); - --syntax-info: var(--lilac-dark-10); + --syntax-critical: var(--ember-dark-9); + --syntax-info: var(--lilac-dark-11); + --syntax-diff-add: var(--mint-dark-11); + --syntax-diff-delete: var(--ember-dark-11); --markdown-heading: #9d7cd8; --markdown-text: #eeeeee; --markdown-link: #fab283; diff --git a/theme-test.java b/theme-test.java new file mode 100644 index 000000000..7c1b582af --- /dev/null +++ b/theme-test.java @@ -0,0 +1,461 @@ +package com.example.theme; + +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.stream.*; +import java.time.*; +import java.time.format.*; +import java.net.*; +import java.io.*; +import java.nio.file.*; +import java.sql.*; +import java.lang.annotation.*; +import java.math.BigDecimal; +import java.math.BigInteger; + +// Enum definition +public enum LogLevel { + DEBUG(0, "Debug"), + INFO(1, "Info"), + WARN(2, "Warning"), + ERROR(3, "Error"); + + private final int level; + private final String description; + + LogLevel(int level, String description) { + this.level = level; + this.description = description; + } + + public int getLevel() { return level; } + public String getDescription() { return description; } +} + +// Interface with generics +public interface Repository { + Optional findById(Long id); + List findAll(); + T save(T entity); + void delete(Long id); + Stream stream(); + + @FunctionalInterface + interface Predicate { + boolean test(T t); + } +} + +// Abstract class +public abstract class AbstractService implements Repository { + protected final Map cache = new ConcurrentHashMap<>(); + protected volatile boolean initialized = false; + + @Override + public Optional findById(Long id) { + return Optional.ofNullable(cache.get(id)); + } + + @Override + public List findAll() { + return new ArrayList<>(cache.values()); + } + + @Override + public Stream stream() { + return cache.values().stream(); + } + + protected abstract void validate(T entity) throws ValidationException; +} + +// Annotation definition +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Service { + String value() default ""; + boolean transactional() default true; + Class[] exceptions() = {}; +} + +// Record class (Java 14+) +public record User( + Long id, + String username, + String email, + @Deprecated String fullName, + LocalDateTime createdAt, + boolean active +) implements Entity { + + public User { + Objects.requireNonNull(username, "Username cannot be null"); + Objects.requireNonNull(email, "Email cannot be null"); + Objects.requireNonNull(createdAt, "Created date cannot be null"); + } + + public static User of(String username, String email) { + return new User(null, username, email, null, LocalDateTime.now(), true); + } + + public User withId(Long id) { + return new User(id, username, email, fullName, createdAt, active); + } +} + +// Exception classes +public class ValidationException extends RuntimeException { + private final List errors; + + public ValidationException(String message) { + super(message); + this.errors = List.of(message); + } + + public ValidationException(List errors) { + super(String.join(", ", errors)); + this.errors = Collections.unmodifiableList(errors); + } + + public List getErrors() { return errors; } +} + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String resource, Long id) { + super(String.format("%s with id %d not found", resource, id)); + } +} + +// Service implementation +@Service(value = "userService", transactional = true) +public class UserService extends AbstractService { + + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + private static final int MAX_RETRY_ATTEMPTS = 3; + private static final Duration TIMEOUT = Duration.ofSeconds(30); + + private final EmailService emailService; + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + @Inject + public UserService(EmailService emailService, + UserRepository userRepository, + PasswordEncoder passwordEncoder) { + this.emailService = emailService; + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + } + + @Override + protected void validate(User user) throws ValidationException { + List errors = new ArrayList<>(); + + if (user.username() == null || user.username().trim().isEmpty()) { + errors.add("Username is required"); + } else if (!user.username().matches("^[a-zA-Z0-9_]{3,20}$")) { + errors.add("Username must be 3-20 characters, alphanumeric and underscore only"); + } + + if (user.email() == null || !user.email().matches("^[A-Za-z0-9+_.-]+@(.+)$")) { + errors.add("Valid email is required"); + } + + if (!errors.isEmpty()) { + throw new ValidationException(errors); + } + } + + @Transactional + public User createUser(CreateUserRequest request) throws ValidationException { + logger.info("Creating new user: {}", request.username()); + + // Check if user already exists + if (userRepository.findByUsername(request.username()).isPresent()) { + throw new ValidationException("Username already exists"); + } + + if (userRepository.findByEmail(request.email()).isPresent()) { + throw new ValidationException("Email already exists"); + } + + // Create new user + User user = User.of(request.username(), request.email()) + .withId(generateId()); + + validate(user); + + try { + User savedUser = userRepository.save(user); + cache.put(savedUser.id(), savedUser); + + // Send welcome email asynchronously + CompletableFuture.runAsync(() -> + emailService.sendWelcomeEmail(savedUser) + ).exceptionally(throwable -> { + logger.error("Failed to send welcome email to user {}", savedUser.id(), throwable); + return null; + }); + + logger.info("Successfully created user with ID: {}", savedUser.id()); + return savedUser; + + } catch (DataAccessException e) { + logger.error("Database error while creating user", e); + throw new ServiceException("Failed to create user", e); + } + } + + public Optional findByUsername(String username) { + return cache.values().stream() + .filter(user -> user.username().equals(username)) + .findFirst(); + } + + public List findActiveUsers() { + return cache.values().stream() + .filter(User::active) + .sorted(Comparator.comparing(User::createdAt).reversed()) + .collect(Collectors.toList()); + } + + @Retry(maxAttempts = MAX_RETRY_ATTEMPTS, backoff = @Backoff(delay = 1000)) + public User updateUser(Long id, UpdateUserRequest request) { + User existingUser = findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User", id)); + + User updatedUser = new User( + id, + request.username() != null ? request.username() : existingUser.username(), + request.email() != null ? request.email() : existingUser.email(), + existingUser.fullName(), + existingUser.createdAt(), + request.active() != null ? request.active() : existingUser.active() + ); + + validate(updatedUser); + + try { + User savedUser = userRepository.save(updatedUser); + cache.put(id, savedUser); + return savedUser; + } catch (DataAccessException e) { + logger.error("Failed to update user with ID: {}", id, e); + throw new ServiceException("Failed to update user", e); + } + } + + @Async + public CompletableFuture deleteUser(Long id) { + return CompletableFuture.runAsync(() -> { + try { + userRepository.deleteById(id); + cache.remove(id); + logger.info("Successfully deleted user with ID: {}", id); + } catch (DataAccessException e) { + logger.error("Failed to delete user with ID: {}", id, e); + throw new ServiceException("Failed to delete user", e); + } + }); + } + + private Long generateId() { + return System.currentTimeMillis() + (long)(Math.random() * 1000); + } +} + +// Utility class +public final class DateUtils { + + private DateUtils() { + // Utility class - prevent instantiation + } + + private static final DateTimeFormatter ISO_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + public static String formatIsoDateTime(LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).format(ISO_FORMATTER); + } + + public static LocalDateTime parseIsoDateTime(String isoString) { + return LocalDateTime.parse(isoString, ISO_FORMATTER); + } + + public static boolean isWithinLastDays(LocalDateTime dateTime, int days) { + return dateTime.isAfter(LocalDateTime.now().minusDays(days)); + } +} + +// Builder pattern +public class UserQueryBuilder { + private String username; + private String email; + private Boolean active; + private LocalDateTime createdAfter; + private LocalDateTime createdBefore; + private SortOrder sortOrder = SortOrder.ASC; + private String sortBy = "createdAt"; + private int limit = 100; + private int offset = 0; + + public UserQueryBuilder withUsername(String username) { + this.username = username; + return this; + } + + public UserQueryBuilder withEmail(String email) { + this.email = email; + return this; + } + + public UserQueryBuilder activeOnly(boolean active) { + this.active = active; + return this; + } + + public UserQueryBuilder createdAfter(LocalDateTime date) { + this.createdAfter = date; + return this; + } + + public UserQueryBuilder createdBefore(LocalDateTime date) { + this.createdBefore = date; + return this; + } + + public UserQueryBuilder sortBy(String field, SortOrder order) { + this.sortBy = field; + this.sortOrder = order; + return this; + } + + public UserQueryBuilder limit(int limit) { + this.limit = Math.max(1, Math.min(limit, 1000)); + return this; + } + + public UserQueryBuilder offset(int offset) { + this.offset = Math.max(0, offset); + return this; + } + + public UserQuery build() { + return new UserQuery(username, email, active, createdAfter, createdBefore, + sortBy, sortOrder, limit, offset); + } +} + +// Lambda expressions and streams +public class StreamProcessor { + + private static final Map> TYPE_CONVERTERS = Map.of( + "string", s -> s, + "int", Integer::parseInt, + "double", Double::parseDouble, + "boolean", Boolean::parseBoolean, + "bigdecimal", BigDecimal::new, + "bigint", BigInteger::new + ); + + public Map processConfig(Properties properties) { + return properties.entrySet().stream() + .filter(entry -> entry.getKey() instanceof String) + .filter(entry -> entry.getValue() != null) + .collect(Collectors.toMap( + entry -> (String) entry.getKey(), + entry -> convertValue((String) entry.getKey(), (String) entry.getValue()) + )); + } + + private Object convertValue(String key, String value) { + String type = determineType(key, value); + return TYPE_CONVERTERS.getOrDefault(type, Function.identity()).apply(value); + } + + private String determineType(String key, String value) { + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return "boolean"; + } else if (value.matches("-?\\d+")) { + return "int"; + } else if (value.matches("-?\\d*\\.\\d+")) { + return "double"; + } else if (key.toLowerCase().contains("amount") || key.toLowerCase().contains("price")) { + return "bigdecimal"; + } + return "string"; + } + + public List validateEmails(List emails) { + return emails.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(email -> !email.isEmpty()) + .filter(email -> email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) + .distinct() + .collect(Collectors.toList()); + } + + public CompletableFuture> processUsersAsync(List users) { + return CompletableFuture.supplyAsync(() -> + users.parallelStream() + .filter(User::active) + .filter(user -> user.createdAt().isAfter(LocalDateTime.now().minusYears(1))) + .sorted(Comparator.comparing(User::username)) + .collect(Collectors.toList()) + ); + } +} + +// Main class for testing +public class Main { + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + public static void main(String[] args) { + try { + // Initialize application context + ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + + // Get service bean + UserService userService = context.getBean(UserService.class); + + // Create test users + List users = Arrays.asList( + User.of("john_doe", "john@example.com"), + User.of("jane_smith", "jane@example.com"), + User.of("bob_wilson", "bob@example.com") + ); + + // Process users + List> futures = users.stream() + .map(user -> { + try { + return CompletableFuture.completedFuture(userService.createUser( + new CreateUserRequest(user.username(), user.email()) + )); + } catch (ValidationException e) { + logger.error("Failed to create user: {}", user.username(), e); + return CompletableFuture.failedFuture(e); + } + }) + .collect(Collectors.toList()); + + // Wait for all to complete + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenRun(() -> { + logger.info("All users created successfully"); + System.out.println("Application started successfully!"); + }) + .exceptionally(throwable -> { + logger.error("Failed to initialize users", throwable); + System.err.println("Application startup failed!"); + return null; + }); + + } catch (Exception e) { + logger.error("Application startup failed", e); + System.exit(1); + } + } +} \ No newline at end of file diff --git a/theme-test.md b/theme-test.md new file mode 100644 index 000000000..3863cc325 --- /dev/null +++ b/theme-test.md @@ -0,0 +1,669 @@ +# TextMate Grammar Token Examples + +This file contains examples of every major TextMate token style for theme testing. + +## Comments + + + +// Single line comment +/_ Multi-line comment _/ + +# Shell comment + +/_ JSDoc comment with @param and @return _/ + +## Strings + +"Double quoted string" +'Single quoted string' +`Backtick string` +"String with \"escaped\" quotes" +'String with \'escaped\' quotes' +`String with \`escaped\` backticks` + +## Template Literals + +`Simple template literal` +`Template with ${variable} interpolation` +`Template with ${function.call()} expression` +Multi-line template with ${nested.interpolation} + +## Numbers + +42 +-17 +3.14159 +-0.001 +1e10 +-2.5e-8 +0xFF +0o755 +0b1010 + +## Keywords + +if else elif for while do switch case default +function class extends implements import export +return break continue throw try catch finally +var let const static async await yield +new this super null undefined true false + +## Storage Types + +int float double string boolean char void +static final abstract private public protected +readonly volatile transient synchronized + +## Constants + +MAX_VALUE +DEFAULT_TIMEOUT +API_ENDPOINT +PI +E + +## Variables + +variableName +\_privateVariable +$specialVariable +camelCase +snake_case +PascalCase +kebab-case + +## Functions + +functionName() +method.call() +object.property() +array[index] +arrowFunction => expression + +## Operators + +- - - / % ++ -- + == === != !== > < >= <= + && || ! & | ^ ~ << >> >>> + = += -= \*= /= %= &= |= ^= <<= >>= >>>= + +## Punctuation + +, ; : . ... ( ) [ ] { } < > / \\ + +# @ $ % ^ & \* - \_ + = | ~ ` ? + +## Entities + +ClassName +InterfaceName +EnumName +TypeName +MethodName +PropertyName + +## Tags + +
    + +

    + +description + +## Attributes + +class="container" +id="main" +data-value="123" +disabled +required +readonly + +## CSS Selectors & Properties + +.container +#header +.button:hover +input[type="text"] +::before +::after + +color: #ffffff; +background: linear-gradient(45deg, #ff0000, #00ff00); +font-size: 16px; +margin: 0 auto; +padding: 10px 20px; + +## Regular Expressions + +/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ +/\d{3}-\d{3}-\d{4}/g +/(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})/ + +## URLs & Paths + +https://example.com/path/to/resource +file:///Users/username/project +./relative/path +../parent/directory +/home/user/documents + +## JSON + +{ +"name": "example", +"version": "1.0.0", +"dependencies": { +"react": "^18.0.0", +"typescript": "^4.9.0" +}, +"scripts": { +"start": "node index.js", +"test": "jest" +} +} + +## XML/HTML + + + + + + + Document + + +

    + + + +## SQL + +SELECT u.id, u.name, u.email, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.active = true +AND o.created_at >= '2023-01-01' +GROUP BY u.id, u.name, u.email +HAVING COUNT(o.id) > 5 +ORDER BY order_count DESC +LIMIT 10; + +## GraphQL + +query GetUserProfile($userId: ID!, $includePosts: Boolean!) { +user(id: $userId) { +id +name +email +avatar +createdAt +posts @include(if: $includePosts) { +id +title +content +publishedAt +comments(first: 10) { +edges { +node { +id +author +content +createdAt +} +} +} +} +} +} + +## Shell/Bash + +#!/bin/bash + +# Variables + +PROJECT*DIR="/home/user/projects" +BACKUP_DIR="$PROJECT_DIR/backups" +TIMESTAMP=$(date +"%Y%m%d*%H%M%S") + +# Functions + +create*backup() { +local source_dir=$1 + local backup_file="$BACKUP_DIR/backup*$TIMESTAMP.tar.gz" + + echo "Creating backup of $source_dir..." + tar -czf "$backup_file" "$source_dir" + echo "Backup created: $backup_file" + +} + +# Conditional logic + +if [ -d "$PROJECT_DIR" ]; then +create_backup "$PROJECT_DIR" +else +echo "Project directory not found: $PROJECT_DIR" +exit 1 +fi + +## Python + +import os +import sys +from typing import List, Dict, Optional +import requests +from dataclasses import dataclass + +@dataclass +class User: +id: int +name: str +email: Optional[str] = None +active: bool = True + + def __post_init__(self): + if not self.name.strip(): + raise ValueError("Name cannot be empty") + +class UserService: +def **init**(self, api_url: str): +self.api_url = api_url +self.session = requests.Session() + + async def get_user(self, user_id: int) -> Optional[User]: + """Fetch user data from API.""" + try: + response = await self.session.get(f"{self.api_url}/users/{user_id}") + response.raise_for_status() + data = response.json() + return User(**data) + except requests.RequestException as e: + print(f"Error fetching user {user_id}: {e}") + return None + +## Rust + +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, Read}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct User { +pub id: u64, +pub name: String, +pub email: Option, #[serde(default)] +pub active: bool, +pub created_at: chrono::DateTime, +} + +impl User { +pub fn new(id: u64, name: String, email: Option) -> Self { +Self { +id, +name, +email, +active: true, +created_at: chrono::Utc::now(), +} +} + + pub fn display_name(&self) -> String { + match &self.email { + Some(email) => format!("{} <{}>", self.name, email), + None => self.name.clone(), + } + } + +} + +pub struct UserService { +api_url: String, +client: reqwest::Client, +} + +impl UserService { +pub fn new(api_url: String) -> Self { +Self { +api_url, +client: reqwest::Client::new(), +} +} + + pub async fn get_user(&self, user_id: u64) -> Result> { + let url = format!("{}/users/{}", self.api_url, user_id); + let response = self.client.get(&url).send().await?; + let user: User = response.json().await?; + Ok(user) + } + +} + +## Go + +package main + +import ( +"context" +"encoding/json" +"fmt" +"log" +"net/http" +"time" +"github.com/gorilla/mux" +) + +type User struct { +ID int64 `json:"id"` +Name string `json:"name"` +Email \*string `json:"email,omitempty"` +Active bool `json:"active"` +CreatedAt time.Time `json:"created_at"` +} + +type UserService struct { +re UserRepository +} + +func NewUserService(repo UserRepository) \*UserService { +return &UserService{repo: repo} +} + +func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) { +user, err := s.repo.FindByID(ctx, id) +if err != nil { +return nil, fmt.Errorf("failed to get user %d: %w", id, err) +} +return user, nil +} + +func (s *UserService) CreateUser(ctx context.Context, req *CreateUserRequest) (\*User, error) { +user := &User{ +Name: req.Name, +Email: req.Email, +Active: true, +CreatedAt: time.Now(), +} +if err := s.repo.Create(ctx, user); err != nil { +return nil, fmt.Errorf("failed to create user: %w", err) +} +return user, nil +} + +## YAML + +apiVersion: apps/v1 +kind: Deployment +metadata: +name: web-app +namespace: production +labels: +app: web-app +version: v1.2.3 +spec: +replicas: 3 +selector: +matchLabels: +app: web-app +template: +metadata: +labels: +app: web-app +tier: frontend +spec: +containers: - name: web-app +image: nginx:1.21-alpine +ports: - containerPort: 80 +protocol: TCP +env: - name: NODE_ENV +value: "production" - name: API_URL +valueFrom: +secretKeyRef: +name: app-secrets +key: api-url +resources: +requests: +memory: "64Mi" +cpu: "250m" +limits: +memory: "128Mi" +cpu: "500m" +livenessProbe: +httpGet: +path: /health +port: 80 +initialDelaySeconds: 30 +periodSeconds: 10 +readinessProbe: +httpGet: +path: /ready +port: 80 +initialDelaySeconds: 5 +periodSeconds: 5 + +## TOML + +[project] +name = "example-app" +version = "1.0.0" +description = "An example application" +authors = ["John Doe "] +license = "MIT" +readme = "README.md" +homepage = "https://example.com" +repository = "https://github.com/johndoe/example-app" +keywords = ["web", "api", "rust"] +categories = ["web-programming"] +edition = "2021" + +[dependencies] +tokio = { version = "1.0", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = { version = "0.11", features = ["json"] } +chrono = { version = "0.4", features = ["serde"] } +log = "0.4" +env_logger = "0.10" + +[dev-dependencies] +tokio-test = "0.4" +mockito = "1.0" + +[[bin]] +name = "server" +path = "src/main.rs" + +[[bin]] +name = "client" +path = "src/client.rs" + +## Dockerfile + +FROM node:18-alpine AS base + +# Install dependencies only when needed + +FROM base AS deps + +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. + +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager + +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Rebuild the source code only when needed + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. + +# Learn more here: https://nextjs.org/telemetry + +# Uncomment the following line in case you want to disable telemetry during the build. + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next + +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV production + +# Uncomment the following line in case you want to disable telemetry during runtime. + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache + +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size + +# https://nextjs.org/docs/advanced-features/output-file-tracing + +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + +# set hostname to localhost + +ENV HOSTNAME "0.0.0.0" + +# server.js is created by next build from the standalone output + +# https://nextjs.org/docs/pages/api-reference/next-config-js/output + +CMD ["node", "server.js"] + +## Makefile + +.PHONY: help build test clean install dev lint format + +# Default target + +.DEFAULT_GOAL := help + +# Variables + +APP_NAME := myapp +VERSION := $(shell git describe --tags --always --dirty) +BUILD_DIR := ./build +DIST_DIR := ./dist +GO_FILES := $(shell find . -name '\*.go' -type f) + +help: ## Show this help message +@echo "Available targets:" +@grep -E '^[a-zA-Z_-]+:._?## ._$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.\*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' + +install: ## Install dependencies +go mod download +npm install + +build: ## Build the application +@echo "Building $(APP_NAME) version $(VERSION)..." + mkdir -p $(BUILD_DIR) + go build -ldflags "-X main.version=$(VERSION)" -o $(BUILD_DIR)/$(APP_NAME) ./cmd/main.go + +test: ## Run tests +go test -v ./... +npm test + +lint: ## Run linters +golangci-lint run +npx eslint . + +format: ## Format code +go fmt ./... +npx prettier --write . + +dev: ## Run in development mode +go run ./cmd/main.go --dev + +clean: ## Clean build artifacts +rm -rf $(BUILD_DIR) +rm -rf $(DIST_DIR) +go clean -cache + +docker-build: ## Build Docker image +docker build -t $(APP_NAME):$(VERSION) . +docker tag $(APP_NAME):$(VERSION) $(APP_NAME):latest + +docker-run: ## Run Docker container +docker run -p 8080:8080 $(APP_NAME):latest + +release: ## Create a new release +@echo "Creating release $(VERSION)" +git tag -a $(VERSION) -m "Release $(VERSION)" +git push origin $(VERSION) +goreleaser release --rm-dist + +## Git Diff + +diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx +index 1234567..abcdefg 100644 +--- a/src/components/UserProfile.tsx ++++ b/src/components/UserProfile.tsx +@@ -10,7 +10,7 @@ interface User { +id: number +name: string +email?: string + +- createdAt: Date + +* readonly createdAt: Date + active: boolean + } + +@@ -25,8 +25,12 @@ const UserProfile: FC<{ user: User }> = ({ user }) => { +const [isEditing, setIsEditing] = useState(false) +const [formData, setFormData] = useState(user) + +- const handleSubmit = async (e: React.FormEvent) => { +- e.preventDefault() +- // Handle form submission +- } +- return ( + +*
    + +-
    +

    {user.name}

    +

    {user.email}

    +
    diff --git a/theme-test.tsx b/theme-test.tsx index 16559bf70..61837473e 100644 --- a/theme-test.tsx +++ b/theme-test.tsx @@ -29,6 +29,8 @@ class Repository { } public find(id: number): T | undefined { + const x = undefined + type x = { foo: undefined } return this.items.find((item) => item.id === id) } @@ -66,6 +68,72 @@ const sql = ` AND created_at > '${new Date().toISOString()}' ` +// String source examples (CSS-in-JS, GraphQL, etc.) +const styledComponent = css` + .container { + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15); + } + + .title { + font-size: 1.5rem; + font-weight: bold; + color: white; + margin-bottom: 1rem; + } + } +` + +const graphqlQuery = ` + query GetUserProfile($userId: ID!) { + user(id: $userId) { + id + name + email + avatar + createdAt + posts { + id + title + content + publishedAt + comments { + id + author + content + createdAt + } + } + } + } +` + +const htmlTemplate = ` +
    + ${user.name} + +
    + + +
    +
    +` + // Arrow functions const debounce = any>( func: T, From 89922a8598bf344ed24fb1b472307f383ef2b18a Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:56:07 -0600 Subject: [PATCH 118/218] fix(desktop): prompt input missing on new session --- packages/desktop/src/pages/session.tsx | 738 ++++++++++++------------- 1 file changed, 366 insertions(+), 372 deletions(-) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 3dcc24e61..b872c1434 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -330,142 +330,39 @@ export default function Page() {
    - -
    New session
    -
    - -
    - {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
    -
    -
    - -
    - Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
    -
    -
    - } +
    - {(_) => { - return ( -
    -
    -
    - - - -
    -
    - 1}> -
      - - {(message) => { - const assistantMessages = createMemo(() => { - if (!session.id) return [] - return sync.data.message[session.id]?.filter( - (m) => m.role === "assistant" && m.parentID == message.id, - ) as AssistantMessageType[] - }) - const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const working = createMemo(() => !message.summary?.body && !error()) - - const handleClick = () => session.messages.setActive(message.id) - - return ( -
    • - - - {message.summary?.title} -
    - } - > - - - -
  • - ) - }} - - - -
    +
    + + +
    + + + +
    +
    + 1}> +
      {(message) => { - const isActive = createMemo(() => session.messages.active()?.id === message.id) - const [titled, setTitled] = createSignal(!!message.summary?.title) const assistantMessages = createMemo(() => { if (!session.id) return [] return sync.data.message[session.id]?.filter( @@ -473,261 +370,358 @@ export default function Page() { ) as AssistantMessageType[] }) const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) - const [detailsExpanded, setDetailsExpanded] = createSignal(false) - const parts = createMemo(() => sync.data.part[message.id]) - const hasToolPart = createMemo(() => - assistantMessages() - ?.flatMap((m) => sync.data.part[m.id]) - .some((p) => p?.type === "tool"), - ) const working = createMemo(() => !message.summary?.body && !error()) - // allowing time for the animations to finish - createEffect(() => { - const title = message.summary?.title - setTimeout(() => setTitled(!!title), 10_000) - }) - createEffect(() => { - const summary = message.summary?.body - const complete = !!summary || !!error() - setTimeout(() => setCompleted(complete), 1200) - }) + const handleClick = () => session.messages.setActive(message.id) return ( - -
      + + + {message.summary?.title} +
      + } > - {/* Title */} -
      -
      - - } - > -

      - {message.summary?.title} -

      -
      -
      + + +
      -
      + + ) }}
      -
    + + +
    + + {(message) => { + const isActive = createMemo(() => session.messages.active()?.id === message.id) + const [titled, setTitled] = createSignal(!!message.summary?.title) + const assistantMessages = createMemo(() => { + if (!session.id) return [] + return sync.data.message[session.id]?.filter( + (m) => m.role === "assistant" && m.parentID == message.id, + ) as AssistantMessageType[] + }) + const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) + const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) + const [detailsExpanded, setDetailsExpanded] = createSignal(false) + const parts = createMemo(() => sync.data.part[message.id]) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => sync.data.part[m.id]) + .some((p) => p?.type === "tool"), + ) + const working = createMemo(() => !message.summary?.body && !error()) -
    - { - inputRef = el - }} - /> + // allowing time for the animations to finish + createEffect(() => { + const title = message.summary?.title + setTimeout(() => setTitled(!!title), 10_000) + }) + createEffect(() => { + const summary = message.summary?.body + const complete = !!summary || !!error() + setTimeout(() => setCompleted(complete), 1200) + }) + + return ( + +
    + {/* Title */} +
    +
    + + } + > +

    + {message.summary?.title} +

    +
    +
    +
    +
    + +
    + {/* Summary */} + +
    +
    +

    + + Summary + Response + +

    + + {(summary) => ( + *]:fade-up-text": !message.summary?.diffs?.length, + }} + text={summary()} + /> + )} + +
    + + + {(diff) => ( + + + +
    +
    + +
    + + + {getDirectory(diff.file)}‎ + + + + {getFilename(diff.file)} + +
    +
    +
    + + +
    +
    +
    +
    + + + +
    + )} +
    +
    +
    +
    + + + {error()?.data?.message as string} + + + {/* Response */} +
    + + + + + + + +
    +
    + + Hide details + Show details + +
    + +
    +
    + +
    + + {(assistantMessage) => { + const parts = createMemo(() => sync.data.part[assistantMessage.id]) + return + }} + + + + {error()?.data?.message as string} + + +
    +
    +
    +
    +
    +
    +
    +
    + ) + }} + +
    +
    +
    + +
    +
    New session
    +
    + +
    + {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)} +
    +
    +
    + +
    + Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} +
    - -
    -
    -
    - - - - - { - local.layout.review.tab() - session.layout.setActiveTab("review") + + +
    + { + inputRef = el + }} + /> +
    +
    + +
    +
    +
    + + + + + { + local.layout.review.tab() + session.layout.setActiveTab("review") + }} + /> + +
    +
    +
    All changes
    +
    + + + {(diff) => ( + + + +
    +
    + +
    + + + {getDirectory(diff.file)}‎ + + + {getFilename(diff.file)} +
    +
    +
    + + +
    +
    +
    +
    + + - -
    -
    -
    All changes
    -
    - - - {(diff) => ( - - - -
    -
    - -
    - - - {getDirectory(diff.file)}‎ - - - {getFilename(diff.file)} -
    -
    -
    - - -
    -
    -
    -
    - - - -
    - )} -
    -
    -
    -
    - + + + )} + +
    - ) - }} -
    +
    + +
    From 7adbc3ad443af942b400923b66119436589c676c Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:14:57 -0600 Subject: [PATCH 119/218] fix(desktop): code tab padding --- packages/desktop/src/pages/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index b872c1434..1c92941d0 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -791,14 +791,14 @@ export default function Page() { }, ) return ( - + {(f) => ( )} From 7f51b181d4b77326c5e7f6961552036ae2d56234 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:30:07 -0600 Subject: [PATCH 120/218] chore(desktop): cleanup shiki theme stuff --- packages/desktop/src/components/theme.json | 558 --------------- packages/desktop/src/index.tsx | 36 +- packages/ui/src/components/code.tsx | 3 +- packages/ui/src/components/diff.tsx | 763 ++++++++++----------- packages/ui/src/components/index.ts | 1 - packages/ui/src/context/marked.tsx | 8 +- packages/ui/src/context/shiki.tsx | 577 ---------------- 7 files changed, 394 insertions(+), 1552 deletions(-) delete mode 100644 packages/desktop/src/components/theme.json delete mode 100644 packages/ui/src/context/shiki.tsx diff --git a/packages/desktop/src/components/theme.json b/packages/desktop/src/components/theme.json deleted file mode 100644 index 36a4d020d..000000000 --- a/packages/desktop/src/components/theme.json +++ /dev/null @@ -1,558 +0,0 @@ -{ - "colors": { - "actionBar.toggledBackground": "var(--surface-raised-base)", - "activityBarBadge.background": "var(--surface-brand-base)", - "checkbox.border": "var(--border-base)", - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "editor.inactiveSelectionBackground": "var(--surface-raised-base)", - "editor.selectionHighlightBackground": "var(--border-active)", - "editorIndentGuide.activeBackground1": "var(--border-weak-base)", - "editorIndentGuide.background1": "var(--border-weak-base)", - "input.placeholderForeground": "var(--text-weak)", - "list.activeSelectionIconForeground": "var(--text-base)", - "list.dropBackground": "var(--surface-raised-base)", - "menu.background": "var(--surface-base)", - "menu.border": "var(--border-base)", - "menu.foreground": "var(--text-base)", - "menu.selectionBackground": "var(--surface-interactive-base)", - "menu.separatorBackground": "var(--border-base)", - "ports.iconRunningProcessForeground": "var(--icon-success-base)", - "sideBarSectionHeader.background": "transparent", - "sideBarSectionHeader.border": "var(--border-weak-base)", - "sideBarTitle.foreground": "var(--text-weak)", - "statusBarItem.remoteBackground": "var(--surface-success-base)", - "statusBarItem.remoteForeground": "var(--text-base)", - "tab.lastPinnedBorder": "var(--border-weak-base)", - "tab.selectedBackground": "var(--surface-raised-base)", - "tab.selectedForeground": "var(--text-weak)", - "terminal.inactiveSelectionBackground": "var(--surface-raised-base)", - "widget.border": "var(--border-base)" - }, - "displayName": "opencode", - "name": "opencode", - "semanticHighlighting": true, - "semanticTokenColors": { - "customLiteral": "var(--syntax-function)", - "newOperator": "var(--syntax-operator)", - "numberLiteral": "var(--syntax-number)", - "stringLiteral": "var(--syntax-string)" - }, - "tokenColors": [ - { - "scope": [ - "meta.embedded", - "source.groovy.embedded", - "string meta.image.inline.markdown", - "variable.legacy.builtin.python" - ], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "emphasis", - "settings": { - "fontStyle": "italic" - } - }, - { - "scope": "strong", - "settings": { - "fontStyle": "bold" - } - }, - { - "scope": "header", - "settings": { - "foreground": "var(--markdown-heading)" - } - }, - { - "scope": "comment", - "settings": { - "foreground": "var(--syntax-comment)" - } - }, - { - "scope": "constant.language", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": [ - "constant.numeric", - "variable.other.enummember", - "keyword.operator.plus.exponent", - "keyword.operator.minus.exponent" - ], - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": "constant.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.name.tag", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["entity.name.tag.css", "entity.name.tag.less"], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.other.attribute-name", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": [ - "entity.other.attribute-name.class.css", - "source.css entity.other.attribute-name.class", - "entity.other.attribute-name.id.css", - "entity.other.attribute-name.parent-selector.css", - "entity.other.attribute-name.parent.less", - "source.css entity.other.attribute-name.pseudo-class", - "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.scss" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "invalid", - "settings": { - "foreground": "var(--syntax-critical)" - } - }, - { - "scope": "markup.underline", - "settings": { - "fontStyle": "underline" - } - }, - { - "scope": "markup.bold", - "settings": { - "fontStyle": "bold", - "foreground": "var(--markdown-strong)" - } - }, - { - "scope": "markup.heading", - "settings": { - "fontStyle": "bold", - "foreground": "var(--theme-markdown-heading)" - } - }, - { - "scope": "markup.italic", - "settings": { - "fontStyle": "italic" - } - }, - { - "scope": "markup.strikethrough", - "settings": { - "fontStyle": "strikethrough" - } - }, - { - "scope": "markup.inserted", - "settings": { - "foreground": "var(--text-diff-add-base)" - } - }, - { - "scope": "markup.deleted", - "settings": { - "foreground": "var(--text-diff-delete-base)" - } - }, - { - "scope": "markup.changed", - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "punctuation.definition.quote.begin.markdown", - "settings": { - "foreground": "var(--markdown-block-quote)" - } - }, - { - "scope": "punctuation.definition.list.begin.markdown", - "settings": { - "foreground": "var(--markdown-list-enumeration)" - } - }, - { - "scope": "markup.inline.raw", - "settings": { - "foreground": "var(--markdown-code)" - } - }, - { - "scope": "punctuation.definition.tag", - "settings": { - "foreground": "var(--syntax-punctuation)" - } - }, - { - "scope": ["meta.preprocessor", "entity.name.function.preprocessor"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "meta.preprocessor.string", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "meta.preprocessor.numeric", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": "meta.structure.dictionary.key.python", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "meta.diff.header", - "settings": { - "foreground": "var(--text-weak)" - } - }, - { - "scope": "storage", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "storage.type", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["storage.modifier", "keyword.operator.noexcept"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["string", "meta.embedded.assembly"], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.tag", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.value", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - "punctuation.section.embedded" - ], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["meta.template.expression"], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": [ - "support.type.vendored.property-name", - "support.type.property-name", - "source.css variable", - "source.coffee.embedded" - ], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "keyword", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.control", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.operator", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "keyword.operator.new", - "keyword.operator.expression", - "keyword.operator.cast", - "keyword.operator.sizeof", - "keyword.operator.alignof", - "keyword.operator.typeid", - "keyword.operator.alignas", - "keyword.operator.instanceof", - "keyword.operator.logical.python", - "keyword.operator.wordlike" - ], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.other.unit", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "support.function.git-rebase", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "constant.sha.git-rebase", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "variable.language", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": [ - "entity.name.function", - "support.function", - "support.constant.handlebars", - "source.powershell variable.other.member", - "entity.name.operator.custom-literal" - ], - "settings": { - "foreground": "var(--syntax-function)" - } - }, - { - "scope": [ - "support.class", - "support.type", - "entity.name.type", - "entity.name.namespace", - "entity.other.attribute", - "entity.name.scope-resolution", - "entity.name.class", - "storage.type.numeric.go", - "storage.type.byte.go", - "storage.type.boolean.go", - "storage.type.string.go", - "storage.type.uintptr.go", - "storage.type.error.go", - "storage.type.rune.go", - "storage.type.cs", - "storage.type.generic.cs", - "storage.type.modifier.cs", - "storage.type.variable.cs", - "storage.type.annotation.java", - "storage.type.generic.java", - "storage.type.java", - "storage.type.object.array.java", - "storage.type.primitive.array.java", - "storage.type.primitive.java", - "storage.type.token.java", - "storage.type.groovy", - "storage.type.annotation.groovy", - "storage.type.parameters.groovy", - "storage.type.generic.groovy", - "storage.type.object.array.groovy", - "storage.type.primitive.array.groovy", - "storage.type.primitive.groovy" - ], - "settings": { - "foreground": "var(--syntax-type)" - } - }, - { - "scope": [ - "meta.type.cast.expr", - "meta.type.new.expr", - "support.constant.math", - "support.constant.dom", - "support.constant.json", - "entity.other.inherited-class", - "punctuation.separator.namespace.ruby" - ], - "settings": { - "foreground": "var(--syntax-type)" - } - }, - { - "scope": [ - "keyword.control", - "source.cpp keyword.operator.new", - "keyword.operator.delete", - "keyword.other.using", - "keyword.other.directive.using", - "keyword.other.operator", - "entity.name.operator" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "variable", - "meta.definition.variable.name", - "support.variable", - "entity.name.variable", - "constant.other.placeholder" - ], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": ["variable.other.constant", "variable.other.enummember"], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": ["meta.object-literal.key"], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": [ - "support.constant.property-value", - "support.constant.font-name", - "support.constant.media-type", - "support.constant.media", - "constant.other.color.rgb-value", - "constant.other.rgb-value", - "support.constant.color" - ], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": [ - "punctuation.definition.group.regexp", - "punctuation.definition.group.assertion.regexp", - "punctuation.definition.character-class.regexp", - "punctuation.character.set.begin.regexp", - "punctuation.character.set.end.regexp", - "keyword.operator.negation.regexp", - "support.other.parenthesis.regexp" - ], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": [ - "constant.character.character-class.regexp", - "constant.other.character-class.set.regexp", - "constant.other.character-class.regexp", - "constant.character.set.regexp" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "keyword.operator.quantifier.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": ["constant.character", "constant.other.option"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "constant.character.escape", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.name.label", - "settings": { - "foreground": "var(--text-weak)" - } - } - ], - "type": "dark" -} diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 9d402138d..63d96ae84 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -3,7 +3,7 @@ import "@/index.css" import { render } from "solid-js/web" import { Router, Route } from "@solidjs/router" import { MetaProvider } from "@solidjs/meta" -import { Fonts, ShikiProvider, MarkedProvider } from "@opencode-ai/ui" +import { Fonts, MarkedProvider } from "@opencode-ai/ui" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" import { LocalProvider } from "./context/local" @@ -29,24 +29,22 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { render( () => ( - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + ), root!, ) diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx index 6b95e6653..06541fe4e 100644 --- a/packages/ui/src/components/code.tsx +++ b/packages/ui/src/components/code.tsx @@ -14,8 +14,7 @@ export function Code(props: CodeProps) { createEffect(() => { const instance = new File({ - theme: { dark: "oc-1-dark", light: "oc-1-light" }, - // theme: { dark: "pierre-dark", light: "pierre-light" }, + theme: "OpenCode", overflow: "wrap", // or 'scroll' themeType: "system", // 'system', 'light', or 'dark' disableFileHeader: true, diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index 09085b44c..21ff980c1 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -55,9 +55,7 @@ export function Diff(props: DiffProps) { // annotations and a container element to hold the diff createEffect(() => { const instance = new FileDiff({ - // theme: "pierre-light", - theme: { dark: "oc-1-dark", light: "oc-1-light" }, - // theme: { dark: "pierre-dark", light: "pierre-light" }, + theme: "OpenCode", // When using the 'themes' prop, 'themeType' allows you to force 'dark' // or 'light' theme, or inherit from the OS ('system') theme. themeType: "system", @@ -181,394 +179,377 @@ export function Diff(props: DiffProps) { ) } -const colors = { - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", - "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", - // "gitDecoration.conflictingResourceForeground": "#ffca00", - // "gitDecoration.modifiedResourceForeground": "#1a76d4", - // "gitDecoration.untrackedResourceForeground": "#00cab1", - // "gitDecoration.ignoredResourceForeground": "#84848A", - // "terminal.titleForeground": "#adadb1", - // "terminal.titleInactiveForeground": "#84848A", - // "terminal.background": "#141415", - // "terminal.foreground": "#adadb1", - // "terminal.ansiBlack": "#141415", - // "terminal.ansiRed": "#ff2e3f", - // "terminal.ansiGreen": "#0dbe4e", - // "terminal.ansiYellow": "#ffca00", - // "terminal.ansiBlue": "#008cff", - // "terminal.ansiMagenta": "#c635e4", - // "terminal.ansiCyan": "#08c0ef", - // "terminal.ansiWhite": "#c6c6c8", - // "terminal.ansiBrightBlack": "#141415", - // "terminal.ansiBrightRed": "#ff2e3f", - // "terminal.ansiBrightGreen": "#0dbe4e", - // "terminal.ansiBrightYellow": "#ffca00", - // "terminal.ansiBrightBlue": "#008cff", - // "terminal.ansiBrightMagenta": "#c635e4", - // "terminal.ansiBrightCyan": "#08c0ef", - // "terminal.ansiBrightWhite": "#c6c6c8", -} - -const tokenColors = [ - { - scope: ["comment", "punctuation.definition.comment", "string.comment"], - settings: { - foreground: "var(--syntax-comment)", - }, - }, - { - scope: ["entity.other.attribute-name"], - settings: { - foreground: "var(--syntax-property)", // maybe attribute - }, - }, - { - scope: [ - "constant", - "entity.name.constant", - "variable.other.constant", - "variable.language", - "entity", - ], - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: ["entity.name", "meta.export.default", "meta.definition.variable"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "variable.parameter.function", - "meta.jsx.children", - "meta.block", - "meta.tag.attributes", - "entity.name.constant", - "meta.object.member", - "meta.embedded.expression", - "meta.template.expression", - "string.other.begin.yaml", - "string.other.end.yaml", - ], - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["entity.name.function", "support.type.primitive"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.class.component"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "keyword.operator", - "storage.type.function.arrow", - "punctuation.separator.key-value.css", - "entity.name.tag.yaml", - "punctuation.separator.key-value.mapping.yaml", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["storage", "storage.type"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "string", - "punctuation.definition.string", - "string punctuation.section.embedded source", - "entity.name.tag", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "support", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "support.type.object.module", - "variable.other.object", - "support.type.property-name.css", - ], - settings: { - foreground: "var(--syntax-object)", - }, - }, - { - scope: "meta.property-name", - settings: { - foreground: "var(--syntax-property)", - }, - }, - { - scope: "variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "variable.other", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "invalid.broken", - "invalid.illegal", - "invalid.unimplemented", - "invalid.deprecated", - "message.error", - "markup.deleted", - "meta.diff.header.from-file", - "punctuation.definition.deleted", - "brackethighlighter.unmatched", - "token.error-token", - ], - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "carriage-return", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "string source", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "string variable", - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: [ - "source.regexp", - "string.regexp", - "string.regexp.character-class", - "string.regexp constant.character.escape", - "string.regexp source.ruby.embedded", - "string.regexp string.regexp.arbitrary-repitition", - "string.regexp constant.character.escape", - ], - settings: { - foreground: "var(--syntax-regexp)", - }, - }, - { - scope: "support.constant", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: "support.variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.module-reference", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["markup.heading", "markup.heading entity.name"], - settings: { - fontStyle: "bold", - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.quote", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - // foreground: "", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--text-strong)", - }, - }, - { - scope: [ - "markup.raw", - "markup.inserted", - "meta.diff.header.to-file", - "punctuation.definition.inserted", - "markup.changed", - "punctuation.definition.changed", - "markup.ignored", - "markup.untracked", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "meta.diff.range", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.separator", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.output", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.export.default", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: [ - "brackethighlighter.tag", - "brackethighlighter.curly", - "brackethighlighter.round", - "brackethighlighter.square", - "brackethighlighter.angle", - "brackethighlighter.quote", - ], - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: ["constant.other.reference.link", "string.other.link"], - settings: { - fontStyle: "underline", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "var(--syntax-warning)", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, -] - -const semanticTokenColors = { - comment: "var(--syntax-comment)", - string: "var(--syntax-string)", - number: "var(--syntax-constant)", - regexp: "var(--syntax-regexp)", - keyword: "var(--syntax-keyword)", - variable: "var(--syntax-variable)", - parameter: "var(--syntax-variable)", - property: "var(--syntax-property)", - function: "var(--syntax-primitive)", - method: "var(--syntax-primitive)", - type: "var(--syntax-type)", - class: "var(--syntax-type)", - namespace: "var(--syntax-type)", - enumMember: "var(--syntax-primitive)", - "variable.constant": "var(--syntax-constant)", - "variable.defaultLibrary": "var(--syntax-unknown)", -} - -registerCustomTheme("oc-1-light", () => { +registerCustomTheme("OpenCode", () => { return Promise.resolve({ - type: "light", - name: "oc-1-light", - colors, - tokenColors, - semanticTokenColors, - } as unknown as ThemeRegistrationResolved) -}) - -registerCustomTheme("oc-1-dark", () => { - return Promise.resolve({ - name: "oc-1-dark", - type: "dark", - colors, - tokenColors, - semanticTokenColors, + name: "OpenCode", + colors: { + "editor.background": "transparent", + "editor.foreground": "var(--text-base)", + "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", + "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", + // "gitDecoration.conflictingResourceForeground": "#ffca00", + // "gitDecoration.modifiedResourceForeground": "#1a76d4", + // "gitDecoration.untrackedResourceForeground": "#00cab1", + // "gitDecoration.ignoredResourceForeground": "#84848A", + // "terminal.titleForeground": "#adadb1", + // "terminal.titleInactiveForeground": "#84848A", + // "terminal.background": "#141415", + // "terminal.foreground": "#adadb1", + // "terminal.ansiBlack": "#141415", + // "terminal.ansiRed": "#ff2e3f", + // "terminal.ansiGreen": "#0dbe4e", + // "terminal.ansiYellow": "#ffca00", + // "terminal.ansiBlue": "#008cff", + // "terminal.ansiMagenta": "#c635e4", + // "terminal.ansiCyan": "#08c0ef", + // "terminal.ansiWhite": "#c6c6c8", + // "terminal.ansiBrightBlack": "#141415", + // "terminal.ansiBrightRed": "#ff2e3f", + // "terminal.ansiBrightGreen": "#0dbe4e", + // "terminal.ansiBrightYellow": "#ffca00", + // "terminal.ansiBrightBlue": "#008cff", + // "terminal.ansiBrightMagenta": "#c635e4", + // "terminal.ansiBrightCyan": "#08c0ef", + // "terminal.ansiBrightWhite": "#c6c6c8", + }, + tokenColors: [ + { + scope: ["comment", "punctuation.definition.comment", "string.comment"], + settings: { + foreground: "var(--syntax-comment)", + }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { + foreground: "var(--syntax-property)", // maybe attribute + }, + }, + { + scope: [ + "constant", + "entity.name.constant", + "variable.other.constant", + "variable.language", + "entity", + ], + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: ["entity.name", "meta.export.default", "meta.definition.variable"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: [ + "variable.parameter.function", + "meta.jsx.children", + "meta.block", + "meta.tag.attributes", + "entity.name.constant", + "meta.object.member", + "meta.embedded.expression", + "meta.template.expression", + "string.other.begin.yaml", + "string.other.end.yaml", + ], + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["entity.name.function", "support.type.primitive"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.class.component"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: "keyword", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: [ + "keyword.operator", + "storage.type.function.arrow", + "punctuation.separator.key-value.css", + "entity.name.tag.yaml", + "punctuation.separator.key-value.mapping.yaml", + ], + settings: { + foreground: "var(--syntax-operator)", + }, + }, + { + scope: ["storage", "storage.type"], + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "string", + "punctuation.definition.string", + "string punctuation.section.embedded source", + "entity.name.tag", + ], + settings: { + foreground: "var(--syntax-string)", + }, + }, + { + scope: "support", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "support.type.object.module", + "variable.other.object", + "support.type.property-name.css", + ], + settings: { + foreground: "var(--syntax-object)", + }, + }, + { + scope: "meta.property-name", + settings: { + foreground: "var(--syntax-property)", + }, + }, + { + scope: "variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "variable.other", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: [ + "invalid.broken", + "invalid.illegal", + "invalid.unimplemented", + "invalid.deprecated", + "message.error", + "markup.deleted", + "meta.diff.header.from-file", + "punctuation.definition.deleted", + "brackethighlighter.unmatched", + "token.error-token", + ], + settings: { + foreground: "var(--syntax-critical)", + }, + }, + { + scope: "carriage-return", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: "string source", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "string variable", + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: [ + "source.regexp", + "string.regexp", + "string.regexp.character-class", + "string.regexp constant.character.escape", + "string.regexp source.ruby.embedded", + "string.regexp string.regexp.arbitrary-repitition", + "string.regexp constant.character.escape", + ], + settings: { + foreground: "var(--syntax-regexp)", + }, + }, + { + scope: "support.constant", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: "support.variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "meta.module-reference", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "punctuation.definition.list.begin.markdown", + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["markup.heading", "markup.heading entity.name"], + settings: { + fontStyle: "bold", + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.quote", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.italic", + settings: { + fontStyle: "italic", + // foreground: "", + }, + }, + { + scope: "markup.bold", + settings: { + fontStyle: "bold", + foreground: "var(--text-strong)", + }, + }, + { + scope: [ + "markup.raw", + "markup.inserted", + "meta.diff.header.to-file", + "punctuation.definition.inserted", + "markup.changed", + "punctuation.definition.changed", + "markup.ignored", + "markup.untracked", + ], + settings: { + foreground: "var(--text-base)", + }, + }, + { + scope: "meta.diff.range", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.diff.header", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.separator", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.output", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.export.default", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: [ + "brackethighlighter.tag", + "brackethighlighter.curly", + "brackethighlighter.round", + "brackethighlighter.square", + "brackethighlighter.angle", + "brackethighlighter.quote", + ], + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: ["constant.other.reference.link", "string.other.link"], + settings: { + fontStyle: "underline", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "token.info-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "token.warn-token", + settings: { + foreground: "var(--syntax-warning)", + }, + }, + { + scope: "token.debug-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + ], + semanticTokenColors: { + comment: "var(--syntax-comment)", + string: "var(--syntax-string)", + number: "var(--syntax-constant)", + regexp: "var(--syntax-regexp)", + keyword: "var(--syntax-keyword)", + variable: "var(--syntax-variable)", + parameter: "var(--syntax-variable)", + property: "var(--syntax-property)", + function: "var(--syntax-primitive)", + method: "var(--syntax-primitive)", + type: "var(--syntax-type)", + class: "var(--syntax-type)", + namespace: "var(--syntax-type)", + enumMember: "var(--syntax-primitive)", + "variable.constant": "var(--syntax-constant)", + "variable.defaultLibrary": "var(--syntax-unknown)", + }, } as unknown as ThemeRegistrationResolved) }) diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index cd2d4caa9..ebc897a1f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -24,5 +24,4 @@ export * from "./tooltip" export * from "./typewriter" export * from "../context/helper" -export * from "../context/shiki" export * from "../context/marked" diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx index 18ce4280a..804d449c5 100644 --- a/packages/ui/src/context/marked.tsx +++ b/packages/ui/src/context/marked.tsx @@ -1,14 +1,14 @@ import { marked } from "marked" import markedShiki from "marked-shiki" import { bundledLanguages, type BundledLanguage } from "shiki" - import { createSimpleContext } from "./helper" -import { useShiki } from "./shiki" +import { getSharedHighlighter } from "@pierre/precision-diffs" + +const highlighter = await getSharedHighlighter({ themes: ["OpenCode"], langs: [] }) export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({ name: "Marked", init: () => { - const highlighter = useShiki() return marked.use( markedShiki({ async highlight(code, lang) { @@ -20,7 +20,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext( } return highlighter.codeToHtml(code, { lang: lang || "text", - theme: "opencode", + theme: "OpenCode", tabindex: false, }) }, diff --git a/packages/ui/src/context/shiki.tsx b/packages/ui/src/context/shiki.tsx deleted file mode 100644 index d33b98ab7..000000000 --- a/packages/ui/src/context/shiki.tsx +++ /dev/null @@ -1,577 +0,0 @@ -import { createSimpleContext } from "./helper" -import { createHighlighter, type ThemeInput } from "shiki" - -const theme: ThemeInput = { - colors: { - "actionBar.toggledBackground": "var(--surface-raised-base)", - "activityBarBadge.background": "var(--surface-brand-base)", - "checkbox.border": "var(--border-base)", - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "editor.inactiveSelectionBackground": "var(--surface-raised-base)", - "editor.selectionHighlightBackground": "var(--border-active)", - "editorIndentGuide.activeBackground1": "var(--border-weak-base)", - "editorIndentGuide.background1": "var(--border-weak-base)", - "input.placeholderForeground": "var(--text-weak)", - "list.activeSelectionIconForeground": "var(--text-base)", - "list.dropBackground": "var(--surface-raised-base)", - "menu.background": "var(--surface-base)", - "menu.border": "var(--border-base)", - "menu.foreground": "var(--text-base)", - "menu.selectionBackground": "var(--surface-interactive-base)", - "menu.separatorBackground": "var(--border-base)", - "ports.iconRunningProcessForeground": "var(--icon-success-base)", - "sideBarSectionHeader.background": "transparent", - "sideBarSectionHeader.border": "var(--border-weak-base)", - "sideBarTitle.foreground": "var(--text-weak)", - "statusBarItem.remoteBackground": "var(--surface-success-base)", - "statusBarItem.remoteForeground": "var(--text-base)", - "tab.lastPinnedBorder": "var(--border-weak-base)", - "tab.selectedBackground": "var(--surface-raised-base)", - "tab.selectedForeground": "var(--text-weak)", - "terminal.inactiveSelectionBackground": "var(--surface-raised-base)", - "widget.border": "var(--border-base)", - }, - displayName: "opencode", - name: "opencode", - semanticHighlighting: true, - semanticTokenColors: { - customLiteral: "var(--syntax-function)", - newOperator: "var(--syntax-operator)", - numberLiteral: "var(--syntax-number)", - stringLiteral: "var(--syntax-string)", - }, - tokenColors: [ - { - scope: [ - "meta.embedded", - "source.groovy.embedded", - "string meta.image.inline.markdown", - "variable.legacy.builtin.python", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "emphasis", - settings: { - fontStyle: "italic", - }, - }, - { - scope: "strong", - settings: { - fontStyle: "bold", - }, - }, - { - scope: "header", - settings: { - foreground: "var(--markdown-heading)", - }, - }, - { - scope: "comment", - settings: { - foreground: "var(--syntax-comment)", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "constant.numeric", - "variable.other.enummember", - "keyword.operator.plus.exponent", - "keyword.operator.minus.exponent", - ], - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: "constant.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["entity.name.tag.css", "entity.name.tag.less"], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "entity.other.attribute-name.class.css", - "source.css entity.other.attribute-name.class", - "entity.other.attribute-name.id.css", - "entity.other.attribute-name.parent-selector.css", - "entity.other.attribute-name.parent.less", - "source.css entity.other.attribute-name.pseudo-class", - "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.scss", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "invalid", - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "markup.underline", - settings: { - fontStyle: "underline", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--markdown-strong)", - }, - }, - { - scope: "markup.heading", - settings: { - fontStyle: "bold", - foreground: "var(--theme-markdown-heading)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - }, - }, - { - scope: "markup.strikethrough", - settings: { - fontStyle: "strikethrough", - }, - }, - { - scope: "markup.inserted", - settings: { - foreground: "var(--text-diff-add-base)", - }, - }, - { - scope: "markup.deleted", - settings: { - foreground: "var(--text-diff-delete-base)", - }, - }, - { - scope: "markup.changed", - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "punctuation.definition.quote.begin.markdown", - settings: { - foreground: "var(--markdown-block-quote)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--markdown-list-enumeration)", - }, - }, - { - scope: "markup.inline.raw", - settings: { - foreground: "var(--markdown-code)", - }, - }, - { - scope: "punctuation.definition.tag", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["meta.preprocessor", "entity.name.function.preprocessor"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "meta.preprocessor.string", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "meta.preprocessor.numeric", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: "meta.structure.dictionary.key.python", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--text-weak)", - }, - }, - { - scope: "storage", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "storage.type", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier", "keyword.operator.noexcept"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["string", "meta.embedded.assembly"], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.tag", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.value", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - "punctuation.section.embedded", - ], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["meta.template.expression"], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: [ - "support.type.vendored.property-name", - "support.type.property-name", - "source.css variable", - "source.coffee.embedded", - ], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression", - "keyword.operator.cast", - "keyword.operator.sizeof", - "keyword.operator.alignof", - "keyword.operator.typeid", - "keyword.operator.alignas", - "keyword.operator.instanceof", - "keyword.operator.logical.python", - "keyword.operator.wordlike", - ], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "support.function.git-rebase", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "constant.sha.git-rebase", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "entity.name.function", - "support.function", - "support.constant.handlebars", - "source.powershell variable.other.member", - "entity.name.operator.custom-literal", - ], - settings: { - foreground: "var(--syntax-function)", - }, - }, - { - scope: [ - "support.class", - "support.type", - "entity.name.type", - "entity.name.namespace", - "entity.other.attribute", - "entity.name.scope-resolution", - "entity.name.class", - "storage.type.numeric.go", - "storage.type.byte.go", - "storage.type.boolean.go", - "storage.type.string.go", - "storage.type.uintptr.go", - "storage.type.error.go", - "storage.type.rune.go", - "storage.type.cs", - "storage.type.generic.cs", - "storage.type.modifier.cs", - "storage.type.variable.cs", - "storage.type.annotation.java", - "storage.type.generic.java", - "storage.type.java", - "storage.type.object.array.java", - "storage.type.primitive.array.java", - "storage.type.primitive.java", - "storage.type.token.java", - "storage.type.groovy", - "storage.type.annotation.groovy", - "storage.type.parameters.groovy", - "storage.type.generic.groovy", - "storage.type.object.array.groovy", - "storage.type.primitive.array.groovy", - "storage.type.primitive.groovy", - ], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "meta.type.cast.expr", - "meta.type.new.expr", - "support.constant.math", - "support.constant.dom", - "support.constant.json", - "entity.other.inherited-class", - "punctuation.separator.namespace.ruby", - ], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "keyword.control", - "source.cpp keyword.operator.new", - "keyword.operator.delete", - "keyword.other.using", - "keyword.other.directive.using", - "keyword.other.operator", - "entity.name.operator", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "variable", - "meta.definition.variable.name", - "support.variable", - "entity.name.variable", - "constant.other.placeholder", - ], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: ["variable.other.constant", "variable.other.enummember"], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: ["meta.object-literal.key"], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "support.constant.property-value", - "support.constant.font-name", - "support.constant.media-type", - "support.constant.media", - "constant.other.color.rgb-value", - "constant.other.rgb-value", - "support.constant.color", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: [ - "punctuation.definition.group.regexp", - "punctuation.definition.group.assertion.regexp", - "punctuation.definition.character-class.regexp", - "punctuation.character.set.begin.regexp", - "punctuation.character.set.end.regexp", - "keyword.operator.negation.regexp", - "support.other.parenthesis.regexp", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: [ - "constant.character.character-class.regexp", - "constant.other.character-class.set.regexp", - "constant.other.character-class.regexp", - "constant.character.set.regexp", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["constant.character", "constant.other.option"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.name.label", - settings: { - foreground: "var(--text-weak)", - }, - }, - ], - type: "dark", -} - -const highlighter = await createHighlighter({ - themes: [theme], - langs: [], -}) - -export const { use: useShiki, provider: ShikiProvider } = createSimpleContext({ - name: "Shiki", - init: () => { - return highlighter - }, -}) From c5a558f3dad12fd41d65d68eac9d774d4c4bd8d4 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:34:41 -0600 Subject: [PATCH 121/218] chore(desktop): remove dead code --- packages/desktop/src/components/code.tsx | 846 ----------------------- 1 file changed, 846 deletions(-) delete mode 100644 packages/desktop/src/components/code.tsx diff --git a/packages/desktop/src/components/code.tsx b/packages/desktop/src/components/code.tsx deleted file mode 100644 index bbf7e28aa..000000000 --- a/packages/desktop/src/components/code.tsx +++ /dev/null @@ -1,846 +0,0 @@ -import { bundledLanguages, type BundledLanguage, type ShikiTransformer } from "shiki" -import { splitProps, type ComponentProps, createEffect, onMount, onCleanup, createMemo, createResource } from "solid-js" -import { useLocal, type TextSelection } from "@/context/local" -import { getFileExtension, getNodeOffsetInLine, getSelectionInContainer } from "@/utils" -import { useShiki } from "@opencode-ai/ui" - -type DefinedSelection = Exclude - -interface Props extends ComponentProps<"div"> { - code: string - path: string -} - -export function Code(props: Props) { - const ctx = useLocal() - const highlighter = useShiki() - const [local, others] = splitProps(props, ["class", "classList", "code", "path"]) - const lang = createMemo(() => { - const ext = getFileExtension(local.path) - if (ext in bundledLanguages) return ext - return "text" - }) - - let container: HTMLDivElement | undefined - let isProgrammaticSelection = false - - const ranges = createMemo(() => { - const items = ctx.context.all() as Array<{ type: "file"; path: string; selection?: DefinedSelection }> - const result: DefinedSelection[] = [] - for (const item of items) { - if (item.path !== local.path) continue - const selection = item.selection - if (!selection) continue - result.push(selection) - } - return result - }) - - const createLineNumberTransformer = (selections: DefinedSelection[]): ShikiTransformer => { - const highlighted = new Set() - for (const selection of selections) { - const startLine = selection.startLine - const endLine = selection.endLine - const start = Math.max(1, Math.min(startLine, endLine)) - const end = Math.max(start, Math.max(startLine, endLine)) - const count = end - start + 1 - if (count <= 0) continue - const values = Array.from({ length: count }, (_, index) => start + index) - for (const value of values) highlighted.add(value) - } - return { - name: "line-number-highlight", - line(node, index) { - if (!highlighted.has(index)) return - this.addClassToHast(node, "line-number-highlight") - const children = node.children - if (!Array.isArray(children)) return - for (const child of children) { - if (!child || typeof child !== "object") continue - const element = child as { type?: string; properties?: { className?: string[] } } - if (element.type !== "element") continue - const className = element.properties?.className - if (!Array.isArray(className)) continue - const matches = className.includes("diff-oldln") || className.includes("diff-newln") - if (!matches) continue - if (className.includes("line-number-highlight")) continue - className.push("line-number-highlight") - } - }, - } - } - - const [html] = createResource( - () => ranges(), - async (activeRanges) => { - if (!highlighter.getLoadedLanguages().includes(lang())) { - await highlighter.loadLanguage(lang() as BundledLanguage) - } - return highlighter.codeToHtml(local.code || "", { - lang: lang() && lang() in bundledLanguages ? lang() : "text", - theme: "opencode", - transformers: [transformerUnifiedDiff(), transformerDiffGroups(), createLineNumberTransformer(activeRanges)], - }) as string - }, - ) - - onMount(() => { - if (!container) return - - let ticking = false - const onScroll = () => { - if (!container) return - // if (ctx.file.active()?.path !== local.path) return - if (ticking) return - ticking = true - requestAnimationFrame(() => { - ticking = false - ctx.file.scroll(local.path, container!.scrollTop) - }) - } - - const onSelectionChange = async () => { - if (!container) return - if (isProgrammaticSelection) return - // if (ctx.file.active()?.path !== local.path) return - const d = getSelectionInContainer(container) - if (!d) return - const p = (await ctx.file.node(local.path))?.selection - if (p && p.startLine === d.sl && p.endLine === d.el && p.startChar === d.sch && p.endChar === d.ech) return - ctx.file.select(local.path, { startLine: d.sl, startChar: d.sch, endLine: d.el, endChar: d.ech }) - } - - const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" - const onKeyDown = (e: KeyboardEvent) => { - // if (ctx.file.active()?.path !== local.path) return - const ae = document.activeElement as HTMLElement | undefined - const tag = (ae?.tagName || "").toLowerCase() - const inputFocused = !!ae && (tag === "input" || tag === "textarea" || ae.isContentEditable) - if (inputFocused) return - if (e.getModifierState(MOD) && e.key.toLowerCase() === "a") { - e.preventDefault() - if (!container) return - const element = container.querySelector("code") as HTMLElement | undefined - if (!element) return - const lines = Array.from(element.querySelectorAll(".line")) - if (!lines.length) return - const r = document.createRange() - const last = lines[lines.length - 1] - r.selectNodeContents(last) - const lastLen = r.toString().length - ctx.file.select(local.path, { startLine: 1, startChar: 0, endLine: lines.length, endChar: lastLen }) - } - } - - container.addEventListener("scroll", onScroll) - document.addEventListener("selectionchange", onSelectionChange) - document.addEventListener("keydown", onKeyDown) - - onCleanup(() => { - container?.removeEventListener("scroll", onScroll) - document.removeEventListener("selectionchange", onSelectionChange) - document.removeEventListener("keydown", onKeyDown) - }) - }) - - // Restore scroll position from store when content is ready - createEffect(async () => { - const content = html() - if (!container || !content) return - const top = (await ctx.file.node(local.path))?.scrollTop - if (top !== undefined && container.scrollTop !== top) container.scrollTop = top - }) - - // Sync selection from store -> DOM - createEffect(async () => { - const content = html() - if (!container || !content) return - // if (ctx.file.active()?.path !== local.path) return - const codeEl = container.querySelector("code") as HTMLElement | undefined - if (!codeEl) return - const target = (await ctx.file.node(local.path))?.selection - const current = getSelectionInContainer(container) - const sel = window.getSelection() - if (!sel) return - if (!target) { - if (current) { - isProgrammaticSelection = true - sel.removeAllRanges() - queueMicrotask(() => { - isProgrammaticSelection = false - }) - } - return - } - const matches = !!( - current && - current.sl === target.startLine && - current.sch === target.startChar && - current.el === target.endLine && - current.ech === target.endChar - ) - if (matches) return - const lines = Array.from(codeEl.querySelectorAll(".line")) - if (lines.length === 0) return - let sIdx = Math.max(0, target.startLine - 1) - let eIdx = Math.max(0, target.endLine - 1) - let sChar = Math.max(0, target.startChar || 0) - let eChar = Math.max(0, target.endChar || 0) - if (sIdx > eIdx || (sIdx === eIdx && sChar > eChar)) { - const ti = sIdx - sIdx = eIdx - eIdx = ti - const tc = sChar - sChar = eChar - eChar = tc - } - if (eChar === 0 && eIdx > sIdx) { - eIdx = eIdx - 1 - eChar = Number.POSITIVE_INFINITY - } - if (sIdx >= lines.length) return - if (eIdx >= lines.length) eIdx = lines.length - 1 - const s = getNodeOffsetInLine(lines[sIdx], sChar) ?? { node: lines[sIdx], offset: 0 } - const e = getNodeOffsetInLine(lines[eIdx], eChar) ?? { node: lines[eIdx], offset: lines[eIdx].childNodes.length } - const range = document.createRange() - range.setStart(s.node, s.offset) - range.setEnd(e.node, e.offset) - isProgrammaticSelection = true - sel.removeAllRanges() - sel.addRange(range) - queueMicrotask(() => { - isProgrammaticSelection = false - }) - }) - - // Build/toggle split layout and apply folding (both unified and split) - createEffect(() => { - const content = html() - if (!container || !content) return - const view = ctx.file.view(local.path) - - const pres = Array.from(container.querySelectorAll("pre")) - if (pres.length === 0) return - const originalPre = pres[0] - - const split = container.querySelector(".diff-split") - if (view === "diff-split") { - applySplitDiff(container) - const next = container.querySelector(".diff-split") - if (next) next.style.display = "" - originalPre.style.display = "none" - } else { - if (split) split.style.display = "none" - originalPre.style.display = "" - } - - const expanded = ctx.file.folded(local.path) - if (view === "diff-split") { - const left = container.querySelector(".diff-split pre:nth-child(1) code") - const right = container.querySelector(".diff-split pre:nth-child(2) code") - if (left) - applyDiffFolding(left, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "left" }) - if (right) - applyDiffFolding(right, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "right" }) - } else { - const code = container.querySelector("pre code") - if (code) - applyDiffFolding(code, 3, { - expanded, - onExpand: (key) => ctx.file.unfold(local.path, key), - }) - } - }) - - // Highlight groups + scroll coupling - const clearHighlights = () => { - if (!container) return - container.querySelectorAll(".diff-selected").forEach((el) => el.classList.remove("diff-selected")) - } - - const applyHighlight = (idx: number, scroll?: boolean) => { - if (!container) return - const view = ctx.file.view(local.path) - if (view === "raw") return - - clearHighlights() - - const nodes: HTMLElement[] = [] - if (view === "diff-split") { - const left = container.querySelector(".diff-split pre:nth-child(1) code") - const right = container.querySelector(".diff-split pre:nth-child(2) code") - if (left) - nodes.push(...Array.from(left.querySelectorAll(`[data-chgrp="${idx}"][data-diff="remove"]`))) - if (right) - nodes.push(...Array.from(right.querySelectorAll(`[data-chgrp="${idx}"][data-diff="add"]`))) - } else { - const code = container.querySelector("pre code") - if (code) nodes.push(...Array.from(code.querySelectorAll(`[data-chgrp="${idx}"]`))) - } - - for (const n of nodes) n.classList.add("diff-selected") - if (scroll && nodes.length) nodes[0].scrollIntoView({ block: "center", behavior: "smooth" }) - } - - const countGroups = () => { - if (!container) return 0 - const code = container.querySelector("pre code") - if (!code) return 0 - const set = new Set() - for (const el of Array.from(code.querySelectorAll(".diff-line[data-chgrp]"))) { - const v = el.getAttribute("data-chgrp") - if (v != undefined) set.add(v) - } - return set.size - } - - let lastIdx: number | undefined = undefined - let lastView: string | undefined - let lastContent: string | undefined - let lastRawIdx: number | undefined = undefined - createEffect(() => { - const content = html() - if (!container || !content) return - const view = ctx.file.view(local.path) - const raw = ctx.file.changeIndex(local.path) - if (raw === undefined) return - const total = countGroups() - if (total <= 0) return - const next = ((raw % total) + total) % total - - const navigated = lastRawIdx !== undefined && lastRawIdx !== raw - - if (next !== raw) { - ctx.file.setChangeIndex(local.path, next) - applyHighlight(next, true) - } else { - if (lastView !== view || lastContent !== content) applyHighlight(next) - if ((lastIdx !== undefined && lastIdx !== next) || navigated) applyHighlight(next, true) - } - - lastRawIdx = raw - lastIdx = next - lastView = view - lastContent = content - }) - - return ( -
    { - container = el - }} - innerHTML={html()} - class=" - font-mono text-xs tracking-wide overflow-y-auto h-full - [&]:[counter-reset:line] - [&_pre]:focus-visible:outline-none - [&_pre]:overflow-x-auto [&_pre]:no-scrollbar - [&_code]:min-w-full [&_code]:inline-block - [&_.tab]:relative - [&_.tab::before]:content['⇥'] - [&_.tab::before]:absolute - [&_.tab::before]:opacity-0 - [&_.space]:relative - [&_.space::before]:content-['·'] - [&_.space::before]:absolute - [&_.space::before]:opacity-0 - [&_.line]:inline-block [&_.line]:w-full - [&_.line]:hover:bg-background-element - [&_.line::before]:sticky [&_.line::before]:left-0 - [&_.line::before]:w-12 [&_.line::before]:pr-4 - [&_.line::before]:z-10 - [&_.line::before]:bg-background-panel - [&_.line::before]:text-text-muted/60 - [&_.line::before]:text-right [&_.line::before]:inline-block - [&_.line::before]:select-none - [&_.line::before]:[counter-increment:line] - [&_.line::before]:content-[counter(line)] - [&_.line-number-highlight]:bg-accent/20 - [&_.line-number-highlight::before]:bg-accent/40! - [&_.line-number-highlight::before]:text-background-panel! - [&_code.code-diff_.line::before]:content-[''] - [&_code.code-diff_.line::before]:w-0 - [&_code.code-diff_.line::before]:pr-0 - [&_.diff-split_code.code-diff::before]:w-10 - [&_.diff-split_.diff-newln]:left-0 - [&_.diff-oldln]:sticky [&_.diff-oldln]:left-0 - [&_.diff-oldln]:w-10 [&_.diff-oldln]:pr-2 - [&_.diff-oldln]:z-40 - [&_.diff-oldln]:text-text-muted/60 - [&_.diff-oldln]:text-right [&_.diff-oldln]:inline-block - [&_.diff-oldln]:select-none - [&_.diff-oldln]:bg-background-panel - [&_.diff-newln]:sticky [&_.diff-newln]:left-10 - [&_.diff-newln]:w-10 [&_.diff-newln]:pr-2 - [&_.diff-newln]:z-40 - [&_.diff-newln]:text-text-muted/60 - [&_.diff-newln]:text-right [&_.diff-newln]:inline-block - [&_.diff-newln]:select-none - [&_.diff-newln]:bg-background-panel - [&_.diff-add]:bg-success/20! - [&_.diff-add.diff-selected]:bg-success/50! - [&_.diff-add_.diff-oldln]:bg-success! - [&_.diff-add_.diff-oldln]:text-background-panel! - [&_.diff-add_.diff-newln]:bg-success! - [&_.diff-add_.diff-newln]:text-background-panel! - [&_.diff-remove]:bg-error/20! - [&_.diff-remove.diff-selected]:bg-error/50! - [&_.diff-remove_.diff-newln]:bg-error! - [&_.diff-remove_.diff-newln]:text-background-panel! - [&_.diff-remove_.diff-oldln]:bg-error! - [&_.diff-remove_.diff-oldln]:text-background-panel! - [&_.diff-sign]:inline-block [&_.diff-sign]:px-2 [&_.diff-sign]:select-none - [&_.diff-blank]:bg-background-element - [&_.diff-blank_.diff-oldln]:bg-background-element - [&_.diff-blank_.diff-newln]:bg-background-element - [&_.diff-collapsed]:block! [&_.diff-collapsed]:w-full [&_.diff-collapsed]:relative - [&_.diff-collapsed]:select-none - [&_.diff-collapsed]:bg-info/20 [&_.diff-collapsed]:hover:bg-info/40! - [&_.diff-collapsed]:text-info/80 [&_.diff-collapsed]:hover:text-info - [&_.diff-collapsed]:text-xs - [&_.diff-collapsed_.diff-oldln]:bg-info! - [&_.diff-collapsed_.diff-newln]:bg-info! - " - classList={{ - ...(local.classList || {}), - [local.class ?? ""]: !!local.class, - }} - {...others} - >
    - ) -} - -function transformerUnifiedDiff(): ShikiTransformer { - const kinds = new Map() - const meta = new Map() - let isDiff = false - - return { - name: "unified-diff", - preprocess(input) { - kinds.clear() - meta.clear() - isDiff = false - - const ls = input.split(/\r?\n/) - const out: Array = [] - let oldNo = 0 - let newNo = 0 - let inHunk = false - - for (let i = 0; i < ls.length; i++) { - const s = ls[i] - - const m = s.match(/^@@\s*-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s*@@/) - if (m) { - isDiff = true - inHunk = true - oldNo = parseInt(m[1], 10) - newNo = parseInt(m[3], 10) - continue - } - - if ( - /^diff --git /.test(s) || - /^Index: /.test(s) || - /^--- /.test(s) || - /^\+\+\+ /.test(s) || - /^[=]{3,}$/.test(s) || - /^\*{3,}$/.test(s) || - /^\\ No newline at end of file$/.test(s) - ) { - isDiff = true - continue - } - - if (!inHunk) { - out.push(s) - continue - } - - if (/^\+/.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "add") - meta.set(ln, { new: newNo, sign: "+" }) - newNo++ - continue - } - - if (/^-/.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "remove") - meta.set(ln, { old: oldNo, sign: "-" }) - oldNo++ - continue - } - - if (/^ /.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "context") - meta.set(ln, { old: oldNo, new: newNo }) - oldNo++ - newNo++ - continue - } - - // fallback in hunks - out.push(s) - } - - return out.join("\n").trimEnd() - }, - code(node) { - if (isDiff) this.addClassToHast(node, "code-diff") - }, - pre(node) { - if (isDiff) this.addClassToHast(node, "code-diff") - }, - line(node, line) { - if (!isDiff) return - const kind = kinds.get(line) - if (!kind) return - - const m = meta.get(line) || {} - - this.addClassToHast(node, "diff-line") - this.addClassToHast(node, `diff-${kind}`) - node.properties = node.properties || {} - ;(node.properties as any)["data-diff"] = kind - if (m.old != undefined) (node.properties as any)["data-old"] = String(m.old) - if (m.new != undefined) (node.properties as any)["data-new"] = String(m.new) - - const oldSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-oldln"] }, - children: [{ type: "text", value: m.old != undefined ? String(m.old) : " " }], - } - const newSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-newln"] }, - children: [{ type: "text", value: m.new != undefined ? String(m.new) : " " }], - } - - if (kind === "add" || kind === "remove" || kind === "context") { - const first = (node.children && (node.children as any[])[0]) as any - if (first && first.type === "element" && first.children && first.children.length > 0) { - const t = first.children[0] - if (t && t.type === "text" && typeof t.value === "string" && t.value.length > 0) { - const ch = t.value[0] - if (ch === "+" || ch === "-" || ch === " ") t.value = t.value.slice(1) - } - } - } - - const signSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-sign"] }, - children: [{ type: "text", value: (m as any).sign || " " }], - } - - // @ts-expect-error hast typing across versions - node.children = [oldSpan, newSpan, signSpan, ...(node.children || [])] - }, - } -} - -function transformerDiffGroups(): ShikiTransformer { - let group = -1 - let inGroup = false - return { - name: "diff-groups", - pre() { - group = -1 - inGroup = false - }, - line(node) { - const props = (node.properties || {}) as any - const kind = props["data-diff"] as string | undefined - if (kind === "add" || kind === "remove") { - if (!inGroup) { - group += 1 - inGroup = true - } - ;(node.properties as any)["data-chgrp"] = String(group) - } else { - inGroup = false - } - }, - } -} - -function applyDiffFolding( - root: HTMLElement, - context = 3, - options?: { expanded?: string[]; onExpand?: (key: string) => void; side?: "left" | "right" }, -) { - if (!root.classList.contains("code-diff")) return - - // Cleanup: unwrap previous collapsed blocks and remove toggles - const blocks = Array.from(root.querySelectorAll(".diff-collapsed-block")) - for (const block of blocks) { - const p = block.parentNode - if (!p) { - block.remove() - continue - } - while (block.firstChild) p.insertBefore(block.firstChild, block) - block.remove() - } - const toggles = Array.from(root.querySelectorAll(".diff-collapsed")) - for (const t of toggles) t.remove() - - const lines = Array.from(root.querySelectorAll(".diff-line")) - if (lines.length === 0) return - - const n = lines.length - const isChange = lines.map((l) => l.dataset["diff"] === "add" || l.dataset["diff"] === "remove") - const isContext = lines.map((l) => l.dataset["diff"] === "context") - if (!isChange.some(Boolean)) return - - const visible = new Array(n).fill(false) as boolean[] - for (let i = 0; i < n; i++) if (isChange[i]) visible[i] = true - for (let i = 0; i < n; i++) { - if (isChange[i]) { - const s = Math.max(0, i - context) - const e = Math.min(n - 1, i + context) - for (let j = s; j <= e; j++) if (isContext[j]) visible[j] = true - } - } - - type Range = { start: number; end: number } - const ranges: Range[] = [] - let i = 0 - while (i < n) { - if (!visible[i] && isContext[i]) { - let j = i - while (j + 1 < n && !visible[j + 1] && isContext[j + 1]) j++ - ranges.push({ start: i, end: j }) - i = j + 1 - } else { - i++ - } - } - - for (const r of ranges) { - const start = lines[r.start] - const end = lines[r.end] - const count = r.end - r.start + 1 - const minCollapse = 20 - if (count < minCollapse) { - continue - } - - // Wrap the entire collapsed chunk (including trailing newline) so it takes no space - const block = document.createElement("span") - block.className = "diff-collapsed-block" - start.parentElement?.insertBefore(block, start) - - let cur: Node | undefined = start - while (cur) { - const next: Node | undefined = cur.nextSibling || undefined - block.appendChild(cur) - if (cur === end) { - // Also move the newline after the last line into the block - if (next && next.nodeType === Node.TEXT_NODE && (next.textContent || "").startsWith("\n")) { - block.appendChild(next) - } - break - } - cur = next - } - - block.style.display = "none" - const row = document.createElement("span") - row.className = "line diff-collapsed" - row.setAttribute("data-kind", "collapsed") - row.setAttribute("data-count", String(count)) - row.setAttribute("tabindex", "0") - row.setAttribute("role", "button") - - const oldln = document.createElement("span") - oldln.className = "diff-oldln" - oldln.textContent = " " - - const newln = document.createElement("span") - newln.className = "diff-newln" - newln.textContent = " " - - const sign = document.createElement("span") - sign.className = "diff-sign" - sign.textContent = "…" - - const label = document.createElement("span") - label.textContent = `show ${count} unchanged line${count > 1 ? "s" : ""}` - - const key = `o${start.dataset["old"] || ""}-${end.dataset["old"] || ""}:n${start.dataset["new"] || ""}-${end.dataset["new"] || ""}` - - const show = (record = true) => { - if (record) options?.onExpand?.(key) - const p = block.parentNode - if (p) { - while (block.firstChild) p.insertBefore(block.firstChild, block) - block.remove() - } - row.remove() - } - - row.addEventListener("click", () => show(true)) - row.addEventListener("keydown", (ev) => { - if (ev.key === "Enter" || ev.key === " ") { - ev.preventDefault() - show(true) - } - }) - - block.parentElement?.insertBefore(row, block) - if (!options?.side || options.side === "left") row.appendChild(oldln) - if (!options?.side || options.side === "right") row.appendChild(newln) - row.appendChild(sign) - row.appendChild(label) - - if (options?.expanded && options.expanded.includes(key)) { - show(false) - } - } -} - -function applySplitDiff(container: HTMLElement) { - const pres = Array.from(container.querySelectorAll("pre")) - if (pres.length === 0) return - const originalPre = pres[0] - const originalCode = originalPre.querySelector("code") as HTMLElement | undefined - if (!originalCode || !originalCode.classList.contains("code-diff")) return - - // Rebuild split each time to match current content - const existing = container.querySelector(".diff-split") - if (existing) existing.remove() - - const grid = document.createElement("div") - grid.className = "diff-split grid grid-cols-2 gap-x-6" - - const makeColumn = () => { - const pre = document.createElement("pre") - pre.className = originalPre.className - const code = document.createElement("code") - code.className = originalCode.className - pre.appendChild(code) - return { pre, code } - } - - const left = makeColumn() - const right = makeColumn() - - // Helpers - const cloneSide = (line: HTMLElement, side: "old" | "new"): HTMLElement => { - const clone = line.cloneNode(true) as HTMLElement - const oldln = clone.querySelector(".diff-oldln") - const newln = clone.querySelector(".diff-newln") - if (side === "old") { - if (newln) newln.remove() - } else { - if (oldln) oldln.remove() - } - return clone - } - - const blankLine = (side: "old" | "new", kind: "add" | "remove"): HTMLElement => { - const span = document.createElement("span") - span.className = "line diff-line diff-blank" - span.setAttribute("data-diff", kind) - const ln = document.createElement("span") - ln.className = side === "old" ? "diff-oldln" : "diff-newln" - ln.textContent = " " - span.appendChild(ln) - return span - } - - const lines = Array.from(originalCode.querySelectorAll(".diff-line")) - let i = 0 - while (i < lines.length) { - const cur = lines[i] - const kind = cur.dataset["diff"] - - if (kind === "context") { - left.code.appendChild(cloneSide(cur, "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(cur, "new")) - right.code.appendChild(document.createTextNode("\n")) - i++ - continue - } - - if (kind === "remove") { - // Batch consecutive removes and following adds, then pair - const removes: HTMLElement[] = [] - const adds: HTMLElement[] = [] - let j = i - while (j < lines.length && lines[j].dataset["diff"] === "remove") { - removes.push(lines[j]) - j++ - } - let k = j - while (k < lines.length && lines[k].dataset["diff"] === "add") { - adds.push(lines[k]) - k++ - } - - const pairs = Math.min(removes.length, adds.length) - for (let p = 0; p < pairs; p++) { - left.code.appendChild(cloneSide(removes[p], "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - for (let p = pairs; p < removes.length; p++) { - left.code.appendChild(cloneSide(removes[p], "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(blankLine("new", "remove")) - right.code.appendChild(document.createTextNode("\n")) - } - for (let p = pairs; p < adds.length; p++) { - left.code.appendChild(blankLine("old", "add")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - - i = k - continue - } - - if (kind === "add") { - // Run of adds not preceded by removes - const adds: HTMLElement[] = [] - let j = i - while (j < lines.length && lines[j].dataset["diff"] === "add") { - adds.push(lines[j]) - j++ - } - for (let p = 0; p < adds.length; p++) { - left.code.appendChild(blankLine("old", "add")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - i = j - continue - } - - // Any other kind: mirror as context - left.code.appendChild(cloneSide(cur, "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(cur, "new")) - right.code.appendChild(document.createTextNode("\n")) - i++ - } - - grid.appendChild(left.pre) - grid.appendChild(right.pre) - container.appendChild(grid) -} From d462e380f4b444c7817ed2c4fbef27fec1eeeed1 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 14:46:58 -0500 Subject: [PATCH 122/218] fix: update references after moving message functions to MessageV2 namespace --- packages/opencode/src/server/server.ts | 2 +- packages/opencode/src/session/compaction.ts | 8 ++--- packages/opencode/src/session/index.ts | 36 +-------------------- packages/opencode/src/session/message-v2.ts | 35 ++++++++++++++++++++ packages/opencode/src/session/prompt.ts | 8 ++--- packages/opencode/src/tool/task.ts | 2 +- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index c72650060..53d8b4ddf 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -827,7 +827,7 @@ export namespace Server { ), async (c) => { const params = c.req.valid("param") - const message = await Session.getMessage({ + const message = await MessageV2.get({ sessionID: params.id, messageID: params.messageID, }) diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 4924a35dd..d54b07caf 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -111,7 +111,7 @@ export namespace SessionCompaction { draft.time.compacting = undefined }) }) - const toSummarize = await MessageV2.filterCompacted(Session.messageStream(input.sessionID)) + const toSummarize = await MessageV2.filterCompacted(MessageV2.stream(input.sessionID)) const model = await Provider.getModel(input.providerID, input.modelID) const system = [ ...SystemPrompt.summarize(model.providerID), @@ -270,7 +270,7 @@ export namespace SessionCompaction { } } - const parts = await Session.getParts(msg.id) + const parts = await MessageV2.parts(msg.id) return { info: msg, parts, @@ -287,7 +287,7 @@ export namespace SessionCompaction { }) if (result.shouldRetry) { for (let retry = 1; retry < maxRetries; retry++) { - const lastRetryPart = result.parts.findLast((p) => p.type === "retry") + const lastRetryPart = result.parts.findLast((p): p is MessageV2.RetryPart => p.type === "retry") if (lastRetryPart) { const delayMs = SessionRetry.getRetryDelayInMs(lastRetryPart.error, retry) @@ -336,7 +336,7 @@ export namespace SessionCompaction { if ( !msg.error || (MessageV2.AbortedError.isInstance(msg.error) && - result.parts.some((part) => part.type === "text" && part.text.length > 0)) + result.parts.some((part): part is MessageV2.TextPart => part.type === "text" && part.text.length > 0)) ) { msg.summary = true Bus.publish(Event.Compacted, { diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 0971531fe..831c2dddf 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -273,17 +273,6 @@ export namespace Session { return diffs ?? [] }) - export const messageStream = fn(Identifier.schema("session"), async function* (sessionID) { - const list = await Array.fromAsync(await Storage.list(["message", sessionID])) - for (let i = list.length - 1; i >= 0; i--) { - const read = await Storage.read(list[i]) - yield { - info: read, - parts: await getParts(read.id), - } - } - }) - export const messages = fn( z.object({ sessionID: Identifier.schema("session"), @@ -291,7 +280,7 @@ export namespace Session { }), async (input) => { const result = [] as MessageV2.WithParts[] - for await (const msg of messageStream(input.sessionID)) { + for await (const msg of MessageV2.stream(input.sessionID)) { if (input.limit && result.length >= input.limit) break result.push(msg) } @@ -300,29 +289,6 @@ export namespace Session { }, ) - export const getMessage = fn( - z.object({ - sessionID: Identifier.schema("session"), - messageID: Identifier.schema("message"), - }), - async (input) => { - return { - info: await Storage.read(["message", input.sessionID, input.messageID]), - parts: await getParts(input.messageID), - } - }, - ) - - export const getParts = fn(Identifier.schema("message"), async (messageID) => { - const result = [] as MessageV2.Part[] - for (const item of await Storage.list(["part", messageID])) { - const read = await Storage.read(item) - result.push(read) - } - result.sort((a, b) => (a.id > b.id ? 1 : -1)) - return result - }) - export async function* list() { const project = Instance.project for (const item of await Storage.list(["session", project.id])) { diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 66d293192..bf15d7341 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -12,6 +12,8 @@ import { import { Identifier } from "../id/id" import { LSP } from "../lsp" import { Snapshot } from "@/snapshot" +import { fn } from "@/util/fn" +import { Storage } from "@/storage/storage" export namespace MessageV2 { export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({})) @@ -655,6 +657,39 @@ export namespace MessageV2 { return convertToModelMessages(result) } + export const stream = fn(Identifier.schema("session"), async function* (sessionID) { + const list = await Array.fromAsync(await Storage.list(["message", sessionID])) + for (let i = list.length - 1; i >= 0; i--) { + yield await get({ + sessionID, + messageID: list[i][2], + }) + } + }) + + export const parts = fn(Identifier.schema("message"), async (messageID) => { + const result = [] as MessageV2.Part[] + for (const item of await Storage.list(["part", messageID])) { + const read = await Storage.read(item) + result.push(read) + } + result.sort((a, b) => (a.id > b.id ? 1 : -1)) + return result + }) + + export const get = fn( + z.object({ + sessionID: Identifier.schema("session"), + messageID: Identifier.schema("message"), + }), + async (input) => { + return { + info: await Storage.read(["message", input.sessionID, input.messageID]), + parts: await parts(input.messageID), + } + }, + ) + export async function filterCompacted(stream: AsyncIterable) { const result = [] as MessageV2.WithParts[] for await (const msg of stream) { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index cf11b1290..f9480cd60 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -362,7 +362,7 @@ export namespace SessionPrompt { }) if (result.shouldRetry) { for (let retry = 1; retry < maxRetries; retry++) { - const lastRetryPart = result.parts.findLast((p) => p.type === "retry") + const lastRetryPart = result.parts.findLast((p): p is MessageV2.RetryPart => p.type === "retry") if (lastRetryPart) { const delayMs = SessionRetry.getRetryDelayInMs(lastRetryPart.error, retry) @@ -434,7 +434,7 @@ export namespace SessionPrompt { providerID: string signal: AbortSignal }) { - let msgs = await MessageV2.filterCompacted(Session.messageStream(input.sessionID)) + let msgs = await MessageV2.filterCompacted(MessageV2.stream(input.sessionID)) const lastAssistant = msgs.findLast((msg) => msg.info.role === "assistant") if ( lastAssistant?.info.role === "assistant" && @@ -1106,7 +1106,7 @@ export namespace SessionPrompt { }) toolcalls[value.toolCallId] = part as MessageV2.ToolPart - const parts = await Session.getParts(assistantMsg.id) + const parts = await MessageV2.parts(assistantMsg.id) const lastThree = parts.slice(-DOOM_LOOP_THRESHOLD) if ( lastThree.length === DOOM_LOOP_THRESHOLD && @@ -1319,7 +1319,7 @@ export namespace SessionPrompt { }) } } - const p = await Session.getParts(assistantMsg.id) + const p = await MessageV2.parts(assistantMsg.id) for (const part of p) { if ( part.type === "tool" && diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 642611f8a..312c3bba7 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -35,7 +35,7 @@ export const TaskTool = Tool.define("task", async () => { parentID: ctx.sessionID, title: params.description + ` (@${agent.name} subagent)`, }) - const msg = await Session.getMessage({ sessionID: ctx.sessionID, messageID: ctx.messageID }) + const msg = await MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID }) if (msg.info.role !== "assistant") throw new Error("Not an assistant message") ctx.metadata({ From 5f7e1e099b2b5786dd94a172c33d6997d54c215f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 19:47:53 +0000 Subject: [PATCH 123/218] chore: format code --- packages/opencode/src/session/compaction.ts | 8 ++++++-- packages/opencode/src/session/prompt.ts | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index d54b07caf..678311012 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -287,7 +287,9 @@ export namespace SessionCompaction { }) if (result.shouldRetry) { for (let retry = 1; retry < maxRetries; retry++) { - const lastRetryPart = result.parts.findLast((p): p is MessageV2.RetryPart => p.type === "retry") + const lastRetryPart = result.parts.findLast( + (p): p is MessageV2.RetryPart => p.type === "retry", + ) if (lastRetryPart) { const delayMs = SessionRetry.getRetryDelayInMs(lastRetryPart.error, retry) @@ -336,7 +338,9 @@ export namespace SessionCompaction { if ( !msg.error || (MessageV2.AbortedError.isInstance(msg.error) && - result.parts.some((part): part is MessageV2.TextPart => part.type === "text" && part.text.length > 0)) + result.parts.some( + (part): part is MessageV2.TextPart => part.type === "text" && part.text.length > 0, + )) ) { msg.summary = true Bus.publish(Event.Compacted, { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index f9480cd60..3210a08fd 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -362,7 +362,9 @@ export namespace SessionPrompt { }) if (result.shouldRetry) { for (let retry = 1; retry < maxRetries; retry++) { - const lastRetryPart = result.parts.findLast((p): p is MessageV2.RetryPart => p.type === "retry") + const lastRetryPart = result.parts.findLast( + (p): p is MessageV2.RetryPart => p.type === "retry", + ) if (lastRetryPart) { const delayMs = SessionRetry.getRetryDelayInMs(lastRetryPart.error, retry) From a96365fd81555a2967524930aeb949009008f9a2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 14:51:37 -0500 Subject: [PATCH 124/218] Add command bar action to rename sessions --- .../opencode/src/cli/cmd/tui/routes/session/index.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 830d475f8..cd3a8f72d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -60,6 +60,7 @@ import type { PromptInfo } from "../../component/prompt/history" import { iife } from "@/util/iife" import { DialogConfirm } from "@tui/ui/dialog-confirm" import { DialogTimeline } from "./dialog-timeline" +import { DialogSessionRename } from "../../component/dialog-session-rename" import { Sidebar } from "./sidebar" import { LANGUAGE_EXTENSIONS } from "@/lsp/language" import parsers from "../../../../../../parsers-config.ts" @@ -191,6 +192,15 @@ export function Session() { const command = useCommandDialog() command.register(() => [ + { + title: "Rename session", + value: "session.rename", + keybind: "session_rename", + category: "Session", + onSelect: (dialog) => { + dialog.replace(() => ) + }, + }, { title: "Jump to message", value: "session.timeline", From 45fabec0919ba3268b6bd0b9566ec98cac0ed3d7 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:54:18 -0600 Subject: [PATCH 125/218] fix(desktop): prompt input on non-chat tabs --- packages/desktop/src/pages/session.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 1c92941d0..23d44cfcb 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -829,6 +829,15 @@ export default function Page() {
    + +
    + { + inputRef = el + }} + /> +
    +
    From b46c3f2a26b06b2fe7459b082def635654756094 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:54:49 -0600 Subject: [PATCH 126/218] fix(desktop): prompt input issues (wip) --- .../desktop/src/components/prompt-input.tsx | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index 15bc54c49..5ae56f827 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -174,10 +174,8 @@ export const PromptInput: Component = (props) => { const addPart = (part: ContentPart) => { const cursorPosition = getCursorPosition(editorRef) - const rawText = session.prompt - .current() - .map((p) => p.content) - .join("") + const prompt = session.prompt.current() + const rawText = prompt.map((p) => p.content).join("") const textBeforeCursor = rawText.substring(0, cursorPosition) const atMatch = textBeforeCursor.match(/@(\S*)$/) @@ -203,7 +201,7 @@ export const PromptInput: Component = (props) => { parts: nextParts, inserted, cursorPositionAfter, - } = session.prompt.current().reduce( + } = prompt.reduce( (acc, item) => { if (acc.inserted) { acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length }) @@ -262,7 +260,7 @@ export const PromptInput: Component = (props) => { ) if (!inserted) { - const baseParts = session.prompt.current().filter((item) => !(item.type === "text" && item.content === "")) + const baseParts = prompt.filter((item) => !(item.type === "text" && item.content === "")) const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0) const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex } if (part.type === "text") { @@ -316,10 +314,8 @@ export const PromptInput: Component = (props) => { const handleSubmit = async (event: Event) => { event.preventDefault() - const text = session.prompt - .current() - .map((part) => part.content) - .join("") + const prompt = session.prompt.current() + const text = prompt.map((part) => part.content).join("") if (text.trim().length === 0) { if (session.working()) abort() return @@ -329,19 +325,17 @@ export const PromptInput: Component = (props) => { if (!existing) { const created = await sdk.client.session.create() existing = created.data ?? undefined + if (existing) navigate(`/session/${existing.id}`) } if (!existing) return - navigate(`/session/${existing.id}`) - if (!session.id) { - // session.layout.setOpenedTabs( - // session.layout.copyTabs("", session.id) - } - session.layout.setActiveTab(undefined) - session.messages.setActive(undefined) - const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + // if (!session.id) { + // session.layout.setOpenedTabs( + // session.layout.copyTabs("", session.id) + // } - const attachments = session.prompt.current().filter((part) => part.type === "file") + const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + const attachments = prompt.filter((part) => part.type === "file") // const activeFile = local.context.active() // if (activeFile) { @@ -382,9 +376,11 @@ export const PromptInput: Component = (props) => { } }) + session.layout.setActiveTab(undefined) + session.messages.setActive(undefined) session.prompt.set(DEFAULT_PROMPT, 0) - await sdk.client.session.prompt({ + sdk.client.session.prompt({ path: { id: existing.id }, body: { agent: local.agent.current()!.name, From 73cd8a334c146237f177c6a78e6ed37b50ada8b6 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:57:12 -0800 Subject: [PATCH 127/218] rework acp to compensate for changes in Zed IDE (#4050) --- packages/extensions/zed/extension.toml | 36 ++ packages/extensions/zed/icons/opencode.svg | 3 + packages/opencode/src/acp/agent.ts | 718 ++++++++++++--------- packages/opencode/src/acp/session.ts | 60 +- packages/opencode/src/acp/types.ts | 5 +- packages/opencode/src/cli/cmd/acp.ts | 39 +- packages/opencode/src/mcp/index.ts | 38 +- packages/opencode/src/server/server.ts | 30 + packages/sdk/js/src/gen/sdk.gen.ts | 17 + packages/sdk/js/src/gen/types.gen.ts | 36 +- 10 files changed, 621 insertions(+), 361 deletions(-) create mode 100644 packages/extensions/zed/extension.toml create mode 100644 packages/extensions/zed/icons/opencode.svg diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml new file mode 100644 index 000000000..34af9621f --- /dev/null +++ b/packages/extensions/zed/extension.toml @@ -0,0 +1,36 @@ +id = "opencode" +name = "OpenCode" +description = "The AI coding agent built for the terminal" +version = "0.1.1" +schema_version = 1 +authors = ["Anomaly"] +repository = "https://github.com/sst/opencode" + +[agent_servers.opencode] +name = "OpenCode" +icon = "./icons/opencode.svg" + +[agent_servers.opencode.targets.darwin-aarch64] +archive = "https://github.com/sst/opencode/releases/latest/download/opencode-darwin-arm64.zip" +cmd = "./opencode" +args = ["acp"] + +[agent_servers.opencode.targets.darwin-x86_64] +archive = "https://github.com/sst/opencode/releases/latest/download/opencode-darwin-x64.zip" +cmd = "./opencode" +args = ["acp"] + +[agent_servers.opencode.targets.linux-aarch64] +archive = "https://github.com/sst/opencode/releases/latest/download/opencode-linux-arm64.zip" +cmd = "./opencode" +args = ["acp"] + +[agent_servers.opencode.targets.linux-x86_64] +archive = "https://github.com/sst/opencode/releases/latest/download/opencode-linux-x64.zip" +cmd = "./opencode" +args = ["acp"] + +[agent_servers.opencode.targets.windows-x86_64] +archive = "https://github.com/sst/opencode/releases/latest/download/opencode-windows-x64.zip" +cmd = "./opencode.exe" +args = ["acp"] diff --git a/packages/extensions/zed/icons/opencode.svg b/packages/extensions/zed/icons/opencode.svg new file mode 100644 index 000000000..fc001e49b --- /dev/null +++ b/packages/extensions/zed/icons/opencode.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 046c8262d..b25b66888 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -20,299 +20,321 @@ import { } from "@agentclientprotocol/sdk" import { Log } from "../util/log" import { ACPSessionManager } from "./session" -import type { ACPConfig } from "./types" +import type { ACPConfig, ACPSessionState } from "./types" import { Provider } from "../provider/provider" -import { SessionPrompt } from "../session/prompt" import { Installation } from "@/installation" -import { SessionLock } from "@/session/lock" -import { Bus } from "@/bus" import { MessageV2 } from "@/session/message-v2" -import { Storage } from "@/storage/storage" -import { Command } from "@/command" -import { Agent as Agents } from "@/agent/agent" -import { Permission } from "@/permission" -import { SessionCompaction } from "@/session/compaction" import { Config } from "@/config/config" import { MCP } from "@/mcp" import { Todo } from "@/session/todo" import { z } from "zod" import { LoadAPIKeyError } from "ai" +import type { OpencodeClient } from "@opencode-ai/sdk" export namespace ACP { const log = Log.create({ service: "acp-agent" }) - export async function init() { - const model = await defaultModel({}) + export async function init({ sdk }: { sdk: OpencodeClient }) { + const model = await defaultModel({ sdk }) return { - create: (connection: AgentSideConnection, config: ACPConfig) => { - if (!config.defaultModel) { - config.defaultModel = model + create: (connection: AgentSideConnection, fullConfig: ACPConfig) => { + if (!fullConfig.defaultModel) { + fullConfig.defaultModel = model } - return new Agent(connection, config) + return new Agent(connection, fullConfig) }, } } export class Agent implements ACPAgent { - private sessionManager = new ACPSessionManager() private connection: AgentSideConnection private config: ACPConfig + private sdk: OpencodeClient + private sessionManager - constructor(connection: AgentSideConnection, config: ACPConfig = {}) { + constructor(connection: AgentSideConnection, config: ACPConfig) { this.connection = connection this.config = config - this.setupEventSubscriptions() + this.sdk = config.sdk + this.sessionManager = new ACPSessionManager(this.sdk) } - private setupEventSubscriptions() { + private setupEventSubscriptions(session: ACPSessionState) { + const sessionId = session.id + const directory = session.cwd + const options: PermissionOption[] = [ { optionId: "once", kind: "allow_once", name: "Allow once" }, { optionId: "always", kind: "allow_always", name: "Always allow" }, { optionId: "reject", kind: "reject_once", name: "Reject" }, ] - Bus.subscribe(Permission.Event.Updated, async (event) => { - const acpSession = this.sessionManager.get(event.properties.sessionID) - if (!acpSession) return - try { - const permission = event.properties - const res = await this.connection - .requestPermission({ - sessionId: acpSession.id, - toolCall: { - toolCallId: permission.callID ?? permission.id, - status: "pending", - title: permission.title, - rawInput: permission.metadata, - kind: toToolKind(permission.type), - locations: toLocations(permission.type, permission.metadata), - }, - options, - }) - .catch((error) => { - log.error("failed to request permission from ACP", { - error, - permissionID: permission.id, - sessionID: permission.sessionID, - }) - Permission.respond({ - sessionID: permission.sessionID, - permissionID: permission.id, - response: "reject", - }) - return - }) - if (!res) return - if (res.outcome.outcome !== "selected") { - Permission.respond({ - sessionID: permission.sessionID, - permissionID: permission.id, - response: "reject", - }) - return - } - Permission.respond({ - sessionID: permission.sessionID, - permissionID: permission.id, - response: res.outcome.optionId as "once" | "always" | "reject", - }) - } catch (err) { - if (!(err instanceof Permission.RejectedError)) { - log.error("unexpected error when handling permission", { error: err }) - throw err - } - } - }) - - Bus.subscribe(MessageV2.Event.PartUpdated, async (event) => { - const props = event.properties - const { part } = props - const acpSession = this.sessionManager.get(part.sessionID) - if (!acpSession) return - - const message = await Storage.read([ - "message", - part.sessionID, - part.messageID, - ]).catch(() => undefined) - if (!message || message.role !== "assistant") return - - if (part.type === "tool") { - switch (part.state.status) { - case "pending": - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "tool_call", - toolCallId: part.callID, - title: part.tool, - kind: toToolKind(part.tool), - status: "pending", - locations: [], - rawInput: {}, - }, - }) - .catch((err) => { - log.error("failed to send tool pending to ACP", { error: err }) - }) - break - case "running": - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "tool_call_update", - toolCallId: part.callID, - status: "in_progress", - locations: toLocations(part.tool, part.state.input), - rawInput: part.state.input, - }, - }) - .catch((err) => { - log.error("failed to send tool in_progress to ACP", { error: err }) - }) - break - case "completed": - const kind = toToolKind(part.tool) - const content: ToolCallContent[] = [ - { - type: "content", - content: { - type: "text", - text: part.state.output, - }, - }, - ] - - if (kind === "edit") { - const input = part.state.input - const filePath = typeof input["filePath"] === "string" ? input["filePath"] : "" - const oldText = typeof input["oldString"] === "string" ? input["oldString"] : "" - const newText = - typeof input["newString"] === "string" - ? input["newString"] - : typeof input["content"] === "string" - ? input["content"] - : "" - content.push({ - type: "diff", - path: filePath, - oldText, - newText, - }) - } - - if (part.tool === "todowrite") { - const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output)) - if (parsedTodos.success) { - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "plan", - entries: parsedTodos.data.map((todo) => { - const status: PlanEntry["status"] = - todo.status === "cancelled" - ? "completed" - : (todo.status as PlanEntry["status"]) - return { - priority: "medium", - status, - content: todo.content, - } - }), + this.config.sdk.event.subscribe({ query: { directory } }).then(async (events) => { + for await (const event of events.stream) { + switch (event.type) { + case "permission.updated": + try { + const permission = event.properties + const res = await this.connection + .requestPermission({ + sessionId, + toolCall: { + toolCallId: permission.callID ?? permission.id, + status: "pending", + title: permission.title, + rawInput: permission.metadata, + kind: toToolKind(permission.type), + locations: toLocations(permission.type, permission.metadata), + }, + options, + }) + .catch(async (error) => { + log.error("failed to request permission from ACP", { + error, + permissionID: permission.id, + sessionID: permission.sessionID, + }) + await this.config.sdk.postSessionIdPermissionsPermissionId({ + path: { id: permission.sessionID, permissionID: permission.id }, + body: { + response: "reject", }, + query: { directory }, }) - .catch((err) => { - log.error("failed to send session update for todo", { error: err }) - }) - } else { - log.error("failed to parse todo output", { error: parsedTodos.error }) + return + }) + if (!res) return + if (res.outcome.outcome !== "selected") { + await this.config.sdk.postSessionIdPermissionsPermissionId({ + path: { id: permission.sessionID, permissionID: permission.id }, + body: { + response: "reject", + }, + query: { directory }, + }) + return } + await this.config.sdk.postSessionIdPermissionsPermissionId({ + path: { id: permission.sessionID, permissionID: permission.id }, + body: { + response: res.outcome.optionId as "once" | "always" | "reject", + }, + query: { directory }, + }) + } catch (err) { + log.error("unexpected error when handling permission", { error: err }) + } finally { + break } - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "tool_call_update", - toolCallId: part.callID, - status: "completed", - kind, - content, - title: part.state.title, - rawOutput: { - output: part.state.output, - metadata: part.state.metadata, + case "message.part.updated": + log.info("message part updated", { event: event.properties }) + try { + const props = event.properties + const { part } = props + + const message = await this.config.sdk.session + .message({ + throwOnError: true, + path: { + id: part.sessionID, + messageID: part.messageID, }, - }, - }) - .catch((err) => { - log.error("failed to send tool completed to ACP", { error: err }) - }) - break - case "error": - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "tool_call_update", - toolCallId: part.callID, - status: "failed", - content: [ - { - type: "content", - content: { - type: "text", - text: part.state.error, + query: { directory }, + }) + .then((x) => x.data) + .catch((err) => { + log.error("unexpected error when fetching message", { error: err }) + return undefined + }) + + if (!message || message.info.role !== "assistant") return + + if (part.type === "tool") { + switch (part.state.status) { + case "pending": + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "tool_call", + toolCallId: part.callID, + title: part.tool, + kind: toToolKind(part.tool), + status: "pending", + locations: [], + rawInput: {}, + }, + }) + .catch((err) => { + log.error("failed to send tool pending to ACP", { error: err }) + }) + break + case "running": + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: part.callID, + status: "in_progress", + locations: toLocations(part.tool, part.state.input), + rawInput: part.state.input, + }, + }) + .catch((err) => { + log.error("failed to send tool in_progress to ACP", { error: err }) + }) + break + case "completed": + const kind = toToolKind(part.tool) + const content: ToolCallContent[] = [ + { + type: "content", + content: { + type: "text", + text: part.state.output, + }, }, - }, - ], - rawOutput: { - error: part.state.error, - }, - }, - }) - .catch((err) => { - log.error("failed to send tool error to ACP", { error: err }) - }) - break - } - } else if (part.type === "text") { - const delta = props.delta - if (delta && part.synthetic !== true) { - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "agent_message_chunk", - content: { - type: "text", - text: delta, - }, - }, - }) - .catch((err) => { - log.error("failed to send text to ACP", { error: err }) - }) - } - } else if (part.type === "reasoning") { - const delta = props.delta - if (delta) { - await this.connection - .sessionUpdate({ - sessionId: acpSession.id, - update: { - sessionUpdate: "agent_thought_chunk", - content: { - type: "text", - text: delta, - }, - }, - }) - .catch((err) => { - log.error("failed to send reasoning to ACP", { error: err }) - }) + ] + + if (kind === "edit") { + const input = part.state.input + const filePath = + typeof input["filePath"] === "string" ? input["filePath"] : "" + const oldText = + typeof input["oldString"] === "string" ? input["oldString"] : "" + const newText = + typeof input["newString"] === "string" + ? input["newString"] + : typeof input["content"] === "string" + ? input["content"] + : "" + content.push({ + type: "diff", + path: filePath, + oldText, + newText, + }) + } + + if (part.tool === "todowrite") { + const parsedTodos = z + .array(Todo.Info) + .safeParse(JSON.parse(part.state.output)) + if (parsedTodos.success) { + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "plan", + entries: parsedTodos.data.map((todo) => { + const status: PlanEntry["status"] = + todo.status === "cancelled" + ? "completed" + : (todo.status as PlanEntry["status"]) + return { + priority: "medium", + status, + content: todo.content, + } + }), + }, + }) + .catch((err) => { + log.error("failed to send session update for todo", { error: err }) + }) + } else { + log.error("failed to parse todo output", { error: parsedTodos.error }) + } + } + + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: part.callID, + status: "completed", + kind, + content, + title: part.state.title, + rawOutput: { + output: part.state.output, + metadata: part.state.metadata, + }, + }, + }) + .catch((err) => { + log.error("failed to send tool completed to ACP", { error: err }) + }) + break + case "error": + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: part.callID, + status: "failed", + content: [ + { + type: "content", + content: { + type: "text", + text: part.state.error, + }, + }, + ], + rawOutput: { + error: part.state.error, + }, + }, + }) + .catch((err) => { + log.error("failed to send tool error to ACP", { error: err }) + }) + break + } + } else if (part.type === "text") { + const delta = props.delta + if (delta && part.synthetic !== true) { + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "agent_message_chunk", + content: { + type: "text", + text: delta, + }, + }, + }) + .catch((err) => { + log.error("failed to send text to ACP", { error: err }) + }) + } + } else if (part.type === "reasoning") { + const delta = props.delta + if (delta) { + await this.connection + .sessionUpdate({ + sessionId, + update: { + sessionUpdate: "agent_thought_chunk", + content: { + type: "text", + text: delta, + }, + }, + }) + .catch((err) => { + log.error("failed to send reasoning to ACP", { error: err }) + }) + } + } + } finally { + break + } } } }) @@ -364,19 +386,26 @@ export namespace ACP { } async newSession(params: NewSessionRequest) { + const directory = params.cwd try { - const model = await defaultModel(this.config) - const session = await this.sessionManager.create(params.cwd, params.mcpServers, model) + const model = await defaultModel(this.config, directory) + + // Store ACP session state + const state = await this.sessionManager.create(params.cwd, params.mcpServers, model) + const sessionId = state.id + + log.info("creating_session", { sessionId, mcpServers: params.mcpServers.length }) - log.info("creating_session", { mcpServers: params.mcpServers.length }) const load = await this.loadSession({ - cwd: params.cwd, + cwd: directory, mcpServers: params.mcpServers, - sessionId: session.id, + sessionId, }) + this.setupEventSubscriptions(state) + return { - sessionId: session.id, + sessionId, models: load.models, modes: load.modes, _meta: {}, @@ -393,26 +422,47 @@ export namespace ACP { } async loadSession(params: LoadSessionRequest) { - const model = await defaultModel(this.config) + const directory = params.cwd + const model = await defaultModel(this.config, directory) const sessionId = params.sessionId - const providers = await Provider.list() - const entries = Object.entries(providers).sort((a, b) => { - const nameA = a[1].info.name.toLowerCase() - const nameB = b[1].info.name.toLowerCase() + const providers = await this.sdk.config + .providers({ throwOnError: true, query: { directory } }) + .then((x) => x.data.providers) + const entries = providers.sort((a, b) => { + const nameA = a.name.toLowerCase() + const nameB = b.name.toLowerCase() if (nameA < nameB) return -1 if (nameA > nameB) return 1 return 0 }) - const availableModels = entries.flatMap(([providerID, provider]) => { - const models = Provider.sort(Object.values(provider.info.models)) + const availableModels = entries.flatMap((provider) => { + const models = Provider.sort(Object.values(provider.models)) return models.map((model) => ({ - modelId: `${providerID}/${model.id}`, - name: `${provider.info.name}/${model.name}`, + modelId: `${provider.id}/${model.id}`, + name: `${provider.name}/${model.name}`, })) }) - const availableCommands = (await Command.list()).map((command) => ({ + const agents = await this.config.sdk.app + .agents({ + throwOnError: true, + query: { + directory, + }, + }) + .then((resp) => resp.data) + + const commands = await this.config.sdk.command + .list({ + throwOnError: true, + query: { + directory, + }, + }) + .then((resp) => resp.data) + + const availableCommands = commands.map((command) => ({ name: command.name, description: command.description ?? "", })) @@ -423,7 +473,7 @@ export namespace ACP { description: "compact the session", }) - const availableModes = (await Agents.list()) + const availableModes = agents .filter((agent) => agent.mode !== "subagent") .map((agent) => ({ id: agent.name, @@ -459,7 +509,18 @@ export namespace ACP { await Promise.all( Object.entries(mcpServers).map(async ([key, mcp]) => { - await MCP.add(key, mcp) + await this.sdk.mcp + .add({ + throwOnError: true, + query: { directory }, + body: { + name: key, + config: mcp, + }, + }) + .catch((error) => { + log.error("failed to add mcp server", { name: key, error }) + }) }), ) @@ -489,12 +550,8 @@ export namespace ACP { async setSessionModel(params: SetSessionModelRequest) { const session = this.sessionManager.get(params.sessionId) - if (!session) { - throw new Error(`Session not found: ${params.sessionId}`) - } - const parsed = Provider.parseModel(params.modelId) - const model = await Provider.getModel(parsed.providerID, parsed.modelID) + const model = Provider.parseModel(params.modelId) this.sessionManager.setModel(session.id, { providerID: model.providerID, @@ -507,31 +564,32 @@ export namespace ACP { } async setSessionMode(params: SetSessionModeRequest): Promise { - const session = this.sessionManager.get(params.sessionId) - if (!session) { - throw new Error(`Session not found: ${params.sessionId}`) - } - await Agents.get(params.modeId).then((agent) => { - if (!agent) throw new Error(`Agent not found: ${params.modeId}`) - }) + this.sessionManager.get(params.sessionId) + await this.config.sdk.app + .agents({ throwOnError: true }) + .then((x) => x.data) + .then((agent) => { + if (!agent) throw new Error(`Agent not found: ${params.modeId}`) + }) this.sessionManager.setMode(params.sessionId, params.modeId) } async prompt(params: PromptRequest) { const sessionID = params.sessionId - const acpSession = this.sessionManager.get(sessionID) - if (!acpSession) { - throw new Error(`Session not found: ${sessionID}`) - } + const session = this.sessionManager.get(sessionID) + const directory = session.cwd - const current = acpSession.model - const model = current ?? (await defaultModel(this.config)) + const current = session.model + const model = current ?? (await defaultModel(this.config, directory)) if (!current) { - this.sessionManager.setModel(acpSession.id, model) + this.sessionManager.setModel(session.id, model) } - const agent = acpSession.modeId ?? "build" + const agent = session.modeId ?? "build" - const parts: SessionPrompt.PromptInput["parts"] = [] + const parts: Array< + | { type: "text"; text: string } + | { type: "file"; url: string; filename: string; mime: string } + > = [] for (const part of params.prompt) { switch (part.type) { case "text": @@ -545,12 +603,14 @@ export namespace ACP { parts.push({ type: "file", url: `data:${part.mimeType};base64,${part.data}`, + filename: "image", mime: part.mimeType, }) } else if (part.uri && part.uri.startsWith("http:")) { parts.push({ type: "file", url: part.uri, + filename: "image", mime: part.mimeType, }) } @@ -581,7 +641,7 @@ export namespace ACP { const cmd = (() => { const text = parts - .filter((p) => p.type === "text") + .filter((p): p is { type: "text"; text: string } => p.type === "text") .map((p) => p.text) .join("") .trim() @@ -598,36 +658,50 @@ export namespace ACP { } if (!cmd) { - await SessionPrompt.prompt({ - sessionID, - model: { - providerID: model.providerID, - modelID: model.modelID, + await this.sdk.session.prompt({ + path: { id: sessionID }, + body: { + model: { + providerID: model.providerID, + modelID: model.modelID, + }, + parts, + agent, + }, + query: { + directory, }, - parts, - agent, }) return done } - const command = await Command.get(cmd.name) + const command = await this.config.sdk.command + .list({ throwOnError: true, query: { directory } }) + .then((x) => x.data.find((c) => c.name === cmd.name)) if (command) { - await SessionPrompt.command({ - sessionID, - command: command.name, - arguments: cmd.args, - model: model.providerID + "/" + model.modelID, - agent, + await this.sdk.session.command({ + path: { id: sessionID }, + body: { + command: command.name, + arguments: cmd.args, + model: model.providerID + "/" + model.modelID, + agent, + }, + query: { + directory, + }, }) return done } switch (cmd.name) { case "compact": - await SessionCompaction.run({ - sessionID, - providerID: model.providerID, - modelID: model.modelID, + await this.config.sdk.session.summarize({ + path: { id: sessionID }, + throwOnError: true, + query: { + directory, + }, }) break } @@ -636,7 +710,14 @@ export namespace ACP { } async cancel(params: CancelNotification) { - SessionLock.abort(params.sessionId) + const session = this.sessionManager.get(params.sessionId) + await this.config.sdk.session.abort({ + path: { id: params.sessionId }, + throwOnError: true, + query: { + directory: session.cwd, + }, + }) } } @@ -687,12 +768,15 @@ export namespace ACP { } } - async function defaultModel(config: ACPConfig) { + async function defaultModel(config: ACPConfig, cwd?: string) { + const sdk = config.sdk const configured = config.defaultModel if (configured) return configured - const model = await Config.get() - .then((cfg) => { + const model = await sdk.config + .get({ throwOnError: true, query: { directory: cwd } }) + .then((resp) => { + const cfg = resp.data if (!cfg.model) return undefined const parsed = Provider.parseModel(cfg.model) return { diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts index d3ab73d21..eb9dd5229 100644 --- a/packages/opencode/src/acp/session.ts +++ b/packages/opencode/src/acp/session.ts @@ -1,66 +1,74 @@ -import type { McpServer } from "@agentclientprotocol/sdk" -import { Session } from "../session" -import { Provider } from "../provider/provider" +import { RequestError, type McpServer } from "@agentclientprotocol/sdk" import type { ACPSessionState } from "./types" +import { Log } from "@/util/log" +import type { OpencodeClient } from "@opencode-ai/sdk" + +const log = Log.create({ service: "acp-session-manager" }) export class ACPSessionManager { private sessions = new Map() + private sdk: OpencodeClient + + constructor(sdk: OpencodeClient) { + this.sdk = sdk + } async create( cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"], ): Promise { - const session = await Session.create({ title: `ACP Session ${crypto.randomUUID()}` }) + const session = await this.sdk.session + .create({ + body: { + title: `ACP Session ${crypto.randomUUID()}`, + }, + query: { + directory: cwd, + }, + throwOnError: true, + }) + .then((x) => x.data) + const sessionId = session.id - const resolvedModel = model ?? (await Provider.defaultModel()) + const resolvedModel = model const state: ACPSessionState = { id: sessionId, - parentId: session.parentID, cwd, mcpServers, createdAt: new Date(), model: resolvedModel, } + log.info("creating_session", { state }) this.sessions.set(sessionId, state) return state } - get(sessionId: string) { - return this.sessions.get(sessionId) - } - - async remove(sessionId: string) { - const state = this.sessions.get(sessionId) - if (!state) return - - await Session.remove(sessionId).catch(() => {}) - this.sessions.delete(sessionId) - } - - has(sessionId: string) { - return this.sessions.has(sessionId) + get(sessionId: string): ACPSessionState { + const session = this.sessions.get(sessionId) + if (!session) { + log.error("session not found", { sessionId }) + throw RequestError.invalidParams(JSON.stringify({ error: `Session not found: ${sessionId}` })) + } + return session } getModel(sessionId: string) { - const session = this.sessions.get(sessionId) - if (!session) return + const session = this.get(sessionId) return session.model } setModel(sessionId: string, model: ACPSessionState["model"]) { - const session = this.sessions.get(sessionId) - if (!session) return + const session = this.get(sessionId) session.model = model this.sessions.set(sessionId, session) return session } setMode(sessionId: string, modeId: string) { - const session = this.sessions.get(sessionId) - if (!session) return + const session = this.get(sessionId) session.modeId = modeId this.sessions.set(sessionId, session) return session diff --git a/packages/opencode/src/acp/types.ts b/packages/opencode/src/acp/types.ts index 119b335ce..8507228ed 100644 --- a/packages/opencode/src/acp/types.ts +++ b/packages/opencode/src/acp/types.ts @@ -1,12 +1,12 @@ import type { McpServer } from "@agentclientprotocol/sdk" +import type { OpencodeClient } from "@opencode-ai/sdk" export interface ACPSessionState { id: string - parentId?: string cwd: string mcpServers: McpServer[] createdAt: Date - model: { + model?: { providerID: string modelID: string } @@ -14,6 +14,7 @@ export interface ACPSessionState { } export interface ACPConfig { + sdk: OpencodeClient defaultModel?: { providerID: string modelID: string diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts index 77ef0c60c..7d27f9416 100644 --- a/packages/opencode/src/cli/cmd/acp.ts +++ b/packages/opencode/src/cli/cmd/acp.ts @@ -3,6 +3,8 @@ import { bootstrap } from "../bootstrap" import { cmd } from "./cmd" import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk" import { ACP } from "@/acp/agent" +import { Server } from "@/server/server" +import { createOpencodeClient } from "@opencode-ai/sdk" const log = Log.create({ service: "acp-command" }) @@ -17,15 +19,34 @@ export const AcpCommand = cmd({ command: "acp", describe: "Start ACP (Agent Client Protocol) server", builder: (yargs) => { - return yargs.option("cwd", { - describe: "working directory", - type: "string", - default: process.cwd(), - }) + return yargs + .option("cwd", { + describe: "working directory", + type: "string", + default: process.cwd(), + }) + .option("port", { + type: "number", + describe: "port to listen on", + default: 0, + }) + .option("hostname", { + type: "string", + describe: "hostname to listen on", + default: "127.0.0.1", + }) }, - handler: async (opts) => { - if (opts.cwd) process.chdir(opts["cwd"]) + handler: async (args) => { await bootstrap(process.cwd(), async () => { + const server = Server.listen({ + port: args.port, + hostname: args.hostname, + }) + + const sdk = createOpencodeClient({ + baseUrl: `http://${server.hostname}:${server.port}`, + }) + const input = new WritableStream({ write(chunk) { return new Promise((resolve, reject) => { @@ -50,10 +71,10 @@ export const AcpCommand = cmd({ }) const stream = ndJsonStream(input, output) - const agent = await ACP.init() + const agent = await ACP.init({ sdk }) new AgentSideConnection((conn) => { - return agent.create(conn, {}) + return agent.create(conn, { sdk }) }, stream) log.info("setup connection") diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index fbc887030..eddd19249 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -92,13 +92,28 @@ export namespace MCP { export async function add(name: string, mcp: Config.Mcp) { const s = await state() const result = await create(name, mcp) - if (!result) return + if (!result) { + const status = { + status: "failed" as const, + error: "unknown error", + } + s.status[name] = status + return { + status, + } + } if (!result.mcpClient) { s.status[name] = result.status - return + return { + status: s.status, + } } s.clients[name] = result.mcpClient s.status[name] = result.status + + return { + status: s.status, + } } async function create(key: string, mcp: Config.Mcp) { @@ -207,8 +222,12 @@ export namespace MCP { } } - const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch(() => {}) + const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch((err) => { + log.error("create() failed to get tools from client", { key, error: err }) + return undefined + }) if (!result) { + log.info("create() tools() returned nothing, closing client", { key }) await mcpClient.close().catch((error) => { log.error("Failed to close MCP client", { error, @@ -227,6 +246,7 @@ export namespace MCP { } } + log.info("create() successfully created client", { key, toolCount: Object.keys(result).length }) return { mcpClient, status, @@ -238,13 +258,18 @@ export namespace MCP { } export async function clients() { - return state().then((state) => state.clients) + const s = await state() + log.info("clients() called", { clientCount: Object.keys(s.clients).length }) + return s.clients } export async function tools() { const result: Record = {} const s = await state() - for (const [clientName, client] of Object.entries(await clients())) { + log.info("tools() called", { clientCount: Object.keys(s.clients).length }) + const clientsSnapshot = await clients() + for (const [clientName, client] of Object.entries(clientsSnapshot)) { + log.info("tools() fetching tools for client", { clientName }) const tools = await client.tools().catch((e) => { log.error("failed to get tools", { clientName, error: e.message }) const failedStatus = { @@ -255,14 +280,17 @@ export namespace MCP { delete s.clients[clientName] }) if (!tools) { + log.info("tools() no tools returned for client", { clientName }) continue } + log.info("tools() got tools for client", { clientName, toolCount: Object.keys(tools).length }) for (const [toolName, tool] of Object.entries(tools)) { const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_") const sanitizedToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_") result[sanitizedClientName + "_" + sanitizedToolName] = tool } } + log.info("tools() final result", { toolCount: Object.keys(result).length }) return result } } diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 53d8b4ddf..bfe804ae1 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -1359,6 +1359,36 @@ export namespace Server { return c.json(await MCP.status()) }, ) + .post( + "/mcp", + describeRoute({ + description: "Add MCP server dynamically", + operationId: "mcp.add", + responses: { + 200: { + description: "MCP server added successfully", + content: { + "application/json": { + schema: resolver(z.record(z.string(), MCP.Status)), + }, + }, + }, + ...errors(400), + }, + }), + validator( + "json", + z.object({ + name: z.string(), + config: Config.Mcp, + }), + ), + async (c) => { + const { name, config } = c.req.valid("json") + const result = await MCP.add(name, config) + return c.json(result.status) + }, + ) .get( "/lsp", describeRoute({ diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 1a54da8fa..f902d91ab 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -106,6 +106,9 @@ import type { AppAgentsResponses, McpStatusData, McpStatusResponses, + McpAddData, + McpAddResponses, + McpAddErrors, LspStatusData, LspStatusResponses, FormatterStatusData, @@ -764,6 +767,20 @@ class Mcp extends _HeyApiClient { ...options, }) } + + /** + * Add MCP server dynamically + */ + public add(options?: Options) { + return (options?.client ?? this._client).post({ + url: "/mcp", + ...options, + headers: { + "Content-Type": "application/json", + ...options?.headers, + }, + }) + } } class Lsp extends _HeyApiClient { diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 5f565df58..2a0df3024 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1979,9 +1979,9 @@ export type SessionMessagesData = { */ id: string } - query?: { + query: { directory?: string - limit?: number + limit: number } url: "/session/{id}/message" } @@ -2552,6 +2552,38 @@ export type McpStatusResponses = { export type McpStatusResponse = McpStatusResponses[keyof McpStatusResponses] +export type McpAddData = { + body?: { + name: string + config: McpLocalConfig | McpRemoteConfig + } + path?: never + query?: { + directory?: string + } + url: "/mcp" +} + +export type McpAddErrors = { + /** + * Bad request + */ + 400: BadRequestError +} + +export type McpAddError = McpAddErrors[keyof McpAddErrors] + +export type McpAddResponses = { + /** + * MCP server added successfully + */ + 200: { + [key: string]: McpStatus + } +} + +export type McpAddResponse = McpAddResponses[keyof McpAddResponses] + export type LspStatusData = { body?: never path?: never From b374a6cac950759eaf14e668e8517bb7abfc32d5 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:58:47 -0600 Subject: [PATCH 128/218] fix(desktop): stop icon size --- packages/ui/src/components/icon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index b61a54fe1..27a43e51e 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -150,7 +150,7 @@ const newIcons = { "code-lines": ``, "square-arrow-top-right": ``, "circle-ban-sign": ``, - stop: ``, + stop: ``, enter: ``, "layout-left": ``, "layout-right": ``, From 090d27df11a718cff3453a38da22d8a5eb405631 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 7 Nov 2025 13:59:18 -0600 Subject: [PATCH 129/218] chore: rm debug logs --- packages/opencode/src/mcp/index.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index eddd19249..149cd76f6 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -223,11 +223,10 @@ export namespace MCP { } const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch((err) => { - log.error("create() failed to get tools from client", { key, error: err }) + log.error("failed to get tools from client", { key, error: err }) return undefined }) if (!result) { - log.info("create() tools() returned nothing, closing client", { key }) await mcpClient.close().catch((error) => { log.error("Failed to close MCP client", { error, @@ -258,18 +257,14 @@ export namespace MCP { } export async function clients() { - const s = await state() - log.info("clients() called", { clientCount: Object.keys(s.clients).length }) - return s.clients + return state().then((state) => state.clients) } export async function tools() { const result: Record = {} const s = await state() - log.info("tools() called", { clientCount: Object.keys(s.clients).length }) const clientsSnapshot = await clients() for (const [clientName, client] of Object.entries(clientsSnapshot)) { - log.info("tools() fetching tools for client", { clientName }) const tools = await client.tools().catch((e) => { log.error("failed to get tools", { clientName, error: e.message }) const failedStatus = { @@ -280,17 +275,14 @@ export namespace MCP { delete s.clients[clientName] }) if (!tools) { - log.info("tools() no tools returned for client", { clientName }) continue } - log.info("tools() got tools for client", { clientName, toolCount: Object.keys(tools).length }) for (const [toolName, tool] of Object.entries(tools)) { const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_") const sanitizedToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_") result[sanitizedClientName + "_" + sanitizedToolName] = tool } } - log.info("tools() final result", { toolCount: Object.keys(result).length }) return result } } From b3c6d0b08a571796e4a9ecce798408b15a525df1 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 7 Nov 2025 14:10:20 -0600 Subject: [PATCH 130/218] fix formatters --- packages/opencode/src/format/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts index ebcc7909a..e307496c4 100644 --- a/packages/opencode/src/format/index.ts +++ b/packages/opencode/src/format/index.ts @@ -91,7 +91,6 @@ export namespace Format { export function init() { log.info("init") - return Bus.subscribe(File.Event.Edited, async (payload) => { const file = payload.properties.file log.info("formatting", { file }) From b5a035ceab79ce55f565823910d7c561f6ab7920 Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Fri, 7 Nov 2025 21:38:30 +0100 Subject: [PATCH 131/218] upgrade to opentui to fix disappearing content (again) and sticky scroll --- bun.lock | 20 ++++++++++---------- packages/opencode/package.json | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bun.lock b/bun.lock index c55d2821a..35071c6e1 100644 --- a/bun.lock +++ b/bun.lock @@ -185,8 +185,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.0.0-20251106-788e97e4", - "@opentui/solid": "0.0.0-20251106-788e97e4", + "@opentui/core": "0.1.38", + "@opentui/solid": "0.1.38", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -962,21 +962,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.0.0-20251106-788e97e4", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-darwin-x64": "0.0.0-20251106-788e97e4", "@opentui/core-linux-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-linux-x64": "0.0.0-20251106-788e97e4", "@opentui/core-win32-arm64": "0.0.0-20251106-788e97e4", "@opentui/core-win32-x64": "0.0.0-20251106-788e97e4", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-Es2Oe7/J/yb58e0jjq/04pV9Mekx6hM4go4C5uTiZksX3asfIGWk553cuf5WlWj0PDlVnC+s7Nnayi/NbLJ5jQ=="], + "@opentui/core": ["@opentui/core@0.1.38", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.38", "@opentui/core-darwin-x64": "0.1.38", "@opentui/core-linux-arm64": "0.1.38", "@opentui/core-linux-x64": "0.1.38", "@opentui/core-win32-arm64": "0.1.38", "@opentui/core-win32-x64": "0.1.38", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-GwSsOfGos0CTJhVaCT9DSGM/VcM38Kj8Ds06+sfloxB4sxPgrkItfXxwIm2uCLGUAtuGgGGUN8nMfUmrTibKZw=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.0.0-20251106-788e97e4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EOO8SSIYJBIh+Sd9bgVTiQmt+TEJmfg65/oym54J4zfDtCYlAqSaLcRnDe4TzB+4hejV9of8etrG3ZZACBJT+A=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.38", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iC71iR1N1ZiHjTC84PbncD7+NC1a+hWdF+wK7DVpW0NsKFRhNImIKGFxoq9aAG+/mH1dcvyNmk29gBwQnbPSpg=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.0.0-20251106-788e97e4", "", { "os": "darwin", "cpu": "x64" }, "sha512-MUTt7CDbzL2afNGK8gJ4jUZd+AHiOUJEO0eJGDSfWU8DUs0zv8XoLZfaI5PPbkUPEL/7CEBMARAAiwfRtoG/4A=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.38", "", { "os": "darwin", "cpu": "x64" }, "sha512-5f68CY8VC6/W78FjaeP/B/K6L5R28M+ypyZMzZphssimUAXY2uXzJyXN4AMSRWnI9ztDb1XS54m1GWkB2MBLGw=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.0.0-20251106-788e97e4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Zi1EzLCzooRfYoQnN/Dz8OxzrpRXByny8SJqhdO9ZP2mYX72yJ3AhUUW1Sl6YSzVi0H+QIKj7g+RX2KfsXIGFg=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.38", "", { "os": "linux", "cpu": "arm64" }, "sha512-WNiI7jx6o7UYCPNtp05ajODPSzQLZN2+DADKBWT3pOwGpWvLYeMQpUGYQ0Z1nmwsqO5R/27SJdaoPnbKU6DIPw=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.0.0-20251106-788e97e4", "", { "os": "linux", "cpu": "x64" }, "sha512-/E0XEBVzO4JEEhJGzfURF2tPxDE2oTODxlgNYYB1QbAuOsLcV69uSrwAjo1TxuIn4P78tBR+ZOlmONjroPqfbQ=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.38", "", { "os": "linux", "cpu": "x64" }, "sha512-sjQlgbmY0QNIyaCUO/Qp8J7wLLrTjhFzrQQE50Q4ReTNC6puUgsNBlT4PxoS3bYxVtvHYr+y95OCRqZPrbkyrQ=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.0.0-20251106-788e97e4", "", { "os": "win32", "cpu": "arm64" }, "sha512-En/29cgpYVvzlrQ7fAoP+EUdzmczgMzBIGGM0RuLi2hmCmCqyMtOJ0EJUh9UXa5jYIXNGOP49sIP6bUBbvXt7g=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.38", "", { "os": "win32", "cpu": "arm64" }, "sha512-mFylg8AItMvWaf4YhUfrO72NIrArjY3zhmHXObnV2pssvWZQWmskvtLPaXoaOEAyRBbev5hx7h58DgVzq6ECqw=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.0.0-20251106-788e97e4", "", { "os": "win32", "cpu": "x64" }, "sha512-2lu0bgEi+k/1c9VHQFg3wjVxMgQnuZhs/6sDDpxk9eNS3fuHEJfZi0PFJQk2J4IFQL61nzukOvJKgYDWQvKB1g=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.38", "", { "os": "win32", "cpu": "x64" }, "sha512-5JmYrlmT1OlLI8eAdSW7r4m1J903c15VtkfLOkjEpqasvS75J0ulvq6kuofdqXuYqBarIh8cb8lcsTFQJniM2Q=="], - "@opentui/solid": ["@opentui/solid@0.0.0-20251106-788e97e4", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.0.0-20251106-788e97e4", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-82rFS6BB60rJZU5Ad8Wf58V6HaMSkpnjciizkv3vsjJc9hvIAwLRNYqPypQB+etypuELhYMzzaVqt+wUsPHSqQ=="], + "@opentui/solid": ["@opentui/solid@0.1.38", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.38", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-xkBmuVEvpSddryzgGS7Y7KoP3UKiRbKcW7T9vlMrYQwq+JhqGuKsG0RI0wpG2kv6gTYeU1/OkwYzPm1DjXBaoA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e5b2a1387..d48cac816 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -54,8 +54,8 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentui/core": "0.0.0-20251106-788e97e4", - "@opentui/solid": "0.0.0-20251106-788e97e4", + "@opentui/core": "0.1.38", + "@opentui/solid": "0.1.38", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", From 58bbe9e689d4ae99e18cea4e0bdd40599643c427 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 15:58:51 -0500 Subject: [PATCH 132/218] ci: add optional version parameter to publish workflow Allows overriding the version when publishing releases instead of only using semantic bumping. This gives maintainers more control over release versioning for special cases or hotfixes. --- .github/workflows/publish.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index df1a36f77..a339c7dab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,6 +12,10 @@ on: - major - minor - patch + version: + description: "Override version (optional)" + required: false + type: string concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -62,6 +66,7 @@ jobs: ./script/publish.ts env: OPENCODE_BUMP: ${{ inputs.bump }} + OPENCODE_VERSION: ${{ inputs.version }} OPENCODE_CHANNEL: latest NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} From 5d6bdca6d06d4fbeccc94200b6ca4f552ad2da43 Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 7 Nov 2025 21:04:26 +0000 Subject: [PATCH 133/218] release: v1.0.43 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/sdk/js/src/gen/types.gen.ts | 4 ++-- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 15 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bun.lock b/bun.lock index 35071c6e1..8abcb293f 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.41", + "version": "1.0.43", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.41", + "version": "1.0.43", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index e830cdbcc..182062951 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.41" + "version": "1.0.43" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 25dc25f96..fa4b93501 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.41", + "version": "1.0.43", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index cf3ea4260..0182a4728 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.41", + "version": "1.0.43", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 19205a33f..d5e280f21 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.41", + "version": "1.0.43", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index cebbe8934..a2c1e3cb1 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.41", + "version": "1.0.43", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 57f0a8768..23c669cca 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.41", + "version": "1.0.43", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d48cac816..713e4651c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.41", + "version": "1.0.43", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f9d15e491..9317ac3e3 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.41", + "version": "1.0.43", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index d96c1a4ca..ead1c964b 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.41", + "version": "1.0.43", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 2a0df3024..08ce17b45 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1979,9 +1979,9 @@ export type SessionMessagesData = { */ id: string } - query: { + query?: { directory?: string - limit: number + limit?: number } url: "/session/{id}/message" } diff --git a/packages/slack/package.json b/packages/slack/package.json index 633050cd0..1ee34d212 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.41", + "version": "1.0.43", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 1bf2253c6..f0caecce4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.41", + "version": "1.0.43", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 0381033a3..e6d032a44 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.41", + "version": "1.0.43", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index baf51e575..55a7aa998 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.41", + "version": "1.0.43", "publisher": "sst-dev", "repository": { "type": "git", From aa07be09e18f7d2a3f2bfc36f9980e2bd88175d2 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 7 Nov 2025 15:08:50 -0600 Subject: [PATCH 134/218] ignore: update zed extension --- packages/extensions/zed/extension.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 34af9621f..6ab238b2b 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The AI coding agent built for the terminal" -version = "0.1.1" +version = "0.1.2" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/sst/opencode/releases/latest/download/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/sst/opencode/releases/latest/download/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/latest/download/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-linux-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/latest/download/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-linux-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/latest/download/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] From 4271df96d27965428a547ecf38db75baf169e0a8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 7 Nov 2025 21:09:47 +0000 Subject: [PATCH 135/218] chore: format code --- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 9317ac3e3..13e95bd9e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index ead1c964b..91d6eab70 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} From 79bb22a5735610e7d7691741865c22808436343c Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 7 Nov 2025 15:17:19 -0600 Subject: [PATCH 136/218] ci: auto update zed extension --- script/publish.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script/publish.ts b/script/publish.ts index 307755a1c..7cd81d41f 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -83,6 +83,14 @@ for (const file of pkgjsons) { console.log("updated:", file) await Bun.file(file).write(pkg) } + +const extensionToml = new URL("../packages/extensions/zed/extension.toml", import.meta.url).pathname +let toml = await Bun.file(extensionToml).text() +toml = toml.replace(/^version = "[^"]+"/m, `version = "${Script.version}"`) +toml = toml.replaceAll(/releases\/v[^/]+\//g, `releases/v${Script.version}/`) +console.log("updated:", extensionToml) +await Bun.file(extensionToml).write(toml) + await $`bun install` console.log("\n=== opencode ===\n") From 0a395d87839e4d4700d5f51f5085785ec30b5fb6 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 7 Nov 2025 15:18:44 -0600 Subject: [PATCH 137/218] ignore: update version --- packages/extensions/zed/extension.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 6ab238b2b..bdde47815 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The AI coding agent built for the terminal" -version = "0.1.2" +version = "v1.0.43" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" From 1a2b3701f2a4f6ea12de4c51c6d4ba5d3d8d083a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 7 Nov 2025 16:19:38 -0500 Subject: [PATCH 138/218] tui: show more sessions in list and fix sync timing to prevent race conditions --- .../opencode/src/cli/cmd/tui/component/dialog-session-list.tsx | 2 +- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 2 +- packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index e6f7efbe3..c4f318451 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -43,6 +43,7 @@ export function DialogSessionList() { footer: Locale.time(x.time.updated), } }) + .slice(0, 150) }) createEffect(() => { @@ -57,7 +58,6 @@ export function DialogSessionList() { { setToDelete(undefined) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 5c8d31468..60758aeb1 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -269,8 +269,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ return last.time.completed ? "idle" : "working" }, async sync(sessionID: string) { - const now = Date.now() if (store.message[sessionID]) return + const now = Date.now() console.log("syncing", sessionID) const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 6dd0b5cd4..b442f7b34 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -23,7 +23,6 @@ export interface DialogSelectProps { title: string onTrigger: (option: DialogSelectOption) => void }[] - limit?: number current?: T } @@ -58,7 +57,6 @@ export function DialogSelect(props: DialogSelectProps) { const result = pipe( props.options, filter((x) => x.disabled !== true), - take(props.limit ?? Infinity), (x) => !needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj), ) From 39461fbbce6a54d254a182a982607f5571120551 Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 7 Nov 2025 21:24:15 +0000 Subject: [PATCH 139/218] release: v1.0.44 --- bun.lock | 22 +++++++++++----------- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/extensions/zed/extension.toml | 12 ++++++------ packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 4 ++-- packages/sdk/js/package.json | 4 ++-- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 15 files changed, 32 insertions(+), 32 deletions(-) diff --git a/bun.lock b/bun.lock index 8abcb293f..27cc0f33a 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +66,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +90,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +111,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -151,7 +151,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -167,7 +167,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.43", + "version": "1.0.44", "bin": { "opencode": "./bin/opencode", }, @@ -245,7 +245,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -265,7 +265,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.43", + "version": "1.0.44", "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -276,7 +276,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -289,7 +289,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -319,7 +319,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 182062951..5e37e2140 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "1.0.43" + "version": "1.0.44" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index fa4b93501..a06f59295 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.43", + "version": "1.0.44", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 0182a4728..2c3b5da28 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.43", + "version": "1.0.44", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index d5e280f21..9019c7167 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.43", + "version": "1.0.44", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a2c1e3cb1..eecac67e3 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.43", + "version": "1.0.44", "description": "", "type": "module", "scripts": { diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index bdde47815..a6e6aa6f8 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The AI coding agent built for the terminal" -version = "v1.0.43" +version = "1.0.44" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/sst/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.44/download/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.44/download/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-linux-arm64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.44/download/opencode-linux-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-linux-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.44/download/opencode-linux-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/sst/opencode/releases/v1.0.43/download/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/v1.0.44/download/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 23c669cca..9eb02d591 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.43", + "version": "1.0.44", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 713e4651c..ae0d0b08d 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.43", + "version": "1.0.44", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 13e95bd9e..96f2c8146 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.43", + "version": "1.0.44", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 91d6eab70..1bf28cee8 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.43", + "version": "1.0.44", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} +} \ No newline at end of file diff --git a/packages/slack/package.json b/packages/slack/package.json index 1ee34d212..1e3f0c6bd 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.43", + "version": "1.0.44", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index f0caecce4..b2505a9d2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.43", + "version": "1.0.44", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index e6d032a44..9755c0f9d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.43", + "version": "1.0.44", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 55a7aa998..17afb3e4b 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.43", + "version": "1.0.44", "publisher": "sst-dev", "repository": { "type": "git", From 9885c716f387d35024eaed5c0aab0528189c1191 Mon Sep 17 00:00:00 2001 From: Jay V Date: Fri, 7 Nov 2025 14:40:48 -0500 Subject: [PATCH 140/218] ignore: lander use h1 tags for main headings on landing and zen pages --- packages/console/app/src/routes/index.css | 2 +- packages/console/app/src/routes/index.tsx | 2 +- packages/console/app/src/routes/zen/index.css | 2 +- packages/console/app/src/routes/zen/index.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/console/app/src/routes/index.css b/packages/console/app/src/routes/index.css index 496901339..e5101442f 100644 --- a/packages/console/app/src/routes/index.css +++ b/packages/console/app/src/routes/index.css @@ -479,7 +479,7 @@ body { border-bottom: 1px solid var(--color-border-weak); } - strong { + h1 { font-size: 28px; color: var(--color-text-strong); font-weight: 500; diff --git a/packages/console/app/src/routes/index.tsx b/packages/console/app/src/routes/index.tsx index bcfb22bc3..71b535f79 100644 --- a/packages/console/app/src/routes/index.tsx +++ b/packages/console/app/src/routes/index.tsx @@ -63,7 +63,7 @@ export default function Home() { > What’s new in {release()?.name ?? "the latest release"}
    - The AI coding agent built for the terminal +

    The AI coding agent built for the terminal

    OpenCode is fully open source, giving you control and freedom to use any provider, any model, and any editor. diff --git a/packages/console/app/src/routes/zen/index.css b/packages/console/app/src/routes/zen/index.css index 661517c43..fbdd15306 100644 --- a/packages/console/app/src/routes/zen/index.css +++ b/packages/console/app/src/routes/zen/index.css @@ -277,7 +277,7 @@ body { margin-bottom: 24px; } - strong { + h1 { font-size: 28px; color: var(--color-text-strong); font-weight: 500; diff --git a/packages/console/app/src/routes/zen/index.tsx b/packages/console/app/src/routes/zen/index.tsx index 92cc0a508..220f7a117 100644 --- a/packages/console/app/src/routes/zen/index.tsx +++ b/packages/console/app/src/routes/zen/index.tsx @@ -45,7 +45,7 @@ export default function Home() {

    zen logo light zen logo dark - Reliable optimized models for coding agents +

    Reliable optimized models for coding agents

    Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically for coding agents. No need to worry about inconsistent From 536d10e5abcf43a439ccb48fc8e1447d2cbe131e Mon Sep 17 00:00:00 2001 From: Jay V Date: Fri, 7 Nov 2025 14:59:11 -0500 Subject: [PATCH 141/218] ignore: lander add canonical urls and h1 tags to all landing pages --- packages/console/app/src/config.ts | 3 +++ packages/console/app/src/routes/brand/index.css | 2 +- packages/console/app/src/routes/brand/index.tsx | 6 ++++-- packages/console/app/src/routes/enterprise/index.css | 2 +- packages/console/app/src/routes/enterprise/index.tsx | 6 ++++-- packages/console/app/src/routes/index.tsx | 1 + packages/console/app/src/routes/zen/index.tsx | 2 ++ 7 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/console/app/src/config.ts b/packages/console/app/src/config.ts index 097764b87..40108e968 100644 --- a/packages/console/app/src/config.ts +++ b/packages/console/app/src/config.ts @@ -2,6 +2,9 @@ * Application-wide constants and configuration */ export const config = { + // Base URL + baseUrl: "https://opencode.ai", + // GitHub github: { repoUrl: "https://github.com/sst/opencode", diff --git a/packages/console/app/src/routes/brand/index.css b/packages/console/app/src/routes/brand/index.css index 058d150b6..d3c0d0523 100644 --- a/packages/console/app/src/routes/brand/index.css +++ b/packages/console/app/src/routes/brand/index.css @@ -264,7 +264,7 @@ [data-component="brand-content"] { padding: 4rem 5rem; - h2 { + h1 { font-size: 1.5rem; font-weight: 500; color: var(--color-text-strong); diff --git a/packages/console/app/src/routes/brand/index.tsx b/packages/console/app/src/routes/brand/index.tsx index 4570c73ca..72ea0f150 100644 --- a/packages/console/app/src/routes/brand/index.tsx +++ b/packages/console/app/src/routes/brand/index.tsx @@ -1,6 +1,7 @@ import "./index.css" -import { Title, Meta } from "@solidjs/meta" +import { Title, Meta, Link } from "@solidjs/meta" import { Header } from "~/component/header" +import { config } from "~/config" import { Footer } from "~/component/footer" import { Legal } from "~/component/legal" import previewLogoLight from "../../asset/brand/preview-opencode-logo-light.png" @@ -53,13 +54,14 @@ export default function Brand() { return (

    OpenCode | Brand +
    -

    Brand guidelines

    +

    Brand guidelines

    Resources and assets to help you work with the OpenCode brand.

    - - - -
    @@ -232,31 +215,29 @@ export default function Enterprise() {
    • - OpenCode Enterprise is for organizations that want to ensure that their code and - data never leaves their infrastructure. It can do this by using a centralized - config that integrates with your SSO and internal AI gateway. + OpenCode Enterprise is for organizations that want to ensure that their code and data never leaves + their infrastructure. It can do this by using a centralized config that integrates with your SSO and + internal AI gateway.
    • - Simply start with an internal trial with your team. OpenCode by default does not - store your code or context data, making it easy to get started. Then contact us to - discuss pricing and implementation options. + Simply start with an internal trial with your team. OpenCode by default does not store your code or + context data, making it easy to get started. Then contact us to discuss pricing and implementation + options.
    • - We offer per-seat enterprise pricing. If you have your own LLM gateway, we do not - charge for tokens used. For further details, contact us for a custom quote based - on your organization's needs. + We offer per-seat enterprise pricing. If you have your own LLM gateway, we do not charge for tokens + used. For further details, contact us for a custom quote based on your organization's needs.
    • - Yes. OpenCode does not store your code or context data. All processing happens - locally or through direct API calls to your AI provider. With central config and - SSO integration, your data remains secure within your organization's - infrastructure. + Yes. OpenCode does not store your code or context data. All processing happens locally or through + direct API calls to your AI provider. With central config and SSO integration, your data remains + secure within your organization's infrastructure.
    diff --git a/packages/console/app/src/routes/index.tsx b/packages/console/app/src/routes/index.tsx index ee138e140..8b8f44999 100644 --- a/packages/console/app/src/routes/index.tsx +++ b/packages/console/app/src/routes/index.tsx @@ -42,10 +42,7 @@ export default function Home() { return (
    - + OpenCode | The AI coding agent built for the terminal @@ -57,27 +54,17 @@ export default function Home() {
    - + What’s new in {release()?.name ?? "the latest release"}

    The AI coding agent built for the terminal

    - OpenCode is fully open source, giving you control and freedom to use any provider, - any model, and any editor. + OpenCode is fully open source, giving you control and freedom to use any provider, any model, and any + editor.

    Read docs - +

    What is OpenCode?

    -

    - OpenCode is an open source agent that helps you write and run code directly from the - terminal. -

    +

    OpenCode is an open source agent that helps you write and run code directly from the terminal.

    • @@ -197,8 +181,7 @@ export default function Home() {
    • [*]
      - Multi-session Start multiple agents in parallel on the same - project + Multi-session Start multiple agents in parallel on the same project
    • @@ -210,15 +193,13 @@ export default function Home() {
    • [*]
      - Claude Pro Log in with Anthropic to use your Claude Pro or Max - account + Claude Pro Log in with Anthropic to use your Claude Pro or Max account
    • [*]
      - Any model 75+ LLM providers through Models.dev, including local - models + Any model 75+ LLM providers through Models.dev, including local models
    • @@ -238,21 +219,15 @@ export default function Home() {

      With over {config.github.starsFormatted.full} GitHub stars,{" "} {config.stats.contributors} contributors, and almost{" "} - {config.stats.commits} commits, OpenCode is used and trusted by - over {config.stats.monthlyUsers} developers every month. + {config.stats.commits} commits, OpenCode is used and trusted by over{" "} + {config.stats.monthlyUsers} developers every month.

    - +
    -
    Fig 1.
    {config.github.starsFormatted.compact}{" "} - GitHub Stars +
    Fig 1.
    {config.github.starsFormatted.compact} GitHub Stars
    - + @@ -440,54 +408,12 @@ export default function Home() { - - - - - - + + + + + + @@ -496,55 +422,13 @@ export default function Home() { - - - + + + - - - + + + @@ -553,55 +437,13 @@ export default function Home() { - - - - + + + + - - + + @@ -610,47 +452,12 @@ export default function Home() { - - - + + + - - + + @@ -659,55 +466,13 @@ export default function Home() { - - - + + + - - - + + + @@ -716,55 +481,13 @@ export default function Home() { - - - - + + + + - - + + @@ -773,62 +496,13 @@ export default function Home() { - - - - - - - + + + + + + + @@ -837,55 +511,13 @@ export default function Home() { - + - - - - - + + + + + @@ -895,54 +527,12 @@ export default function Home() { - - - - - - + + + + + + @@ -951,62 +541,13 @@ export default function Home() { - - - - - - - + + + + + + + @@ -1015,54 +556,12 @@ export default function Home() { - - - - - - + + + + + + @@ -1071,31 +570,19 @@ export default function Home() { - +
    -
    Fig 2.
    {config.stats.contributors}{" "} - Contributors +
    Fig 2.
    {config.stats.contributors} Contributors
    - + @@ -1131,8 +618,7 @@ export default function Home() {
    -
    Fig 3.
    {config.stats.monthlyUsers} Monthly - Devs +
    Fig 3.
    {config.stats.monthlyUsers} Monthly Devs
    @@ -1146,9 +632,8 @@ export default function Home() { [*]

    - OpenCode does not store any of your code or context data, so that it can operate - in privacy sensitive environments. Learn more about{" "} - privacy. + OpenCode does not store any of your code or context data, so that it can operate in privacy sensitive + environments. Learn more about privacy.

    @@ -1161,9 +646,9 @@ export default function Home() {
    • - OpenCode is an open source agent that helps you write and run code directly from - the terminal. You can pair OpenCode with any AI model, and because it’s - terminal-based you can pair it with your preferred code editor. + OpenCode is an open source agent that helps you write and run code directly from the terminal. You can + pair OpenCode with any AI model, and because it’s terminal-based you can pair it with your preferred + code editor.
    • @@ -1173,32 +658,30 @@ export default function Home() {
    • - Not necessarily, but probably. You’ll need an AI subscription if you want to - connect OpenCode to a paid provider, although you can work with{" "} + Not necessarily, but probably. You’ll need an AI subscription if you want to connect OpenCode to a + paid provider, although you can work with{" "} local models {" "} - for free. While we encourage users to use Zen, OpenCode works - with all popular providers such as OpenAI, Anthropic, xAI etc. + for free. While we encourage users to use Zen, OpenCode works with all popular + providers such as OpenAI, Anthropic, xAI etc.
    • - Yes, for now. We are actively working on a desktop app. Join the waitlist for - early access. + Yes, for now. We are actively working on a desktop app. Join the waitlist for early access.
    • - OpenCode is 100% free to use. Any additional costs will come from your - subscription to a model provider. While OpenCode works with any model provider, we - recommend using Zen. + OpenCode is 100% free to use. Any additional costs will come from your subscription to a model + provider. While OpenCode works with any model provider, we recommend using Zen.
    • - Your data and information is only stored when you create sharable links in - OpenCode. Learn more about share pages. + Your data and information is only stored when you create sharable links in OpenCode. Learn more about{" "} + share pages.
    • @@ -1211,8 +694,8 @@ export default function Home() { MIT License - , meaning anyone can use, modify, or contribute to its development. Anyone from - the community can file issues, submit pull requests, and extend functionality. + , meaning anyone can use, modify, or contribute to its development. Anyone from the community can file + issues, submit pull requests, and extend functionality.
    @@ -1222,19 +705,13 @@ export default function Home() {
    Access reliable optimized models for coding agents

    - Zen gives you access to a handpicked set of AI models that OpenCode has tested and - benchmarked specifically for coding agents. No need to worry about inconsistent - performance and quality across providers, use validated models that work. + Zen gives you access to a handpicked set of AI models that OpenCode has tested and benchmarked + specifically for coding agents. No need to worry about inconsistent performance and quality across + providers, use validated models that work.

    - +
    - - + +
    - +
    - +
    - + Learn about Zen - + { const customer = await Billing.get() - if (customer?.customerID && customer.customerID !== customerID) - throw new Error("Customer ID mismatch") + if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch") // set customer metadata if (!customer?.customerID) { @@ -72,8 +70,7 @@ export async function POST(input: APIEvent) { expand: ["payment_method"], }) const paymentMethod = paymentIntent.payment_method - if (!paymentMethod || typeof paymentMethod === "string") - throw new Error("Payment method not expanded") + if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded") await Database.transaction(async (tx) => { await tx @@ -128,12 +125,7 @@ export async function POST(input: APIEvent) { amount: PaymentTable.amount, }) .from(PaymentTable) - .where( - and( - eq(PaymentTable.paymentID, paymentIntentID), - eq(PaymentTable.workspaceID, workspaceID), - ), - ) + .where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID))) .then((rows) => rows[0]?.amount), ) if (!amount) throw new Error("Payment not found") @@ -144,12 +136,7 @@ export async function POST(input: APIEvent) { .set({ timeRefunded: new Date(body.created * 1000), }) - .where( - and( - eq(PaymentTable.paymentID, paymentIntentID), - eq(PaymentTable.workspaceID, workspaceID), - ), - ) + .where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID))) await tx .update(BillingTable) diff --git a/packages/console/app/src/routes/temp.tsx b/packages/console/app/src/routes/temp.tsx index 59987e4d0..b0aef00e7 100644 --- a/packages/console/app/src/routes/temp.tsx +++ b/packages/console/app/src/routes/temp.tsx @@ -79,19 +79,17 @@ export default function Home() { LSP enabled Automatically loads the right LSPs for the LLM
  • - opencode zen A curated list of models{" "} - provided by opencode + opencode zen A curated list of models provided by opencode{" "} +
  • Multi-session Start multiple agents in parallel on the same project
  • - Shareable links Share a link to any sessions for reference or to - debug + Shareable links Share a link to any sessions for reference or to debug
  • - Claude Pro Log in with Anthropic to use your Claude Pro or Max - account + Claude Pro Log in with Anthropic to use your Claude Pro or Max account
  • Use any model Supports 75+ LLM providers through{" "} diff --git a/packages/console/app/src/routes/workspace-picker.tsx b/packages/console/app/src/routes/workspace-picker.tsx index 4e218227c..fa8cf1d21 100644 --- a/packages/console/app/src/routes/workspace-picker.tsx +++ b/packages/console/app/src/routes/workspace-picker.tsx @@ -85,10 +85,7 @@ export function WorkspacePicker() { {(workspace) => ( - handleSelectWorkspace(workspace.id)} - > + handleSelectWorkspace(workspace.id)}> {workspace.name || workspace.slug} )} @@ -98,11 +95,7 @@ export function WorkspacePicker() { - setStore("showForm", false)} - title="Create New Workspace" - > + setStore("showForm", false)} title="Create New Workspace">

    Billing

    - Manage payments methods. Contact us if you have any - questions. + Manage payments methods. Contact us if you have any questions.

    @@ -164,32 +163,20 @@ export function BillingSection() { placeholder="Enter amount" />
    -
    - + {(err: any) =>
    {err()}
    }
  • @@ -210,10 +197,7 @@ export function BillingSection() {
    - ----} - > + ----}> •••• {billingInfo()?.paymentMethodLast4} @@ -241,9 +225,7 @@ export function BillingSection() { disabled={checkoutSubmission.pending || store.checkoutRedirecting} onClick={onClickCheckout} > - {checkoutSubmission.pending || store.checkoutRedirecting - ? "Loading..." - : "Enable Billing"} + {checkoutSubmission.pending || store.checkoutRedirecting ? "Loading..." : "Enable Billing"}
    diff --git a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx index e6461ac83..77c017964 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/monthly-limit-section.tsx @@ -104,13 +104,9 @@ export function MonthlyLimitSection() {
    - No usage limit set.

    } - > + No usage limit set.

    }>

    - Current usage for{" "} - {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $ + Current usage for {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $ {(() => { const dateLastUsed = billingInfo()?.timeMonthlyUsageUpdated if (!dateLastUsed) return "0" diff --git a/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx index a7218546d..0fb2a0df6 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/payment-section.tsx @@ -89,10 +89,7 @@ export function PaymentSection() { } > diff --git a/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx index e1c2c00cf..565981c7f 100644 --- a/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/keys/key-section.tsx @@ -146,20 +146,14 @@ export function KeySection() { title="Copy API key" > {key.keyDisplay} - } - > + }> {key.email} - + {key.timeUsed ? formatDateForTable(key.timeUsed) : "-"} diff --git a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx index 4b2a12fdc..5aa1b969e 100644 --- a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx @@ -85,12 +85,7 @@ const updateMember = action(async (form: FormData) => { ) }, "member.update") -function MemberRow(props: { - member: any - workspaceID: string - actorID: string - actorRole: string -}) { +function MemberRow(props: { member: any; workspaceID: string; actorID: string; actorRole: string }) { const submission = useSubmission(updateMember) const isCurrentUser = () => props.actorID === props.member.id const isAdmin = () => props.actorRole === "admin" diff --git a/packages/console/app/src/routes/workspace/[id]/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx index 223d69fc8..7a1980ebe 100644 --- a/packages/console/app/src/routes/workspace/[id]/model-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx @@ -5,15 +5,7 @@ import { withActor } from "~/context/auth.withActor" import { ZenData } from "@opencode-ai/console-core/model.js" import styles from "./model-section.module.css" import { querySessionInfo } from "../common" -import { - IconAlibaba, - IconAnthropic, - IconMoonshotAI, - IconOpenAI, - IconStealth, - IconXai, - IconZai, -} from "~/component/icon" +import { IconAlibaba, IconAnthropic, IconMoonshotAI, IconOpenAI, IconStealth, IconXai, IconZai } from "~/component/icon" const getModelLab = (modelId: string) => { if (modelId.startsWith("claude")) return "Anthropic" @@ -76,8 +68,7 @@ export function ModelSection() {

    Models

    - Manage which models workspace members can access.{" "} - Learn more. + Manage which models workspace members can access. Learn more.

    diff --git a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx index 7b949c661..65edc6847 100644 --- a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx @@ -43,24 +43,15 @@ export function NewUserSection() {

    Tested & Verified Models

    -

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

    +

    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. -

    +

    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. -

    +

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

    diff --git a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx index 67314fbdc..5419ed7fb 100644 --- a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx @@ -55,10 +55,7 @@ const listProviders = query(async (workspaceID: string) => { function ProviderRow(props: { provider: Provider }) { const params = useParams() const providers = createAsync(() => listProviders(params.id)) - const saveSubmission = useSubmission( - saveProvider, - ([fd]) => fd.get("provider")?.toString() === props.provider.key, - ) + const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key) const removeSubmission = useSubmission( removeProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key, @@ -94,16 +91,9 @@ function ProviderRow(props: { provider: Provider }) { {providerData() ? maskCredentials(providerData()!.credentials) : "-"} - } + fallback={{providerData() ? maskCredentials(providerData()!.credentials) : "-"}} > - +
    (input = r)} diff --git a/packages/console/app/src/routes/workspace/common.tsx b/packages/console/app/src/routes/workspace/common.tsx index 5b638192c..a6eaaeb1e 100644 --- a/packages/console/app/src/routes/workspace/common.tsx +++ b/packages/console/app/src/routes/workspace/common.tsx @@ -67,10 +67,7 @@ export const querySessionInfo = query(async (workspaceID: string) => { return withActor(() => { return { isAdmin: Actor.userRole() === "admin", - isBeta: - Resource.App.stage === "production" - ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" - : true, + isBeta: Resource.App.stage === "production" ? workspaceID === "wrk_01K46JDFR0E75SG2Q8K172KF3Y" : true, } }, workspaceID) }, "session.get") diff --git a/packages/console/app/src/routes/zen/index.tsx b/packages/console/app/src/routes/zen/index.tsx index a096b52d7..4eab4dcb9 100644 --- a/packages/console/app/src/routes/zen/index.tsx +++ b/packages/console/app/src/routes/zen/index.tsx @@ -29,10 +29,7 @@ export default function Home() { createAsync(() => checkLoggedIn()) return (
    - + OpenCode Zen | A curated set of reliable optimized models for coding agents @@ -49,19 +46,13 @@ export default function Home() { zen logo dark

    Reliable optimized models for coding agents

    - Zen gives you access to a curated set of AI models that OpenCode has tested and - benchmarked specifically for coding agents. No need to worry about inconsistent - performance and quality, use validated models that work. + Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically + for coding agents. No need to worry about inconsistent performance and quality, use validated models + that work.

    - +
    - - + +
    - +
    - +
    - + Get started with Zen - +

    - Add $20 Pay as you go balance{" "} - (+$1.23 card processing fee) + Add $20 Pay as you go balance (+$1.23 card processing fee)

    Use with any agent. Set monthly spend limits. Cancel any time.

    -
    @@ -193,8 +142,8 @@ export default function Home() {

    What problem is Zen solving?

    - There are so many models available, but only a few work well with coding agents. - Most providers configure them differently with varying results. + There are so many models available, but only a few work well with coding agents. Most providers + configure them differently with varying results.

    We're fixing this for everyone, not just OpenCode users.

    @@ -229,15 +178,14 @@ export default function Home() {
  • [2]
    - Use Zen with transparent pricing -{" "} - pay per request with zero markups + Use Zen with transparent pricing - pay per request{" "} + with zero markups
  • [3]
    - Auto-top up - when your balance reaches $5 we’ll automatically - add $20 + Auto-top up - when your balance reaches $5 we’ll automatically add $20
  • @@ -249,9 +197,8 @@ export default function Home() {
    [*]

    - All Zen models are hosted in the US. Providers follow a zero-retention policy and - do not use your data for model training, with the{" "} - following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data + for model training, with the following exceptions.

    @@ -306,8 +253,7 @@ export default function Home() { ex-Head of Design, Laravel
    - With @OpenCode Zen I know all the models are tested and perfect for - coding agents. + With @OpenCode Zen I know all the models are tested and perfect for coding agents.
    @@ -331,44 +277,38 @@ export default function Home() {
    • - Zen is a curated set of AI models tested and benchmarked for coding agents created - by the team behind OpenCode. + Zen is a curated set of AI models tested and benchmarked for coding agents created by the team behind + OpenCode.
    • - Zen only provides models that have been specifically tested and benchmarked for - coding agents. You wouldn’t use a butter knife to cut steak, don’t use poor models - for coding. + Zen only provides models that have been specifically tested and benchmarked for coding agents. You + wouldn’t use a butter knife to cut steak, don’t use poor models for coding.
    • - Zen is not for profit. Zen passes through the costs from the model providers to - you. The higher Zen’s usage the more OpenCode can negotiate better rates and pass - those to you. + Zen is not for profit. Zen passes through the costs from the model providers to you. The higher Zen’s + usage the more OpenCode can negotiate better rates and pass those to you.
    • - Zen charges per request with zero markups, so you - pay exactly what the model provider charges. Your total cost depends on usage, and - you can set monthly spend limits in your account. To cover - costs, OpenCode adds only a small payment processing fee of $1.23 per $20 balance - top-up. + Zen charges per request with zero markups, so you pay exactly what + the model provider charges. Your total cost depends on usage, and you can set monthly spend limits in + your account. To cover costs, OpenCode adds only a small payment processing fee of + $1.23 per $20 balance top-up.
    • - All Zen models are hosted in the US. Providers follow a zero-retention policy and - do not use your data for model training, with the{" "} - following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data + for model training, with the following exceptions.
    • - - Yes, you can set monthly spending limits in your account. - + Yes, you can set monthly spending limits in your account.
    • @@ -377,8 +317,8 @@ export default function Home() {
    • - While Zen works great with OpenCode, you can use Zen with any agent. Follow the - setup instructions in your preferred coding agent. + While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in + your preferred coding agent.
    diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index deab7ded2..194a7c71e 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -13,11 +13,7 @@ import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js" import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js" import { logger } from "./logger" import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError } from "./error" -import { - createBodyConverter, - createStreamPartConverter, - createResponseConverter, -} from "./provider/provider" +import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider" import { anthropicHelper } from "./provider/anthropic" import { openaiHelper } from "./provider/openai" import { oaCompatHelper } from "./provider/openai-compatible" @@ -46,11 +42,7 @@ export async function handler( }) const zenData = ZenData.list() const modelInfo = validateModel(zenData, body.model) - const providerInfo = selectProvider( - zenData, - modelInfo, - input.request.headers.get("x-real-ip") ?? "", - ) + const providerInfo = selectProvider(zenData, modelInfo, input.request.headers.get("x-real-ip") ?? "") const authInfo = await authenticate(modelInfo, providerInfo) validateBilling(modelInfo, authInfo) validateModelSettings(authInfo) @@ -229,11 +221,7 @@ export async function handler( return { id: modelId, ...modelData } } - function selectProvider( - zenData: ZenData, - model: Awaited>, - ip: string, - ) { + function selectProvider(zenData: ZenData, model: Awaited>, ip: string) { const providers = model.providers .filter((provider) => !provider.disabled) .flatMap((provider) => Array(provider.weight ?? 1).fill(provider)) @@ -252,11 +240,7 @@ export async function handler( return { ...provider, ...zenData.providers[provider.id], - ...(format === "anthropic" - ? anthropicHelper - : format === "openai" - ? openaiHelper - : oaCompatHelper), + ...(format === "anthropic" ? anthropicHelper : format === "openai" ? openaiHelper : oaCompatHelper), } } @@ -297,20 +281,11 @@ export async function handler( .from(KeyTable) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) .innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID)) - .innerJoin( - UserTable, - and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)), - ) - .leftJoin( - ModelTable, - and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, model.id)), - ) + .innerJoin(UserTable, and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID))) + .leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, model.id))) .leftJoin( ProviderTable, - and( - eq(ProviderTable.workspaceID, KeyTable.workspaceID), - eq(ProviderTable.provider, providerInfo.id), - ), + and(eq(ProviderTable.workspaceID, KeyTable.workspaceID), eq(ProviderTable.provider, providerInfo.id)), ) .where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted))) .then((rows) => rows[0]), @@ -401,19 +376,12 @@ export async function handler( providerInfo: Awaited>, usage: any, ) { - const { - inputTokens, - outputTokens, - reasoningTokens, - cacheReadTokens, - cacheWrite5mTokens, - cacheWrite1hTokens, - } = providerInfo.normalizeUsage(usage) + const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } = + providerInfo.normalizeUsage(usage) const modelCost = modelInfo.cost200K && - inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > - 200_000 + inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000 ? modelInfo.cost200K : modelInfo.cost @@ -464,8 +432,7 @@ export async function handler( if (!authInfo) return - const cost = - authInfo.isFree || authInfo.provider?.credentials ? 0 : centsToMicroCents(totalCostInCent) + const cost = authInfo.isFree || authInfo.provider?.credentials ? 0 : centsToMicroCents(totalCostInCent) await Database.transaction(async (tx) => { await tx.insert(UsageTable).values({ workspaceID: authInfo.workspaceID, @@ -505,9 +472,7 @@ export async function handler( `, timeMonthlyUsageUpdated: sql`now()`, }) - .where( - and(eq(UserTable.workspaceID, authInfo.workspaceID), eq(UserTable.id, authInfo.user.id)), - ) + .where(and(eq(UserTable.workspaceID, authInfo.workspaceID), eq(UserTable.id, authInfo.user.id))) }) await Database.use((tx) => @@ -537,10 +502,7 @@ export async function handler( BillingTable.balance, centsToMicroCents((authInfo.billing.reloadTrigger ?? Billing.RELOAD_TRIGGER) * 100), ), - or( - isNull(BillingTable.timeReloadLockedTill), - lt(BillingTable.timeReloadLockedTill, sql`now()`), - ), + or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)), ), ), ) diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.ts index f4e8dc44d..d8d1cd741 100644 --- a/packages/console/app/src/routes/zen/util/provider/anthropic.ts +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.ts @@ -123,15 +123,12 @@ export function fromAnthropicRequest(body: any): CommonRequest { if ((p as any).type === "tool_result") { const id = (p as any).tool_use_id const content = - typeof (p as any).content === "string" - ? (p as any).content - : JSON.stringify((p as any).content) + typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } if (partsOut.length > 0) { - if (partsOut.length === 1 && partsOut[0].type === "text") - msgs.push({ role: "user", content: partsOut[0].text }) + if (partsOut.length === 1 && partsOut[0].type === "text") msgs.push({ role: "user", content: partsOut[0].text }) else msgs.push({ role: "user", content: partsOut }) } continue @@ -143,8 +140,7 @@ export function fromAnthropicRequest(body: any): CommonRequest { const tcs: any[] = [] for (const p of partsIn) { if (!p || !(p as any).type) continue - if ((p as any).type === "text" && typeof (p as any).text === "string") - texts.push((p as any).text) + if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text) if ((p as any).type === "tool_use") { const name = (p as any).name const id = (p as any).id @@ -214,9 +210,7 @@ export function fromAnthropicRequest(body: any): CommonRequest { export function toAnthropicRequest(body: CommonRequest) { if (!body || typeof body !== "object") return body - const sysIn = Array.isArray(body.messages) - ? body.messages.filter((m: any) => m && m.role === "system") - : [] + const sysIn = Array.isArray(body.messages) ? body.messages.filter((m: any) => m && m.role === "system") : [] let ccCount = 0 const cc = () => { ccCount++ @@ -367,9 +361,7 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const idIn = (resp as any).id const id = - typeof idIn === "string" - ? idIn.replace(/^msg_/, "chatcmpl_") - : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -412,9 +404,7 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined const cached = - typeof (u as any).cache_read_input_tokens === "number" - ? (u as any).cache_read_input_tokens - : undefined + typeof (u as any).cache_read_input_tokens === "number" ? (u as any).cache_read_input_tokens : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, @@ -591,9 +581,7 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { prompt_tokens: u.input_tokens, completion_tokens: u.output_tokens, total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0), - ...(u.cache_read_input_tokens - ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } - : {}), + ...(u.cache_read_input_tokens ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } : {}), } } diff --git a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts index d69985728..8a9170ef1 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts @@ -57,8 +57,7 @@ export const oaCompatHelper = { const inputTokens = usage.prompt_tokens ?? 0 const outputTokens = usage.completion_tokens ?? 0 const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? undefined - const cacheReadTokens = - usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined + const cacheReadTokens = usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined return { inputTokens: inputTokens - (cacheReadTokens ?? 0), outputTokens, @@ -80,8 +79,7 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) - msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) continue } @@ -92,12 +90,10 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") - parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url }) } - if (parts.length === 1 && parts[0].type === "text") - msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -141,8 +137,7 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (p.type === "image_url" && p.image_url) return { type: "image_url", image_url: p.image_url } const s = (p as any).source if (!s || typeof s !== "object") return undefined - if (s.type === "url" && typeof s.url === "string") - return { type: "image_url", image_url: { url: s.url } } + if (s.type === "url" && typeof s.url === "string") return { type: "image_url", image_url: { url: s.url } } if (s.type === "base64" && typeof s.media_type === "string" && typeof s.data === "string") return { type: "image_url", image_url: { url: `data:${s.media_type};base64,${s.data}` } } return undefined @@ -152,8 +147,7 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) - msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) continue } @@ -166,13 +160,11 @@ export function toOaCompatibleRequest(body: CommonRequest) { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") - parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) const ip = toImg(p) if (ip) parts.push(ip) } - if (parts.length === 1 && parts[0].type === "text") - msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -325,9 +317,7 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const idIn = (resp as any).id const id = - typeof idIn === "string" - ? idIn.replace(/^msg_/, "chatcmpl_") - : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -369,8 +359,7 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const pt = typeof u.input_tokens === "number" ? u.input_tokens : undefined const ct = typeof u.output_tokens === "number" ? u.output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined - const cached = - typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined + const cached = typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, diff --git a/packages/console/app/src/routes/zen/util/provider/openai.ts b/packages/console/app/src/routes/zen/util/provider/openai.ts index fa0776b7a..e79e83579 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai.ts @@ -86,11 +86,7 @@ export function fromOpenaiRequest(body: any): CommonRequest { const msgs: any[] = [] - const inMsgs = Array.isArray(body.input) - ? body.input - : Array.isArray(body.messages) - ? body.messages - : [] + const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : [] for (const m of inMsgs) { if (!m) continue @@ -103,9 +99,7 @@ export function fromOpenaiRequest(body: any): CommonRequest { const args = typeof a === "string" ? a : JSON.stringify(a ?? {}) msgs.push({ role: "assistant", - tool_calls: [ - { id: (m as any).id, type: "function", function: { name, arguments: args } }, - ], + tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }], }) } if ((m as any).type === "function_call_output") { @@ -122,8 +116,7 @@ export function fromOpenaiRequest(body: any): CommonRequest { if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c }) if (Array.isArray(c)) { const t = c.find((p: any) => p && typeof p.text === "string") - if (t && typeof t.text === "string" && t.text.length > 0) - msgs.push({ role: "system", content: t.text }) + if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text }) } continue } @@ -136,24 +129,18 @@ export function fromOpenaiRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of c) { if (!p || !(p as any).type) continue - if ( - ((p as any).type === "text" || (p as any).type === "input_text") && - typeof (p as any).text === "string" - ) + if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string") parts.push({ type: "text", text: (p as any).text }) const ip = toImg(p) if (ip) parts.push(ip) if ((p as any).type === "tool_result") { const id = (p as any).tool_call_id const content = - typeof (p as any).content === "string" - ? (p as any).content - : JSON.stringify((p as any).content) + typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } - if (parts.length === 1 && parts[0].type === "text") - msgs.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgs.push({ role: "user", content: parts }) } continue @@ -280,10 +267,7 @@ export function toOpenaiRequest(body: CommonRequest) { } if ((m as any).role === "tool") { - const out = - typeof (m as any).content === "string" - ? (m as any).content - : JSON.stringify((m as any).content) + const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content) input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out }) continue } @@ -351,9 +335,7 @@ export function fromOpenaiResponse(resp: any): CommonResponse { const idIn = (r as any).id const id = - typeof idIn === "string" - ? idIn.replace(/^resp_/, "chatcmpl_") - : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (r as any).model ?? (resp as any).model const out = Array.isArray((r as any).output) ? (r as any).output : [] @@ -480,9 +462,7 @@ export function toOpenaiResponse(resp: CommonResponse) { })() return { - id: - (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? - `resp_${Math.random().toString(36).slice(2)}`, + id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`, object: "response", model: (resp as any).model, output: outputItems, diff --git a/packages/console/app/src/routes/zen/v1/models.ts b/packages/console/app/src/routes/zen/v1/models.ts index 3d0c31470..ee2b3ab54 100644 --- a/packages/console/app/src/routes/zen/v1/models.ts +++ b/packages/console/app/src/routes/zen/v1/models.ts @@ -50,10 +50,7 @@ export async function GET(input: APIEvent) { }) .from(KeyTable) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) - .leftJoin( - ModelTable, - and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)), - ) + .leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted))) .where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted))) .then((rows) => rows.map((row) => row.model)), ) diff --git a/packages/console/app/src/style/token/font.css b/packages/console/app/src/style/token/font.css index dc0d298f1..67143e662 100644 --- a/packages/console/app/src/style/token/font.css +++ b/packages/console/app/src/style/token/font.css @@ -15,7 +15,6 @@ body { --font-size-9xl: 8rem; --font-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", - "Courier New", monospace; + "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --font-sans: var(--font-mono); } diff --git a/packages/console/core/script/lookup-user.ts b/packages/console/core/script/lookup-user.ts index af9bcc3a1..1ae18c4dd 100644 --- a/packages/console/core/script/lookup-user.ts +++ b/packages/console/core/script/lookup-user.ts @@ -8,22 +8,15 @@ if (!email) { process.exit(1) } -const authData = await printTable("Auth", (tx) => - tx.select().from(AuthTable).where(eq(AuthTable.subject, email)), -) +const authData = await printTable("Auth", (tx) => tx.select().from(AuthTable).where(eq(AuthTable.subject, email))) if (authData.length === 0) { console.error("User not found") process.exit(1) } -await printTable("Auth", (tx) => - tx.select().from(AuthTable).where(eq(AuthTable.accountID, authData[0].accountID)), -) +await printTable("Auth", (tx) => tx.select().from(AuthTable).where(eq(AuthTable.accountID, authData[0].accountID))) -function printTable( - title: string, - callback: (tx: Database.TxOrDb) => Promise, -): Promise { +function printTable(title: string, callback: (tx: Database.TxOrDb) => Promise): Promise { return Database.use(async (tx) => { const data = await callback(tx) console.log(`== ${title} ==`) diff --git a/packages/console/core/script/reset-db.ts b/packages/console/core/script/reset-db.ts index bd00e1962..02d498901 100644 --- a/packages/console/core/script/reset-db.ts +++ b/packages/console/core/script/reset-db.ts @@ -8,14 +8,6 @@ import { KeyTable } from "../src/schema/key.sql.js" if (Resource.App.stage !== "frank") throw new Error("This script is only for frank") -for (const table of [ - AccountTable, - BillingTable, - KeyTable, - PaymentTable, - UsageTable, - UserTable, - WorkspaceTable, -]) { +for (const table of [AccountTable, BillingTable, KeyTable, PaymentTable, UsageTable, UserTable, WorkspaceTable]) { await Database.use((tx) => tx.delete(table)) } diff --git a/packages/console/core/src/aws.ts b/packages/console/core/src/aws.ts index ce4a20f44..e87ada6ef 100644 --- a/packages/console/core/src/aws.ts +++ b/packages/console/core/src/aws.ts @@ -24,40 +24,37 @@ export namespace AWS { body: z.string(), }), async (input) => { - const res = await createClient().fetch( - "https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", - { - method: "POST", - headers: { - "X-Amz-Target": "SES.SendEmail", - "Content-Type": "application/json", + const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", { + method: "POST", + headers: { + "X-Amz-Target": "SES.SendEmail", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + FromEmailAddress: `OpenCode Zen `, + Destination: { + ToAddresses: [input.to], }, - body: JSON.stringify({ - FromEmailAddress: `OpenCode Zen `, - Destination: { - ToAddresses: [input.to], - }, - Content: { - Simple: { - Subject: { + Content: { + Simple: { + Subject: { + Charset: "UTF-8", + Data: input.subject, + }, + Body: { + Text: { Charset: "UTF-8", - Data: input.subject, + Data: input.body, }, - Body: { - Text: { - Charset: "UTF-8", - Data: input.body, - }, - Html: { - Charset: "UTF-8", - Data: input.body, - }, + Html: { + Charset: "UTF-8", + Data: input.body, }, }, }, - }), - }, - ) + }, + }), + }) if (!res.ok) { throw new Error(`Failed to send email: ${res.statusText}`) } diff --git a/packages/console/core/src/drizzle/index.ts b/packages/console/core/src/drizzle/index.ts index 8b37b1f9c..f0f065de4 100644 --- a/packages/console/core/src/drizzle/index.ts +++ b/packages/console/core/src/drizzle/index.ts @@ -5,10 +5,7 @@ import { Client } from "@planetscale/database" import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core" import type { ExtractTablesWithRelations } from "drizzle-orm" -import type { - PlanetScalePreparedQueryHKT, - PlanetscaleQueryResultHKT, -} from "drizzle-orm/planetscale-serverless" +import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless" import { Context } from "../context" import { memo } from "../util/memo" @@ -70,10 +67,7 @@ export namespace Database { } } - export async function transaction( - callback: (tx: TxOrDb) => Promise, - config?: MySqlTransactionConfig, - ) { + export async function transaction(callback: (tx: TxOrDb) => Promise, config?: MySqlTransactionConfig) { try { const { tx } = TransactionContext.use() return callback(tx) diff --git a/packages/console/core/src/key.ts b/packages/console/core/src/key.ts index 6396fd0b8..688f19b3d 100644 --- a/packages/console/core/src/key.ts +++ b/packages/console/core/src/key.ts @@ -20,14 +20,8 @@ export namespace Key { email: AuthTable.subject, }) .from(KeyTable) - .innerJoin( - UserTable, - and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)), - ) - .innerJoin( - AuthTable, - and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), - ) + .innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID))) + .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) .where( and( ...[ diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts index 30cc15e45..ea719534d 100644 --- a/packages/console/core/src/model.ts +++ b/packages/console/core/src/model.ts @@ -60,9 +60,7 @@ export namespace Model { export const enable = fn(z.object({ model: z.string() }), ({ model }) => { Actor.assertAdmin() return Database.use((db) => - db - .delete(ModelTable) - .where(and(eq(ModelTable.workspaceID, Actor.workspace()), eq(ModelTable.model, model))), + db.delete(ModelTable).where(and(eq(ModelTable.workspaceID, Actor.workspace()), eq(ModelTable.model, model))), ) }) diff --git a/packages/console/core/src/provider.ts b/packages/console/core/src/provider.ts index 0af642f71..cf2040b59 100644 --- a/packages/console/core/src/provider.ts +++ b/packages/console/core/src/provider.ts @@ -11,9 +11,7 @@ export namespace Provider { tx .select() .from(ProviderTable) - .where( - and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted)), - ), + .where(and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted))), ), ) @@ -52,12 +50,7 @@ export namespace Provider { return Database.transaction((tx) => tx .delete(ProviderTable) - .where( - and( - eq(ProviderTable.provider, provider), - eq(ProviderTable.workspaceID, Actor.workspace()), - ), - ), + .where(and(eq(ProviderTable.provider, provider), eq(ProviderTable.workspaceID, Actor.workspace()))), ) }, ) diff --git a/packages/console/core/src/schema/auth.sql.ts b/packages/console/core/src/schema/auth.sql.ts index d55e605aa..27c926d6f 100644 --- a/packages/console/core/src/schema/auth.sql.ts +++ b/packages/console/core/src/schema/auth.sql.ts @@ -1,11 +1,4 @@ -import { - index, - mysqlEnum, - mysqlTable, - primaryKey, - uniqueIndex, - varchar, -} from "drizzle-orm/mysql-core" +import { index, mysqlEnum, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core" import { id, timestamps, ulid } from "../drizzle/types" export const AuthProvider = ["email", "github", "google"] as const diff --git a/packages/console/core/src/schema/model.sql.ts b/packages/console/core/src/schema/model.sql.ts index 343b0c4f3..1c032aad2 100644 --- a/packages/console/core/src/schema/model.sql.ts +++ b/packages/console/core/src/schema/model.sql.ts @@ -9,8 +9,5 @@ export const ModelTable = mysqlTable( ...timestamps, model: varchar("model", { length: 64 }).notNull(), }, - (table) => [ - ...workspaceIndexes(table), - uniqueIndex("model_workspace_model").on(table.workspaceID, table.model), - ], + (table) => [...workspaceIndexes(table), uniqueIndex("model_workspace_model").on(table.workspaceID, table.model)], ) diff --git a/packages/console/core/src/schema/provider.sql.ts b/packages/console/core/src/schema/provider.sql.ts index 04d11e2e5..11be5b4d7 100644 --- a/packages/console/core/src/schema/provider.sql.ts +++ b/packages/console/core/src/schema/provider.sql.ts @@ -10,8 +10,5 @@ export const ProviderTable = mysqlTable( provider: varchar("provider", { length: 64 }).notNull(), credentials: text("credentials").notNull(), }, - (table) => [ - ...workspaceIndexes(table), - uniqueIndex("workspace_provider").on(table.workspaceID, table.provider), - ], + (table) => [...workspaceIndexes(table), uniqueIndex("workspace_provider").on(table.workspaceID, table.provider)], ) diff --git a/packages/console/core/src/schema/user.sql.ts b/packages/console/core/src/schema/user.sql.ts index ce5b6c53e..7fd7f5e1e 100644 --- a/packages/console/core/src/schema/user.sql.ts +++ b/packages/console/core/src/schema/user.sql.ts @@ -1,12 +1,4 @@ -import { - mysqlTable, - uniqueIndex, - varchar, - int, - mysqlEnum, - index, - bigint, -} from "drizzle-orm/mysql-core" +import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index, bigint } from "drizzle-orm/mysql-core" import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types" import { workspaceIndexes } from "./workspace.sql" diff --git a/packages/console/core/src/user.ts b/packages/console/core/src/user.ts index cbb1ac827..8b7a96f44 100644 --- a/packages/console/core/src/user.ts +++ b/packages/console/core/src/user.ts @@ -26,10 +26,7 @@ export namespace User { authEmail: AuthTable.subject, }) .from(UserTable) - .leftJoin( - AuthTable, - and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), - ) + .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) .where(and(eq(UserTable.workspaceID, Actor.workspace()), isNull(UserTable.timeDeleted))), ), ) @@ -39,13 +36,7 @@ export namespace User { tx .select() .from(UserTable) - .where( - and( - eq(UserTable.workspaceID, Actor.workspace()), - eq(UserTable.id, id), - isNull(UserTable.timeDeleted), - ), - ) + .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id), isNull(UserTable.timeDeleted))) .then((rows) => rows[0]), ), ) @@ -57,10 +48,7 @@ export namespace User { email: AuthTable.subject, }) .from(UserTable) - .leftJoin( - AuthTable, - and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), - ) + .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id))) .then((rows) => rows[0]?.email), ), @@ -142,16 +130,10 @@ export namespace User { workspaceName: WorkspaceTable.name, }) .from(UserTable) - .innerJoin( - AuthTable, - and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), - ) + .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, workspaceID)) .where( - and( - eq(UserTable.workspaceID, workspaceID), - eq(UserTable.id, Actor.assert("user").properties.userID), - ), + and(eq(UserTable.workspaceID, workspaceID), eq(UserTable.id, Actor.assert("user").properties.userID)), ) .then((rows) => rows[0]), ) diff --git a/packages/console/mail/emails/templates/InviteEmail.tsx b/packages/console/mail/emails/templates/InviteEmail.tsx index e94eb564c..baf0d383f 100644 --- a/packages/console/mail/emails/templates/InviteEmail.tsx +++ b/packages/console/mail/emails/templates/InviteEmail.tsx @@ -1,18 +1,6 @@ // @ts-nocheck import React from "react" -import { - Img, - Row, - Html, - Link, - Body, - Head, - Button, - Column, - Preview, - Section, - Container, -} from "@jsx-email/all" +import { Img, Row, Html, Link, Body, Head, Button, Column, Preview, Section, Container } from "@jsx-email/all" import { Text, Fonts, Title, A, Span } from "../components" import { unit, @@ -64,8 +52,8 @@ export const InviteEmail = ({
    Join your team's OpenCode workspace - You have been invited by {inviter} to join - the {workspaceName} workspace on OpenCode. + You have been invited by {inviter} to join the{" "} + {workspaceName} workspace on OpenCode.
    @@ -73,12 +61,7 @@ export const InviteEmail = ({ diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts index 3475f5d72..6f00dae9a 100644 --- a/packages/function/src/api.ts +++ b/packages/function/src/api.ts @@ -268,11 +268,7 @@ export default new Hono<{ Bindings: Env }>() // Verify permissions const userClient = new Octokit({ auth: token }) const { data: repoData } = await userClient.repos.get({ owner, repo }) - if ( - !repoData.permissions.admin && - !repoData.permissions.push && - !repoData.permissions.maintain - ) + if (!repoData.permissions.admin && !repoData.permissions.push && !repoData.permissions.maintain) throw new Error("User does not have write permissions") // Get installation token diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 4ce8bfbad..29706c09c 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -41,9 +41,7 @@ for (const [os, arch] of targets) { const opentui = `@opentui/core-${os === "windows" ? "win32" : os}-${arch.replace("-baseline", "")}` await $`mkdir -p ../../node_modules/${opentui}` - await $`npm pack ${opentui}@${pkg.dependencies["@opentui/core"]}`.cwd( - path.join(dir, "../../node_modules"), - ) + await $`npm pack ${opentui}@${pkg.dependencies["@opentui/core"]}`.cwd(path.join(dir, "../../node_modules")) await $`tar -xf ../../node_modules/${opentui.replace("@opentui/", "opentui-")}-*.tgz -C ../../node_modules/${opentui} --strip-components=1` const watcher = `@parcel/watcher-${os === "windows" ? "win32" : os}-${arch.replace("-baseline", "")}${os === "linux" ? "-glibc" : ""}` @@ -51,9 +49,7 @@ for (const [os, arch] of targets) { await $`npm pack ${watcher}`.cwd(path.join(dir, "../../node_modules")).quiet() await $`tar -xf ../../node_modules/${watcher.replace("@parcel/", "parcel-")}-*.tgz -C ../../node_modules/${watcher} --strip-components=1` - const parserWorker = fs.realpathSync( - path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js"), - ) + const parserWorker = fs.realpathSync(path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js")) const workerPath = "./src/cli/cmd/tui/worker.ts" await Bun.build({ diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index 41865273d..b875d158f 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -77,8 +77,7 @@ async function regenerateWindowsCmdWrappers() { // npm_config_global is string | undefined // if it exists, the value is true - const isGlobal = - process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) + const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) // The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links // We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts @@ -94,9 +93,7 @@ async function regenerateWindowsCmdWrappers() { console.log("Successfully rebuilt npm bin links") } catch (error) { console.error("Error rebuilding npm links:", error.message) - console.error( - "npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts", - ) + console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts") } } diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index 3ae4ccf9f..3e989cc6a 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -55,18 +55,10 @@ if (!Script.preview) { } // Calculate SHA values - const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.zip | cut -d' ' -f1` - .text() - .then((x) => x.trim()) - const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.zip | cut -d' ' -f1` - .text() - .then((x) => x.trim()) - const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1` - .text() - .then((x) => x.trim()) - const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1` - .text() - .then((x) => x.trim()) + const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) diff --git a/packages/opencode/script/schema.ts b/packages/opencode/script/schema.ts index 48bf65442..585701c95 100755 --- a/packages/opencode/script/schema.ts +++ b/packages/opencode/script/schema.ts @@ -19,23 +19,12 @@ const result = z.toJSONSchema(Config.Info, { const schema = ctx.jsonSchema // Preserve strictness: set additionalProperties: false for objects - if ( - schema && - typeof schema === "object" && - schema.type === "object" && - schema.additionalProperties === undefined - ) { + if (schema && typeof schema === "object" && schema.type === "object" && schema.additionalProperties === undefined) { schema.additionalProperties = false } // Add examples and default descriptions for string fields with defaults - if ( - schema && - typeof schema === "object" && - "type" in schema && - schema.type === "string" && - schema?.default - ) { + if (schema && typeof schema === "object" && "type" in schema && schema.type === "string" && schema?.default) { if (!schema.examples) { schema.examples = [schema.default] } diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index b25b66888..ff71b0453 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -199,10 +199,8 @@ export namespace ACP { if (kind === "edit") { const input = part.state.input - const filePath = - typeof input["filePath"] === "string" ? input["filePath"] : "" - const oldText = - typeof input["oldString"] === "string" ? input["oldString"] : "" + const filePath = typeof input["filePath"] === "string" ? input["filePath"] : "" + const oldText = typeof input["oldString"] === "string" ? input["oldString"] : "" const newText = typeof input["newString"] === "string" ? input["newString"] @@ -218,9 +216,7 @@ export namespace ACP { } if (part.tool === "todowrite") { - const parsedTodos = z - .array(Todo.Info) - .safeParse(JSON.parse(part.state.output)) + const parsedTodos = z.array(Todo.Info).safeParse(JSON.parse(part.state.output)) if (parsedTodos.success) { await this.connection .sessionUpdate({ @@ -229,9 +225,7 @@ export namespace ACP { sessionUpdate: "plan", entries: parsedTodos.data.map((todo) => { const status: PlanEntry["status"] = - todo.status === "cancelled" - ? "completed" - : (todo.status as PlanEntry["status"]) + todo.status === "cancelled" ? "completed" : (todo.status as PlanEntry["status"]) return { priority: "medium", status, @@ -481,8 +475,7 @@ export namespace ACP { description: agent.description, })) - const currentModeId = - availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id + const currentModeId = availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id const mcpServers: Record = {} for (const server of params.mcpServers) { @@ -587,8 +580,7 @@ export namespace ACP { const agent = session.modeId ?? "build" const parts: Array< - | { type: "text"; text: string } - | { type: "file"; url: string; filename: string; mime: string } + { type: "text"; text: string } | { type: "file"; url: string; filename: string; mime: string } > = [] for (const part of params.prompt) { switch (part.type) { @@ -794,9 +786,7 @@ export namespace ACP { function parseUri( uri: string, - ): - | { type: "file"; url: string; filename: string; mime: string } - | { type: "text"; text: string } { + ): { type: "file"; url: string; filename: string; mime: string } | { type: "text"; text: string } { try { if (uri.startsWith("file://")) { const path = uri.slice(7) diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts index eb9dd5229..63948a8c1 100644 --- a/packages/opencode/src/acp/session.ts +++ b/packages/opencode/src/acp/session.ts @@ -13,11 +13,7 @@ export class ACPSessionManager { this.sdk = sdk } - async create( - cwd: string, - mcpServers: McpServer[], - model?: ACPSessionState["model"], - ): Promise { + async create(cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"]): Promise { const session = await this.sdk.session .create({ body: { diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 16f401629..a6933708b 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -143,18 +143,7 @@ export namespace Agent { tools: {}, builtIn: false, } - const { - name, - model, - prompt, - tools, - description, - temperature, - top_p, - mode, - permission, - ...extra - } = value + const { name, model, prompt, tools, description, temperature, top_p, mode, permission, ...extra } = value item.options = { ...item.options, ...extra, @@ -223,10 +212,7 @@ export namespace Agent { } } -function mergeAgentPermissions( - basePermission: any, - overridePermission: any, -): Agent.Info["permission"] { +function mergeAgentPermissions(basePermission: any, overridePermission: any): Agent.Info["permission"] { if (typeof basePermission.bash === "string") { basePermission.bash = { "*": basePermission.bash, diff --git a/packages/opencode/src/bun/index.ts b/packages/opencode/src/bun/index.ts index 2a8b48eff..5f1847275 100644 --- a/packages/opencode/src/bun/index.ts +++ b/packages/opencode/src/bun/index.ts @@ -8,10 +8,7 @@ import { readableStreamToText } from "bun" export namespace BunProc { const log = Log.create({ service: "bun" }) - export async function run( - cmd: string[], - options?: Bun.SpawnOptions.OptionsObject, - ) { + export async function run(cmd: string[], options?: Bun.SpawnOptions.OptionsObject) { log.info("running", { cmd: [which(), ...cmd], ...options, diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index c424eb879..f4dd3ed2c 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -19,10 +19,7 @@ export namespace Bus { const registry = new Map() - export function event( - type: Type, - properties: Properties, - ) { + export function event(type: Type, properties: Properties) { const result = { type, properties, @@ -73,10 +70,7 @@ export namespace Bus { export function subscribe( def: Definition, - callback: (event: { - type: Definition["type"] - properties: z.infer - }) => void, + callback: (event: { type: Definition["type"]; properties: z.infer }) => void, ) { return raw(def.type, callback) } diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index b4c47f0a4..ae24fbef5 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -14,11 +14,7 @@ export const AuthCommand = cmd({ command: "auth", describe: "manage credentials", builder: (yargs) => - yargs - .command(AuthLoginCommand) - .command(AuthLogoutCommand) - .command(AuthListCommand) - .demandCommand(), + yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(), async handler() {}, }) @@ -64,9 +60,7 @@ export const AuthListCommand = cmd({ prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`) } - prompts.outro( - `${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"), - ) + prompts.outro(`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s")) } }, }) @@ -86,9 +80,7 @@ export const AuthLoginCommand = cmd({ UI.empty() prompts.intro("Add credential") if (args.url) { - const wellknown = await fetch(`${args.url}/.well-known/opencode`).then( - (x) => x.json() as any, - ) + const wellknown = await fetch(`${args.url}/.well-known/opencode`).then((x) => x.json() as any) prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``) const proc = Bun.spawn({ cmd: wellknown.auth.command, @@ -290,8 +282,7 @@ export const AuthLoginCommand = cmd({ if (provider === "other") { provider = await prompts.text({ message: "Enter provider id", - validate: (x) => - x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only", + validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), }) if (prompts.isCancel(provider)) throw new UI.CancelledError() provider = provider.replace(/^@ai-sdk\//, "") diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index 8492395d1..2f5977195 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -7,11 +7,7 @@ import { EOL } from "os" export const LSPCommand = cmd({ command: "lsp", builder: (yargs) => - yargs - .command(DiagnosticsCommand) - .command(SymbolsCommand) - .command(DocumentSymbolsCommand) - .demandCommand(), + yargs.command(DiagnosticsCommand).command(SymbolsCommand).command(DocumentSymbolsCommand).demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/debug/ripgrep.ts b/packages/opencode/src/cli/cmd/debug/ripgrep.ts index 7c1d0d96a..4c18bce90 100644 --- a/packages/opencode/src/cli/cmd/debug/ripgrep.ts +++ b/packages/opencode/src/cli/cmd/debug/ripgrep.ts @@ -6,8 +6,7 @@ import { cmd } from "../cmd" export const RipgrepCommand = cmd({ command: "rg", - builder: (yargs) => - yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), + builder: (yargs) => yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), async handler() {}, }) @@ -19,9 +18,7 @@ const TreeCommand = cmd({ }), async handler(args) { await bootstrap(process.cwd(), async () => { - process.stdout.write( - (await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit })) + EOL, - ) + process.stdout.write((await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit })) + EOL) }) }, }) diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts index b114122b7..1849fe270 100644 --- a/packages/opencode/src/cli/cmd/debug/snapshot.ts +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -4,8 +4,7 @@ import { cmd } from "../cmd" export const SnapshotCommand = cmd({ command: "snapshot", - builder: (yargs) => - yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), + builder: (yargs) => yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index 6fbeee2ea..cd3ceb94b 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -189,9 +189,7 @@ export const GithubInstallCommand = cmd({ async function getAppInfo() { const project = Instance.project if (project.vcs !== "git") { - prompts.log.error( - `Could not find git repository. Please run this command from a git repository.`, - ) + prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) throw new UI.CancelledError() } @@ -204,13 +202,9 @@ export const GithubInstallCommand = cmd({ // ie. git@github.com:sst/opencode // ie. ssh://git@github.com/sst/opencode.git // ie. ssh://git@github.com/sst/opencode - const parsed = info.match( - /^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/, - ) + const parsed = info.match(/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/) if (!parsed) { - prompts.log.error( - `Could not find git repository. Please run this command from a git repository.`, - ) + prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) throw new UI.CancelledError() } const [, owner, repo] = parsed @@ -451,9 +445,7 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToLocalBranch(summary) } - const hasShared = prData.comments.nodes.some((c) => - c.body.includes(`${shareBaseUrl}/s/${shareId}`), - ) + const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) await updateComment(`${response}${footer({ image: !hasShared })}`) } // Fork PR @@ -465,9 +457,7 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToForkBranch(summary, prData) } - const hasShared = prData.comments.nodes.some((c) => - c.body.includes(`${shareBaseUrl}/s/${shareId}`), - ) + const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) await updateComment(`${response}${footer({ image: !hasShared })}`) } } @@ -557,12 +547,8 @@ export const GithubRunCommand = cmd({ // ie. Image // ie. [api.json](https://github.com/user-attachments/files/21433810/api.json) // ie. ![Image](https://github.com/user-attachments/assets/xxxx) - const mdMatches = prompt.matchAll( - /!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi, - ) - const tagMatches = prompt.matchAll( - //gi, - ) + const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi) + const tagMatches = prompt.matchAll(//gi) const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index) console.log("Images", JSON.stringify(matches, null, 2)) @@ -587,10 +573,7 @@ export const GithubRunCommand = cmd({ // Replace img tag with file path, ie. @image.png const replacement = `@${filename}` - prompt = - prompt.slice(0, start + offset) + - replacement + - prompt.slice(start + offset + tag.length) + prompt = prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length) offset += replacement.length - tag.length const contentType = res.headers.get("content-type") @@ -873,8 +856,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` throw new Error(`Failed to check permissions for user ${actor}: ${error}`) } - if (!["admin", "write"].includes(permission)) - throw new Error(`User ${actor} does not have write permissions`) + if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`) } async function createComment() { @@ -922,9 +904,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` return `${titleAlt}\n` })() - const shareUrl = shareId - ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` - : "" + const shareUrl = shareId ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` : "" return `\n\n${image}${shareUrl}[github run](${runUrl})` } @@ -1100,13 +1080,9 @@ query($owner: String!, $repo: String!, $number: Int!) { }) .map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`) - const files = (pr.files.nodes || []).map( - (f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`, - ) + const files = (pr.files.nodes || []).map((f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`) const reviewData = (pr.reviews.nodes || []).map((r) => { - const comments = (r.comments.nodes || []).map( - (c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`, - ) + const comments = (r.comments.nodes || []).map((c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`) return [ `- ${r.author.login} at ${r.submittedAt}:`, ` - Review body: ${r.body}`, @@ -1128,15 +1104,9 @@ query($owner: String!, $repo: String!, $number: Int!) { `Deletions: ${pr.deletions}`, `Total Commits: ${pr.commits.totalCount}`, `Changed Files: ${pr.files.nodes.length} files`, - ...(comments.length > 0 - ? ["", ...comments, ""] - : []), - ...(files.length > 0 - ? ["", ...files, ""] - : []), - ...(reviewData.length > 0 - ? ["", ...reviewData, ""] - : []), + ...(comments.length > 0 ? ["", ...comments, ""] : []), + ...(files.length > 0 ? ["", ...files, ""] : []), + ...(reviewData.length > 0 ? ["", ...reviewData, ""] : []), "", ].join("\n") } diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 756776d05..b646f0b15 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -138,9 +138,7 @@ export const RunCommand = cmd({ const outputJsonEvent = (type: string, data: any) => { if (args.format === "json") { - process.stdout.write( - JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL, - ) + process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL) return true } return false @@ -160,9 +158,7 @@ export const RunCommand = cmd({ const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] const title = part.state.title || - (Object.keys(part.state.input).length > 0 - ? JSON.stringify(part.state.input) - : "Unknown") + (Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown") printEvent(color, tool, title) if (part.tool === "bash" && part.state.output?.trim()) { UI.println() @@ -215,10 +211,7 @@ export const RunCommand = cmd({ ], initialValue: "once", }).catch(() => "reject") - const response = (result.toString().includes("cancel") ? "reject" : result) as - | "once" - | "always" - | "reject" + const response = (result.toString().includes("cancel") ? "reject" : result) as "once" | "always" | "reject" await sdk.postSessionIdPermissionsPermissionId({ path: { id: sessionID, permissionID: permission.id }, body: { response }, @@ -280,10 +273,7 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if ( - cfgResult.data && - (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) - ) { + if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) @@ -336,10 +326,7 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if ( - cfgResult.data && - (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) - ) { + if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) diff --git a/packages/opencode/src/cli/cmd/stats.ts b/packages/opencode/src/cli/cmd/stats.ts index d7afbe330..58e8397db 100644 --- a/packages/opencode/src/cli/cmd/stats.ts +++ b/packages/opencode/src/cli/cmd/stats.ts @@ -68,9 +68,7 @@ async function getAllSessions(): Promise { if (!project) continue const sessionKeys = await Storage.list(["session", project.id]) - const projectSessions = await Promise.all( - sessionKeys.map((key) => Storage.read(key)), - ) + const projectSessions = await Promise.all(sessionKeys.map((key) => Storage.read(key))) for (const session of projectSessions) { if (session) { @@ -87,16 +85,12 @@ async function aggregateSessionStats(days?: number, projectFilter?: string): Pro const DAYS_IN_SECOND = 24 * 60 * 60 * 1000 const cutoffTime = days ? Date.now() - days * DAYS_IN_SECOND : 0 - let filteredSessions = days - ? sessions.filter((session) => session.time.updated >= cutoffTime) - : sessions + let filteredSessions = days ? sessions.filter((session) => session.time.updated >= cutoffTime) : sessions if (projectFilter !== undefined) { if (projectFilter === "") { const currentProject = await getCurrentProject() - filteredSessions = filteredSessions.filter( - (session) => session.projectID === currentProject.id, - ) + filteredSessions = filteredSessions.filter((session) => session.projectID === currentProject.id) } else { filteredSessions = filteredSessions.filter((session) => session.projectID === projectFilter) } @@ -125,9 +119,7 @@ async function aggregateSessionStats(days?: number, projectFilter?: string): Pro } if (filteredSessions.length > 1000) { - console.log( - `Large dataset detected (${filteredSessions.length} sessions). This may take a while...`, - ) + console.log(`Large dataset detected (${filteredSessions.length} sessions). This may take a while...`) } if (filteredSessions.length === 0) { @@ -262,8 +254,7 @@ export function displayStats(stats: SessionStats, toolLimit?: number) { const percentage = ((count / totalToolUsage) * 100).toFixed(1) const maxToolLength = 18 - const truncatedTool = - tool.length > maxToolLength ? tool.substring(0, maxToolLength - 2) + ".." : tool + const truncatedTool = tool.length > maxToolLength ? tool.substring(0, maxToolLength - 2) + ".." : tool const toolName = truncatedTool.padEnd(maxToolLength) const content = ` ${toolName} ${bar.padEnd(20)} ${count.toString().padStart(3)} (${percentage.padStart(4)}%)` diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 9d30ed6d8..fad233987 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -115,11 +115,7 @@ export function tui(input: { render( () => { return ( - ( - - )} - > + }> @@ -413,12 +409,7 @@ function App() { flexShrink={0} > - + open code{" "} @@ -434,11 +425,7 @@ function App() { tab {""} - + {local.agent.current().name.toUpperCase()} AGENT diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 154995997..04f2f6523 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -52,11 +52,7 @@ export function DialogModel() { description: provider.name, category: provider.name, })), - filter( - (x) => - Boolean(ref()?.filter) || - !local.model.recent().find((y) => isDeepEqual(y, x.value)), - ), + filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))), ), ), ), diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index c4f318451..5e0095a8d 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -20,9 +20,7 @@ export function DialogSessionList() { const deleteKeybind = "ctrl+d" - const currentSessionID = createMemo(() => - route.data.type === "session" ? route.data.sessionID : undefined, - ) + const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined)) const options = createMemo(() => { const today = new Date().toDateString() diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx index d1ef5ca56..e427e24e9 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx @@ -77,10 +77,7 @@ export function DialogStatus() { )} - 0} - fallback={No Formatters} - > + 0} fallback={No Formatters}> {enabledFormatters().length} Formatters diff --git a/packages/opencode/src/cli/cmd/tui/component/logo.tsx b/packages/opencode/src/cli/cmd/tui/component/logo.tsx index 7cac51ecc..59db5fe7d 100644 --- a/packages/opencode/src/cli/cmd/tui/component/logo.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/logo.tsx @@ -3,19 +3,9 @@ import { TextAttributes } from "@opentui/core" import { For } from "solid-js" import { useTheme } from "@tui/context/theme" -const LOGO_LEFT = [ - ` `, - `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, - `█░░█ █░░█ █▀▀▀ █░░█`, - `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`, -] +const LOGO_LEFT = [` `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`] -const LOGO_RIGHT = [ - ` ▄ `, - `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, - `█░░░ █░░█ █░░█ █▀▀▀`, - `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`, -] +const LOGO_RIGHT = [` ▄ `, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`] export function Logo() { const { theme } = useTheme() diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 88ca32420..68578e708 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -83,12 +83,7 @@ export function Autocomplete(props: { const extmarkStart = store.index const extmarkEnd = extmarkStart + Bun.stringWidth(virtualText) - const styleId = - part.type === "file" - ? props.fileStyleId - : part.type === "agent" - ? props.agentStyleId - : undefined + const styleId = part.type === "file" ? props.fileStyleId : part.type === "agent" ? props.agentStyleId : undefined const extmarkId = input.extmarks.create({ start: extmarkStart, @@ -185,9 +180,7 @@ export function Autocomplete(props: { ) }) - const session = createMemo(() => - props.sessionID ? sync.session.get(props.sessionID) : undefined, - ) + const session = createMemo(() => (props.sessionID ? sync.session.get(props.sessionID) : undefined)) const commands = createMemo((): AutocompleteOption[] => { const results: AutocompleteOption[] = [] const s = session() @@ -324,9 +317,7 @@ export function Autocomplete(props: { const options = createMemo(() => { const mixed: AutocompleteOption[] = ( - store.visible === "@" - ? [...agents(), ...(files.loading ? files.latest || [] : files())] - : [...commands()] + store.visible === "@" ? [...agents(), ...(files.loading ? files.latest || [] : files())] : [...commands()] ).filter((x) => x.disabled !== true) const currentFilter = filter() if (!currentFilter) return mixed.slice(0, 10) @@ -393,9 +384,7 @@ export function Autocomplete(props: { return } // Check if a space was typed after the trigger character - const currentText = props - .input() - .getTextRange(store.index + 1, props.input().cursorOffset + 1) + const currentText = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1) if (currentText.includes(" ")) { hide() } @@ -433,13 +422,8 @@ export function Autocomplete(props: { if (e.name === "@") { const cursorOffset = props.input().cursorOffset const charBeforeCursor = - cursorOffset === 0 - ? undefined - : props.input().getTextRange(cursorOffset - 1, cursorOffset) - const canTrigger = - charBeforeCursor === undefined || - charBeforeCursor === "" || - /\s/.test(charBeforeCursor) + cursorOffset === 0 ? undefined : props.input().getTextRange(cursorOffset - 1, cursorOffset) + const canTrigger = charBeforeCursor === undefined || charBeforeCursor === "" || /\s/.test(charBeforeCursor) if (canTrigger) show("@") } @@ -487,10 +471,7 @@ export function Autocomplete(props: { {option.display} - + {option.description} diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index b9e406598..eac00d9e7 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -334,9 +334,7 @@ export function Prompt(props: PromptProps) { // Expand pasted text inline before submitting const allExtmarks = input.extmarks.getAllForTypeId(promptPartTypeId) - const sortedExtmarks = allExtmarks.sort( - (a: { start: number }, b: { start: number }) => b.start - a.start, - ) + const sortedExtmarks = allExtmarks.sort((a: { start: number }, b: { start: number }) => b.start - a.start) for (const extmark of sortedExtmarks) { const partIndex = store.extmarkToPartIndex.get(extmark.id) @@ -499,28 +497,15 @@ export function Prompt(props: PromptProps) { - + {store.mode === "normal" ? ">" : "!"} - +