diff --git a/github/index.ts b/github/index.ts index 789aad89..b681ff92 100644 --- a/github/index.ts +++ b/github/index.ts @@ -171,9 +171,7 @@ try { const summary = await summarize(response) await pushToLocalBranch(summary) } - const hasShared = prData.comments.nodes.some((c) => - c.body.includes(`${useShareUrl()}/s/${shareId}`), - ) + const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${useShareUrl()}/s/${shareId}`)) await updateComment(`${response}${footer({ image: !hasShared })}`) } // Fork PR @@ -185,9 +183,7 @@ try { const summary = await summarize(response) await pushToForkBranch(summary, prData) } - const hasShared = prData.comments.nodes.some((c) => - c.body.includes(`${useShareUrl()}/s/${shareId}`), - ) + const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${useShareUrl()}/s/${shareId}`)) await updateComment(`${response}${footer({ image: !hasShared })}`) } } @@ -368,9 +364,7 @@ async function getAccessToken() { if (!response.ok) { const responseJson = (await response.json()) as { error?: string } - throw new Error( - `App token exchange failed: ${response.status} ${response.statusText} - ${responseJson.error}`, - ) + throw new Error(`App token exchange failed: ${response.status} ${response.statusText} - ${responseJson.error}`) } const responseJson = (await response.json()) as { token: string } @@ -411,12 +405,8 @@ async function getUserPrompt() { // 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)) @@ -443,8 +433,7 @@ async function getUserPrompt() { // 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") @@ -512,12 +501,7 @@ async function subscribeSessionEvents() { ? JSON.stringify(part.state.input) : "Unknown" console.log() - console.log( - color + `|`, - "\x1b[0m\x1b[2m" + ` ${tool.padEnd(7, " ")}`, - "", - "\x1b[0m" + title, - ) + console.log(color + `|`, "\x1b[0m\x1b[2m" + ` ${tool.padEnd(7, " ")}`, "", "\x1b[0m" + title) } if (part.type === "text") { @@ -729,8 +713,7 @@ async function assertPermissions() { 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 updateComment(body: string) { @@ -774,9 +757,7 @@ function footer(opts?: { image?: boolean }) { return `${titleAlt}\n` })() - const shareUrl = shareId - ? `[opencode session](${useShareUrl()}/s/${shareId})  |  ` - : "" + const shareUrl = shareId ? `[opencode session](${useShareUrl()}/s/${shareId})  |  ` : "" return `\n\n${image}${shareUrl}[github run](${useEnvRunUrl()})` } @@ -959,13 +940,9 @@ function buildPromptDataForPR(pr: GitHubPullRequest) { }) .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}`, @@ -987,15 +964,9 @@ function buildPromptDataForPR(pr: GitHubPullRequest) { `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/infra/console.ts b/infra/console.ts index 7fbf92bf..98cc4c3f 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -61,13 +61,7 @@ export const auth = new sst.cloudflare.Worker("AuthApi", { domain: `auth.${domain}`, handler: "packages/console/function/src/auth.ts", url: true, - link: [ - database, - authStorage, - GITHUB_CLIENT_ID_CONSOLE, - GITHUB_CLIENT_SECRET_CONSOLE, - GOOGLE_CLIENT_ID, - ], + link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID], }) //////////////// diff --git a/packages/console/app/src/app.tsx b/packages/console/app/src/app.tsx index 7976f6b3..1cf96364 100644 --- a/packages/console/app/src/app.tsx +++ b/packages/console/app/src/app.tsx @@ -12,10 +12,7 @@ export default function App() { root={(props) => ( opencode - + {props.children} )} diff --git a/packages/console/app/src/component/faq.tsx b/packages/console/app/src/component/faq.tsx index 47dca951..753a0dce 100644 --- a/packages/console/app/src/component/faq.tsx +++ b/packages/console/app/src/component/faq.tsx @@ -13,10 +13,7 @@ export function Faq(props: ParentProps & { question: string }) { fill="currentColor" xmlns="http://www.w3.org/2000/svg" > - + ) { - - + + - + ) { fill-opacity="0.2" /> - - + + @@ -61,21 +40,9 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - - + + + @@ -86,40 +53,16 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - + + - - + + - - + + ) @@ -127,14 +70,7 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { export function IconCopy(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconCheck(props: JSX.SvgSVGAttributes) { return ( - - + + ) } @@ -189,14 +113,7 @@ export function IconStripe(props: JSX.SvgSVGAttributes) { export function IconChevron(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconWorkspaceLogo(props: JSX.SvgSVGAttributes) { return ( - + ) @@ -234,10 +144,7 @@ export function IconOpenAI(props: JSX.SvgSVGAttributes) { export function IconAnthropic(props: JSX.SvgSVGAttributes) { return ( - + { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", } - const apiBaseUrl = config.github.repoUrl.replace( - "https://github.com/", - "https://api.github.com/repos/", - ) + const apiBaseUrl = config.github.repoUrl.replace("https://github.com/", "https://api.github.com/repos/") try { const [meta, releases, contributors] = await Promise.all([ fetch(apiBaseUrl, { headers }).then((res) => res.json()), diff --git a/packages/console/app/src/routes/auth/authorize.ts b/packages/console/app/src/routes/auth/authorize.ts index 293e9ede..166466ef 100644 --- a/packages/console/app/src/routes/auth/authorize.ts +++ b/packages/console/app/src/routes/auth/authorize.ts @@ -2,9 +2,6 @@ import type { APIEvent } from "@solidjs/start/server" import { AuthClient } from "~/context/auth" export async function GET(input: APIEvent) { - const result = await AuthClient.authorize( - new URL("./callback", input.request.url).toString(), - "code", - ) + const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code") return Response.redirect(result.url, 302) } diff --git a/packages/console/app/src/routes/brand/index.tsx b/packages/console/app/src/routes/brand/index.tsx index 72ea0f15..6aac4517 100644 --- a/packages/console/app/src/routes/brand/index.tsx +++ b/packages/console/app/src/routes/brand/index.tsx @@ -68,13 +68,7 @@ export default function Brand() { onClick={() => downloadFile(brandAssets, "opencode-brand-assets.zip")} > Download all assets - + OpenCode brand guidelines
- - - - -
@@ -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 ee138e14..8b8f4499 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 59987e4d..b0aef00e 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 4e218227..fa8cf1d2 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 e6461ac8..77c01796 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 a7218546..0fb2a0df 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 e1c2c00c..565981c7 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 4b2a12fd..5aa1b969 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 223d69fc..7a1980eb 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 7b949c66..65edc684 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 67314fbd..5419ed7f 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 5b638192..a6eaaeb1 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 a096b52d..4eab4dcb 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 deab7ded..194a7c71 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 f4e8dc44..d8d1cd74 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 d6998572..8a9170ef 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 fa0776b7..e79e8357 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 3d0c3147..ee2b3ab5 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 dc0d298f..67143e66 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 af9bcc3a..1ae18c4d 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 bd00e196..02d49890 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 ce4a20f4..e87ada6e 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 8b37b1f9..f0f065de 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 6396fd0b..688f19b3 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 30cc15e4..ea719534 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 0af642f7..cf2040b5 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 d55e605a..27c926d6 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 343b0c4f..1c032aad 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 04d11e2e..11be5b4d 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 ce5b6c53..7fd7f5e1 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 cbb1ac82..8b7a96f4 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 e94eb564..baf0d383 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 3475f5d7..6f00dae9 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 4ce8bfba..29706c09 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 41865273..b875d158 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 3ae4ccf9..3e989cc6 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 48bf6544..585701c9 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 b25b6688..ff71b045 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 eb9dd522..63948a8c 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 16f40162..a6933708 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 2a8b48ef..5f184727 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 c424eb87..f4dd3ed2 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 b4c47f0a..ae24fbef 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 8492395d..2f597719 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 7c1d0d96..4c18bce9 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 b114122b..1849fe27 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 6fbeee2e..cd3ceb94 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 756776d0..b646f0b1 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 d7afbe33..58e8397d 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 9d30ed6d..fad23398 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 15499599..04f2f652 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 c4f31845..5e0095a8 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 d1ef5ca5..e427e24e 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 7cac51ec..59db5fe7 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 88ca3242..68578e70 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 b9e40659..eac00d9e 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" ? ">" : "!"} - +