@@ -256,92 +267,15 @@ export default function Page() {
-
-
-
-
-
-
-
setStore("fileSelectOpen", true)}
- >
-
-
-
-
- {(file) => (
- props.onTabClick(props.file)}
- >
-
-
- {getFilename(file.path)}
-
-
- )}
-
-
-
-
- {(file) => (
-
-
-
- )}
-
-
-
+
setStore("modelSelectOpen", true)}
onInputRefChange={(element: HTMLTextAreaElement | undefined) => {
inputRef = element ?? undefined
}}
/>
-
-
- {(activeSession) => (
-
-
-
-
- {activeSession().title || "Untitled Session"}
-
-
-
-
-
- )}
-
-
From 49e859cfd6565c4ef67388c232e639917ec20625 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Tue, 14 Oct 2025 12:06:13 -0500
Subject: [PATCH 2/9] wip: css/ui work
---
packages/css/src/components/select.css | 113 +++++++++----------
packages/css/src/components/tabs.css | 98 +++++++++++++++++
packages/css/src/index.css | 1 +
packages/ui/index.html | 2 +-
packages/ui/src/app.tsx | 146 +++++++++++++++----------
packages/ui/src/components/index.ts | 1 +
packages/ui/src/components/select.tsx | 10 +-
packages/ui/src/components/tabs.tsx | 74 +++++++++++++
packages/ui/src/index.css | 3 +-
packages/ui/src/index.tsx | 1 -
10 files changed, 325 insertions(+), 124 deletions(-)
create mode 100644 packages/css/src/components/tabs.css
create mode 100644 packages/ui/src/components/tabs.tsx
diff --git a/packages/css/src/components/select.css b/packages/css/src/components/select.css
index d7b4b589d..2403bfb26 100644
--- a/packages/css/src/components/select.css
+++ b/packages/css/src/components/select.css
@@ -1,20 +1,4 @@
[data-component="select"] {
- display: inline-flex;
- align-items: center;
- gap: calc(var(--spacing) * 2);
- border-style: solid;
- border-width: 1px;
- border-radius: var(--radius-md);
- border-color: var(--color-smoke-4);
- font-family: var(--font-sans);
- font-size: var(--text-base);
- line-height: var(--text-base--line-height);
- font-weight: var(--font-weight-normal);
- cursor: pointer;
- transition: all 0.2s ease-in-out;
- text-decoration: none;
- user-select: none;
-
&:disabled {
opacity: 0.5;
cursor: not-allowed;
@@ -28,38 +12,35 @@
[data-slot="section"] {
font-size: var(--text-xs);
line-height: var(--text-xs--line-height);
- font-weight: var(--font-weight-normal);
+ font-weight: var(--font-weight-light);
+ text-transform: uppercase;
+ color: var(--text-default-text-weak);
+ opacity: 0.6;
margin-top: calc(var(--spacing) * 3);
margin-left: calc(var(--spacing) * 2);
&:first-child {
- margin-top: calc(var(--spacing) * 0);
+ margin-top: 0;
}
}
[data-slot="item"] {
- /* "relative flex cursor-pointer select-none items-center": true, */
- /* "rounded-sm px-2 py-0.5 text-xs outline-none text-text": true, */
- /* "transition-colors data-[disabled]:pointer-events-none": true, */
- /* "data-[highlighted]:bg-background-element data-[disabled]:opacity-50": true, */
position: relative;
display: flex;
align-items: center;
- justify-content: center;
- gap: calc(var(--spacing) * 2);
- border-style: solid;
- border-width: 1px;
- border-radius: var(--radius-md);
- font-family: var(--font-sans);
- font-size: var(--text-base);
- line-height: var(--text-base--line-height);
- font-weight: var(--font-weight-normal);
+ padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
+ border-radius: var(--radius-sm);
+ font-size: var(--text-xs);
+ line-height: var(--text-xs--line-height);
+ color: var(--text-default-text);
cursor: pointer;
- transition: all 0.2s ease-in-out;
- text-decoration: none;
+ transition:
+ background-color 0.2s ease-in-out,
+ color 0.2s ease-in-out;
+ outline: none;
user-select: none;
&[data-highlighted] {
- background-color: var(--color-background-element);
+ background-color: var(--surface-default-surface);
}
&[data-disabled] {
@@ -67,16 +48,7 @@
opacity: 0.5;
}
- /* [data-slot="item-label"] { */
- /* font-size: var(--text-xs); */
- /* line-height: var(--text-xs--line-height); */
- /* font-weight: var(--font-weight-normal); */
- /* } */
-
[data-slot="item-indicator"] {
- /* display: flex; */
- /* align-items: center; */
- /* gap: calc(var(--spacing) * 1); */
margin-left: auto;
}
}
@@ -88,46 +60,61 @@
white-space: nowrap;
}
[data-slot="icon"] {
- /* "group size-fit shrink-0 text-text-muted transition-transform duration-100": true, */
width: fit-content;
height: fit-content;
flex-shrink: 0;
+ color: var(--text-default-text-weak);
+ transition: transform 0.1s ease-in-out;
}
}
}
[data-component="select-content"] {
- /* "min-w-32 overflow-hidden rounded-md border border-border-subtle/40": true, */
- /* "bg-background-panel p-1 shadow-md z-50": true, */
- /* "data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95": true, */
- /* "data-[expanded]:animate-in data-[expanded]:fade-in-0 data-[expanded]:zoom-in-95": true, */
min-width: 8rem;
overflow: hidden;
border-radius: var(--radius-md);
border-width: 1px;
border-style: solid;
- border-color: var(--color-smoke-4);
- background-color: var(--color-smoke-2);
+ border-color: var(--border-default-border-weak);
+ background-color: var(--surface-raised-surface-raised);
padding: calc(var(--spacing) * 1);
+ box-shadow: var(--shadow-md);
z-index: 50;
- /* &[data-closed] { */
- /* animation: fade-out-0 0.2s ease-out; */
- /* animation-fill-mode: forwards; */
- /* animation-delay: 0.2s; */
- /* opacity: 0; */
- /* } */
- /* &[data-expanded] { */
- /* animation: fade-in-0 0.2s ease-out; */
- /* animation-fill-mode: forwards; */
- /* animation-delay: 0.2s; */
- /* opacity: 1; */
- /* } */
+ &[data-closed] {
+ animation: select-close 0.15s ease-out;
+ }
+
+ &[data-expanded] {
+ animation: select-open 0.15s ease-out;
+ }
+
[data-slot="list"] {
- /* overflow-y-auto max-h-48 whitespace-nowrap overflow-x-hidden */
overflow-y: auto;
max-height: 12rem;
white-space: nowrap;
overflow-x: hidden;
}
}
+
+@keyframes select-open {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes select-close {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+}
diff --git a/packages/css/src/components/tabs.css b/packages/css/src/components/tabs.css
new file mode 100644
index 000000000..3a70b4867
--- /dev/null
+++ b/packages/css/src/components/tabs.css
@@ -0,0 +1,98 @@
+[data-component="tabs"] {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+
+ & [data-slot="list"] {
+ position: relative;
+ display: flex;
+ align-items: center;
+ background-color: var(--surface-default-surface);
+ overflow-x: auto;
+
+ /* Hide scrollbar */
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ /* Divider between tabs */
+ & > [data-slot="trigger"]:not(:first-child) {
+ border-left: 1px solid var(--border-default-border-weak);
+ }
+
+ /* After element to fill remaining space */
+ &::after {
+ content: "";
+ display: block;
+ flex-grow: 1;
+ height: calc(var(--spacing) * 8);
+ border-left: 1px solid var(--border-default-border-weak);
+ border-bottom: 1px solid var(--border-default-border-weak);
+ }
+
+ &:empty::after {
+ border-left: none;
+ }
+ }
+
+ & [data-slot="trigger"] {
+ position: relative;
+ padding: 0 calc(var(--spacing) * 3);
+ height: calc(var(--spacing) * 8);
+ display: flex;
+ align-items: center;
+ font-size: var(--text-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-default-text-weak);
+ cursor: pointer;
+ white-space: nowrap;
+ flex-shrink: 0;
+ border-bottom: 1px solid var(--border-default-border-weak);
+ background-color: transparent;
+ transition:
+ background-color 0.15s ease,
+ color 0.15s ease;
+
+ &:disabled {
+ pointer-events: none;
+ opacity: 0.5;
+ }
+
+ &:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--border-default-border-focus);
+ }
+
+ &[data-selected] {
+ color: var(--text-default-text);
+ background-color: var(--surface-panel-surface);
+ border-bottom-color: transparent;
+ }
+
+ &:hover:not(:disabled):not([data-selected]) {
+ color: var(--text-default-text);
+ }
+ }
+
+ & [data-slot="content"] {
+ background-color: var(--surface-panel-surface);
+ overflow-y: auto;
+ flex: 1;
+
+ /* Hide scrollbar */
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ &:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--border-default-border-focus);
+ }
+ }
+}
diff --git a/packages/css/src/index.css b/packages/css/src/index.css
index 731fc6f3c..1ac357fe1 100644
--- a/packages/css/src/index.css
+++ b/packages/css/src/index.css
@@ -8,5 +8,6 @@
@import "./components/button.css" layer(components);
@import "./components/icon.css" layer(components);
@import "./components/select.css" layer(components);
+@import "./components/tabs.css" layer(components);
@import "./utilities.css" layer(utilities);
diff --git a/packages/ui/index.html b/packages/ui/index.html
index b7f74e79d..7697a5f96 100644
--- a/packages/ui/index.html
+++ b/packages/ui/index.html
@@ -1,5 +1,5 @@
-
+
diff --git a/packages/ui/src/app.tsx b/packages/ui/src/app.tsx
index be2ec357e..f42781543 100644
--- a/packages/ui/src/app.tsx
+++ b/packages/ui/src/app.tsx
@@ -1,66 +1,100 @@
import type { Component } from "solid-js"
-import { Button } from "./components/button"
-import { Select } from "./components"
+import { Button, Select, Tabs } from "./components"
import "@opencode-ai/css"
import "./index.css"
const App: Component = () => {
+ const Content = (props: { dark?: boolean }) => (
+
+
Buttons
+
+
+
+
+
+
+
+
+
Select
+
+
Tabs
+
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+ Disabled Tab
+
+
+
+
+
Tab 1 Content
+
This is the content for the first tab.
+
+
+
+
+
Tab 2 Content
+
This is the content for the second tab.
+
+
+
+
+
Tab 3 Content
+
This is the content for the third tab.
+
+
+
+
+
Tab 4 Content
+
This tab should be disabled.
+
+
+
+
+
+ )
+
return (
-
-
Buttons
-
-
-
-
-
-
-
-
- Select
-
- console.log(x)} placeholder="Select" />
-
-
-
-
Buttons
-
-
-
-
-
-
-
-
- Select
-
- console.log(x)} placeholder="Select" />
-
-
+
+
)
}
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index 80d80299b..be12d6a67 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -1,3 +1,4 @@
export * from "./button"
export * from "./icon"
export * from "./select"
+export * from "./tabs"
diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx
index c4d5443c1..ecf05d5e2 100644
--- a/packages/ui/src/components/select.tsx
+++ b/packages/ui/src/components/select.tsx
@@ -79,11 +79,17 @@ export function Select(props: SelectProps & ButtonProps) {
}}
-
+
-
+
diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx
new file mode 100644
index 000000000..5e047a7ca
--- /dev/null
+++ b/packages/ui/src/components/tabs.tsx
@@ -0,0 +1,74 @@
+import { Tabs as Kobalte } from "@kobalte/core/tabs"
+import { splitProps } from "solid-js"
+import type { ComponentProps, ParentProps } from "solid-js"
+
+export interface TabsProps extends ComponentProps {}
+export interface TabsListProps extends ComponentProps {}
+export interface TabsTriggerProps extends ComponentProps {}
+export interface TabsContentProps extends ComponentProps {}
+
+function TabsRoot(props: TabsProps) {
+ const [split, rest] = splitProps(props, ["class", "classList"])
+ return (
+
+ )
+}
+
+function TabsList(props: TabsListProps) {
+ const [split, rest] = splitProps(props, ["class", "classList"])
+ return (
+
+ )
+}
+
+function TabsTrigger(props: ParentProps) {
+ const [split, rest] = splitProps(props, ["class", "classList", "children"])
+ return (
+
+ {split.children}
+
+ )
+}
+
+function TabsContent(props: ParentProps) {
+ const [split, rest] = splitProps(props, ["class", "classList", "children"])
+ return (
+
+ {split.children}
+
+ )
+}
+
+export const Tabs = Object.assign(TabsRoot, {
+ List: TabsList,
+ Trigger: TabsTrigger,
+ Content: TabsContent,
+})
diff --git a/packages/ui/src/index.css b/packages/ui/src/index.css
index be7adaaed..b5f1cf302 100644
--- a/packages/ui/src/index.css
+++ b/packages/ui/src/index.css
@@ -12,7 +12,7 @@
}
main > div {
flex: 1;
- padding: 3rem;
+ padding: 2rem;
min-width: 0;
overflow-y: auto;
overflow-x: hidden;
@@ -24,6 +24,7 @@
font-size: 1.25rem;
font-weight: 600;
margin: 0 0 1rem 0;
+ margin-bottom: -1rem;
}
section {
display: flex;
diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx
index 216765711..0e4c4a018 100644
--- a/packages/ui/src/index.tsx
+++ b/packages/ui/src/index.tsx
@@ -1,6 +1,5 @@
/* @refresh reload */
import { render } from "solid-js/web"
-import "solid-devtools"
import App from "./app"
From 96d7ccea487b961406a5f4167cc2f844e339fe56 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Tue, 14 Oct 2025 12:07:45 -0500
Subject: [PATCH 3/9] wip: css/ui work
---
packages/css/package.json | 2 +-
packages/ui/package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/css/package.json b/packages/css/package.json
index c281e28d5..bce73606c 100644
--- a/packages/css/package.json
+++ b/packages/css/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/css",
- "version": "0.15.0",
+ "version": "0.15.2",
"type": "module",
"exports": {
".": "./src/index.css",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 0e5d4c379..e9b453612 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
- "version": "0.15.0",
+ "version": "0.15.2",
"type": "module",
"exports": {
".": "./src/components/index.ts",
From 5192c51843eb19f5989460c800722aaa36f0d7a1 Mon Sep 17 00:00:00 2001
From: GitHub Action
Date: Tue, 14 Oct 2025 17:08:18 +0000
Subject: [PATCH 4/9] chore: format code
---
bun.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bun.lock b/bun.lock
index eae651f0f..f76157ca8 100644
--- a/bun.lock
+++ b/bun.lock
@@ -112,7 +112,7 @@
},
"packages/css": {
"name": "@opencode-ai/css",
- "version": "0.15.0",
+ "version": "0.15.2",
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
@@ -257,7 +257,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
- "version": "0.15.0",
+ "version": "0.15.2",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/css": "workspace:*",
From 42c2ffd8427122bd8847d25572dff2509102d36d Mon Sep 17 00:00:00 2001
From: Frank
Date: Tue, 14 Oct 2025 13:52:28 -0400
Subject: [PATCH 5/9] wip: zen
---
packages/console/app/src/routes/workspace.tsx | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/packages/console/app/src/routes/workspace.tsx b/packages/console/app/src/routes/workspace.tsx
index 2ac629f52..ef79ec4fe 100644
--- a/packages/console/app/src/routes/workspace.tsx
+++ b/packages/console/app/src/routes/workspace.tsx
@@ -1,4 +1,3 @@
-import { Show } from "solid-js"
import { query, createAsync, RouteSectionProps, useParams, A } from "@solidjs/router"
import "./workspace.css"
import { IconWorkspaceLogo } from "../component/icon"
@@ -7,7 +6,6 @@ import { UserMenu } from "./user-menu"
import { withActor } from "~/context/auth.withActor"
import { User } from "@opencode-ai/console-core/user.js"
import { Actor } from "@opencode-ai/console-core/actor.js"
-import { querySessionInfo } from "./workspace/common"
const getUserEmail = query(async (workspaceID: string) => {
"use server"
@@ -21,7 +19,6 @@ const getUserEmail = query(async (workspaceID: string) => {
export default function WorkspaceLayout(props: RouteSectionProps) {
const params = useParams()
const userEmail = createAsync(() => getUserEmail(params.id))
- const sessionInfo = createAsync(() => querySessionInfo(params.id))
return (
@@ -29,9 +26,7 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
-
-
-
+
From c1a420717a2c57bc5c363d682a2dc0ba7e253c00 Mon Sep 17 00:00:00 2001
From: Frank
Date: Tue, 14 Oct 2025 13:58:54 -0400
Subject: [PATCH 6/9] ci: fix
---
packages/console/function/src/auth.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/console/function/src/auth.ts b/packages/console/function/src/auth.ts
index e991e8c22..4d3ca6591 100644
--- a/packages/console/function/src/auth.ts
+++ b/packages/console/function/src/auth.ts
@@ -1,3 +1,4 @@
+import type { KVNamespace } from "@cloudflare/workers-types"
import { z } from "zod"
import { issuer } from "@openauthjs/openauth"
import type { Theme } from "@openauthjs/openauth/ui/theme"
@@ -94,6 +95,7 @@ export default {
// }),
},
storage: CloudflareStorage({
+ // @ts-ignore
namespace: env.AuthStorage,
}),
subjects,
From 717b5446331f8ff3034c0d35b95b832e92ffef49 Mon Sep 17 00:00:00 2001
From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Date: Tue, 14 Oct 2025 13:18:40 -0500
Subject: [PATCH 7/9] fix: false positive package manager detection in upgrade
(#3181)
---
packages/opencode/src/cli/cmd/upgrade.ts | 16 +++++++++++++---
packages/opencode/src/installation/index.ts | 1 +
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/packages/opencode/src/cli/cmd/upgrade.ts b/packages/opencode/src/cli/cmd/upgrade.ts
index 8c1abdeab..65f3bab4d 100644
--- a/packages/opencode/src/cli/cmd/upgrade.ts
+++ b/packages/opencode/src/cli/cmd/upgrade.ts
@@ -27,9 +27,19 @@ export const UpgradeCommand = {
const detectedMethod = await Installation.method()
const method = (args.method as Installation.Method) ?? detectedMethod
if (method === "unknown") {
- prompts.log.error(`opencode is installed to ${process.execPath} and seems to be managed by a package manager`)
- prompts.outro("Done")
- return
+ prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`)
+ const install = await prompts.select({
+ message: "Install anyways?",
+ options: [
+ { label: "Yes", value: true },
+ { label: "No", value: false },
+ ],
+ initialValue: false,
+ })
+ if (!install) {
+ prompts.outro("Done")
+ return
+ }
}
prompts.log.info("Using method: " + method)
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest()
diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts
index b01ce5f7b..95b431ecd 100644
--- a/packages/opencode/src/installation/index.ts
+++ b/packages/opencode/src/installation/index.ts
@@ -50,6 +50,7 @@ export namespace Installation {
export async function method() {
if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl"
+ if (process.execPath.includes(path.join(".local", "bin"))) return "curl"
const exec = process.execPath.toLowerCase()
const checks = [
From 0c022ef39da2724255794484caf5090b2d30efc4 Mon Sep 17 00:00:00 2001
From: Dax Raad
Date: Tue, 14 Oct 2025 14:34:19 -0400
Subject: [PATCH 8/9] ci: stuff
---
.github/workflows/publish.yml | 1 +
.github/workflows/snapshot.yml | 2 --
bun.lock | 10 ++++++
package.json | 3 +-
packages/opencode/package.json | 3 +-
packages/opencode/script/build.ts | 12 +++----
packages/opencode/script/publish.ts | 50 ++++++++++++-----------------
packages/script/package.json | 10 ++++++
packages/script/src/index.ts | 35 ++++++++++++++++++++
packages/script/tsconfig.json | 8 +++++
script/publish.ts | 47 ++++++++-------------------
11 files changed, 107 insertions(+), 74 deletions(-)
create mode 100644 packages/script/package.json
create mode 100644 packages/script/src/index.ts
create mode 100644 packages/script/tsconfig.json
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f160b417e..57f3abb93 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -58,6 +58,7 @@ jobs:
./script/publish.ts
env:
OPENCODE_BUMP: ${{ inputs.bump }}
+ OPENCODE_TAG: latest
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 711af3d42..a331462ed 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -30,7 +30,5 @@ jobs:
run: |
./packages/opencode/script/publish.ts
env:
- OPENCODE_SNAPSHOT: true
- OPENCODE_TAG: ${{ github.ref_name }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/bun.lock b/bun.lock
index f76157ca8..939802a86 100644
--- a/bun.lock
+++ b/bun.lock
@@ -4,6 +4,7 @@
"": {
"name": "opencode",
"dependencies": {
+ "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
},
"devDependencies": {
@@ -209,6 +210,7 @@
"@ai-sdk/amazon-bedrock": "2.2.10",
"@ai-sdk/google-vertex": "3.0.16",
"@octokit/webhooks-types": "7.6.1",
+ "@opencode-ai/script": "workspace:*",
"@parcel/watcher-win32-x64": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@tsconfig/bun": "catalog:",
@@ -233,6 +235,12 @@
"typescript": "catalog:",
},
},
+ "packages/script": {
+ "name": "@opencode-ai/script",
+ "devDependencies": {
+ "@types/bun": "catalog:",
+ },
+ },
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "0.15.2",
@@ -830,6 +838,8 @@
"@opencode-ai/plugin": ["@opencode-ai/plugin@workspace:packages/plugin"],
+ "@opencode-ai/script": ["@opencode-ai/script@workspace:packages/script"],
+
"@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
"@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
diff --git a/package.json b/package.json
index 3da690359..14eec3cfa 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,8 @@
"turbo": "2.5.6"
},
"dependencies": {
- "@opencode-ai/sdk": "workspace:*"
+ "@opencode-ai/sdk": "workspace:*",
+ "@opencode-ai/script": "workspace:*"
},
"repository": {
"type": "git",
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index 16f643edd..a5dc4d044 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -28,7 +28,8 @@
"@types/yargs": "17.0.33",
"typescript": "catalog:",
"vscode-languageserver-types": "3.17.5",
- "zod-to-json-schema": "3.24.5"
+ "zod-to-json-schema": "3.24.5",
+ "@opencode-ai/script": "workspace:*"
},
"dependencies": {
"@clack/prompts": "1.0.0-alpha.1",
diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts
index 4c4bc7825..860147f06 100755
--- a/packages/opencode/script/build.ts
+++ b/packages/opencode/script/build.ts
@@ -5,6 +5,7 @@ process.chdir(dir)
import { $ } from "bun"
import pkg from "../package.json"
+import { Script } from "@opencode-ai/script"
const GOARCH: Record = {
arm64: "arm64",
@@ -25,12 +26,11 @@ const targets = [
await $`rm -rf dist`
const binaries: Record = {}
-const version = process.env["OPENCODE_VERSION"] ?? "dev"
for (const [os, arch] of targets) {
console.log(`building ${os}-${arch}`)
const name = `${pkg.name}-${os}-${arch}`
await $`mkdir -p dist/${name}/bin`
- await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`
+ await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${Script.version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`
.cwd("../tui")
.quiet()
@@ -43,12 +43,12 @@ for (const [os, arch] of targets) {
compile: {
target: `bun-${os}-${arch}` as any,
outfile: `dist/${name}/bin/opencode`,
- execArgv: [`--user-agent=opencode/${version}`, `--env-file=""`, `--`],
+ execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
windows: {},
},
entrypoints: ["./src/index.ts"],
define: {
- OPENCODE_VERSION: `'${version}'`,
+ OPENCODE_VERSION: `'${Script.version}'`,
OPENCODE_TUI_PATH: `'../../../dist/${name}/bin/tui'`,
},
})
@@ -57,7 +57,7 @@ for (const [os, arch] of targets) {
JSON.stringify(
{
name,
- version,
+ version: Script.version,
os: [os === "windows" ? "win32" : os],
cpu: [arch],
},
@@ -65,7 +65,7 @@ for (const [os, arch] of targets) {
2,
),
)
- binaries[name] = version
+ binaries[name] = Script.version
}
export { binaries }
diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts
index 948dcf252..e58956350 100755
--- a/packages/opencode/script/publish.ts
+++ b/packages/opencode/script/publish.ts
@@ -1,20 +1,10 @@
#!/usr/bin/env bun
+import { $ } from "bun"
+import pkg from "../package.json"
+import { Script } from "@opencode-ai/script"
+
const dir = new URL("..", import.meta.url).pathname
process.chdir(dir)
-import { $ } from "bun"
-
-import pkg from "../package.json"
-
-const snapshot = process.env["OPENCODE_SNAPSHOT"] === "true"
-let version = process.env["OPENCODE_VERSION"]
-const tag = process.env["OPENCODE_TAG"] ?? (snapshot ? "snapshot" : "latest")
-if (!version && snapshot) {
- version = `0.0.0-${tag}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
- process.env["OPENCODE_VERSION"] = version
-}
-if (!version) throw new Error("OPENCODE_VERSION is required")
-
-console.log(`publishing ${version}`)
const { binaries } = await import("./build.ts")
{
@@ -38,7 +28,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
preinstall: "node ./preinstall.mjs",
postinstall: "node ./postinstall.mjs",
},
- version,
+ version: Script.version,
optionalDependencies: binaries,
},
null,
@@ -46,11 +36,11 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
),
)
for (const [name] of Object.entries(binaries)) {
- await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${tag}`
+ await $`cd dist/${name} && chmod 777 -R . && bun publish --access public --tag ${Script.tag}`
}
-await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${tag}`
+await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${Script.tag}`
-if (!snapshot) {
+if (!Script.preview) {
for (const key of Object.keys(binaries)) {
await $`cd dist/${key}/bin && zip -r ../../${key}.zip *`
}
@@ -67,7 +57,7 @@ if (!snapshot) {
"# Maintainer: adam",
"",
"pkgname='opencode-bin'",
- `pkgver=${version.split("-")[0]}`,
+ `pkgver=${Script.version.split("-")[0]}`,
"options=('!debug' '!strip')",
"pkgrel=1",
"pkgdesc='The AI coding agent built for the terminal.'",
@@ -78,10 +68,10 @@ if (!snapshot) {
"conflicts=('opencode')",
"depends=('fzf' 'ripgrep')",
"",
- `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip")`,
+ `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.zip")`,
`sha256sums_aarch64=('${arm64Sha}')`,
"",
- `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip")`,
+ `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.zip")`,
`sha256sums_x86_64=('${x64Sha}')`,
"",
"package() {",
@@ -96,7 +86,7 @@ if (!snapshot) {
"# Maintainer: adam",
"",
"pkgname='opencode'",
- `pkgver=${version.split("-")[0]}`,
+ `pkgver=${Script.version.split("-")[0]}`,
"options=('!debug' '!strip')",
"pkgrel=1",
"pkgdesc='The AI coding agent built for the terminal.'",
@@ -108,7 +98,7 @@ if (!snapshot) {
"depends=('fzf' 'ripgrep')",
"makedepends=('git' 'bun-bin' 'go')",
"",
- `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v${version}.tar.gz")`,
+ `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v${Script.version}.tar.gz")`,
`sha256sums=('SKIP')`,
"",
"build() {",
@@ -139,7 +129,7 @@ if (!snapshot) {
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild)
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
- await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
+ await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"`
await $`cd ./dist/aur-${pkg} && git push`
break
} catch (e) {
@@ -157,11 +147,11 @@ if (!snapshot) {
"class Opencode < Formula",
` desc "The AI coding agent built for the terminal."`,
` homepage "https://github.com/sst/opencode"`,
- ` version "${version.split("-")[0]}"`,
+ ` version "${Script.version.split("-")[0]}"`,
"",
" on_macos do",
" if Hardware::CPU.intel?",
- ` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-darwin-x64.zip"`,
+ ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`,
` sha256 "${macX64Sha}"`,
"",
" def install",
@@ -169,7 +159,7 @@ if (!snapshot) {
" end",
" end",
" if Hardware::CPU.arm?",
- ` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-darwin-arm64.zip"`,
+ ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`,
` sha256 "${macArm64Sha}"`,
"",
" def install",
@@ -180,14 +170,14 @@ if (!snapshot) {
"",
" on_linux do",
" if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?",
- ` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip"`,
+ ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.zip"`,
` sha256 "${x64Sha}"`,
" def install",
' bin.install "opencode"',
" end",
" end",
" if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?",
- ` url "https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip"`,
+ ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.zip"`,
` sha256 "${arm64Sha}"`,
" def install",
' bin.install "opencode"',
@@ -203,6 +193,6 @@ if (!snapshot) {
await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap`
await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula)
await $`cd ./dist/homebrew-tap && git add opencode.rb`
- await $`cd ./dist/homebrew-tap && git commit -m "Update to v${version}"`
+ await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"`
await $`cd ./dist/homebrew-tap && git push`
}
diff --git a/packages/script/package.json b/packages/script/package.json
new file mode 100644
index 000000000..2791305f3
--- /dev/null
+++ b/packages/script/package.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://json.schemastore.org/package",
+ "name": "@opencode-ai/script",
+ "devDependencies": {
+ "@types/bun": "catalog:"
+ },
+ "exports": {
+ ".": "./src/index.ts"
+ }
+}
diff --git a/packages/script/src/index.ts b/packages/script/src/index.ts
new file mode 100644
index 000000000..4237a76a2
--- /dev/null
+++ b/packages/script/src/index.ts
@@ -0,0 +1,35 @@
+import { $ } from "bun"
+
+if (process.versions.bun !== "1.3.0") {
+ throw new Error("This script requires bun@1.3.0")
+}
+
+const TAG = process.env["OPENCODE_TAG"] ?? (await $`git branch --show-current`.text().then((x) => x.trim()))
+const IS_PREVIEW = TAG !== "latest"
+const VERSION = await (async () => {
+ if (IS_PREVIEW) return `0.0.0-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
+ const version = await fetch("https://registry.npmjs.org/opencode-ai/latest")
+ .then((res) => {
+ if (!res.ok) throw new Error(res.statusText)
+ return res.json()
+ })
+ .then((data: any) => data.version)
+ const [major, minor, patch] = version.split(".").map((x: string) => Number(x) || 0)
+ const t = process.env["OPENCODE_BUMP"]?.toLowerCase()
+ if (t === "major") return `${major + 1}.0.0`
+ if (t === "minor") return `${major}.${minor + 1}.0`
+ return `${major}.${minor}.${patch + 1}`
+})()
+
+export const Script = {
+ get tag() {
+ return TAG
+ },
+ get version() {
+ return VERSION
+ },
+ get preview() {
+ return IS_PREVIEW
+ },
+}
+console.log(`opencode script`, JSON.stringify(Script, null, 2))
diff --git a/packages/script/tsconfig.json b/packages/script/tsconfig.json
new file mode 100644
index 000000000..00ef12546
--- /dev/null
+++ b/packages/script/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@tsconfig/bun/tsconfig.json",
+ "compilerOptions": {
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "noUncheckedIndexedAccess": false
+ }
+}
diff --git a/script/publish.ts b/script/publish.ts
index 05d6df222..a4296ec4b 100755
--- a/script/publish.ts
+++ b/script/publish.ts
@@ -2,34 +2,13 @@
import { $ } from "bun"
import { createOpencode } from "@opencode-ai/sdk"
-if (process.versions.bun !== "1.3.0") {
- throw new Error("This script requires bun@1.3.0")
-}
+import { Script } from "@opencode-ai/script"
const notes = [] as string[]
console.log("=== publishing ===\n")
-const snapshot = process.env["OPENCODE_SNAPSHOT"] === "true"
-const version = await (async () => {
- if (snapshot) return `0.0.0-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
- if (process.env["OPENCODE_VERSION"]) return process.env["OPENCODE_VERSION"]
- const npmVersion = await fetch("https://registry.npmjs.org/opencode-ai/latest")
- .then((res) => {
- if (!res.ok) throw new Error(res.statusText)
- return res.json()
- })
- .then((data: any) => data.version)
- const [major, minor, patch] = npmVersion.split(".").map((x: string) => Number(x) || 0)
- const t = process.env["OPENCODE_BUMP"]?.toLowerCase()
- if (t === "major") return `${major + 1}.0.0`
- if (t === "minor") return `${major}.${minor + 1}.0`
- return `${major}.${minor}.${patch + 1}`
-})()
-process.env["OPENCODE_VERSION"] = version
-console.log("version:", version)
-
-if (!snapshot) {
+if (!Script.preview) {
const previous = await fetch("https://registry.npmjs.org/opencode-ai/latest")
.then((res) => {
if (!res.ok) throw new Error(res.statusText)
@@ -98,7 +77,7 @@ const pkgjsons = await Array.fromAsync(
const tree = await $`git add . && git write-tree`.text().then((x) => x.trim())
for (const file of pkgjsons) {
let pkg = await Bun.file(file).text()
- pkg = pkg.replaceAll(/"version": "[^"]+"/g, `"version": "${version}"`)
+ pkg = pkg.replaceAll(/"version": "[^"]+"/g, `"version": "${Script.version}"`)
console.log("updated:", file)
await Bun.file(file).write(pkg)
}
@@ -116,22 +95,22 @@ await import(`../packages/plugin/script/publish.ts`)
const dir = new URL("..", import.meta.url).pathname
process.chdir(dir)
-if (!snapshot) {
- await $`git commit -am "release: v${version}"`
- await $`git tag v${version}`
+if (!Script.preview) {
+ await $`git commit -am "release: v${Script.version}"`
+ await $`git tag v${Script.version}`
await $`git fetch origin`
await $`git cherry-pick HEAD..origin/dev`.nothrow()
await $`git push origin HEAD --tags --no-verify --force`
- await $`gh release create v${version} --title "v${version}" --notes ${notes.join("\n") ?? "No notable changes"} ./packages/opencode/dist/*.zip`
+ await $`gh release create v${Script.version} --title "v${Script.version}" --notes ${notes.join("\n") ?? "No notable changes"} ./packages/opencode/dist/*.zip`
}
-if (snapshot) {
- await $`git checkout -b snapshot-${version}`
- await $`git commit --allow-empty -m "Snapshot release v${version}"`
- await $`git tag v${version}`
- await $`git push origin v${version} --no-verify`
+if (Script.preview) {
+ await $`git checkout -b snapshot-${Script.version}`
+ await $`git commit --allow-empty -m "Snapshot release v${Script.version}"`
+ await $`git tag v${Script.version}`
+ await $`git push origin v${Script.version} --no-verify`
await $`git checkout dev`
- await $`git branch -D snapshot-${version}`
+ await $`git branch -D snapshot-${Script.version}`
for (const file of pkgjsons) {
await $`git checkout ${tree} ${file}`
}
From 9c129ef767357660a7dd6735897f2ab40d61d2e8 Mon Sep 17 00:00:00 2001
From: Dax Raad
Date: Tue, 14 Oct 2025 14:38:10 -0400
Subject: [PATCH 9/9] ci: fix
---
packages/css/package.json | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/css/package.json b/packages/css/package.json
index bce73606c..c4c28f2de 100644
--- a/packages/css/package.json
+++ b/packages/css/package.json
@@ -7,7 +7,6 @@
"./*": "./src/*"
},
"scripts": {
- "build": "bun run build.ts",
"dev": "bun run dev.ts"
}
}