Compare commits

..

10 Commits

Author SHA1 Message Date
opencode 104d52bc38 release: v1.0.214 2025-12-30 16:37:22 +00:00
Adam dff1fe2d28 fix(desktop): sort servers by health 2025-12-30 10:34:55 -06:00
Adam 72ab4260ee fix(desktop): don't persist fallback server urls 2025-12-30 10:31:48 -06:00
Adam 9e9b4a0555 fix(share): broken share pages 2025-12-30 10:27:06 -06:00
Adam e53192889c fix(app): better text selection 2025-12-30 10:21:37 -06:00
Rohan Mukherjee 23bbfb3d15 fix: cloudflare provider information (#6426) 2025-12-30 09:45:09 -06:00
opencode-agent[bot] 37da005a01 docs: projects, find.files, notifications (#6438)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-30 09:44:09 -06:00
Didier Durand 8b708242f1 chore: fix various typos (#6429) 2025-12-30 09:42:04 -06:00
GitHub Action 339d2dcb98 chore: generate 2025-12-30 15:28:59 +00:00
Adam bbc8678164 fix(desktop): share projects across all local servers 2025-12-30 09:28:19 -06:00
39 changed files with 232 additions and 106 deletions
+15 -15
View File
@@ -22,7 +22,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -70,7 +70,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -98,7 +98,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -125,7 +125,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -149,7 +149,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -173,7 +173,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@solid-primitives/storage": "catalog:",
@@ -201,7 +201,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -230,7 +230,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -246,7 +246,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.0.213",
"version": "1.0.214",
"bin": {
"opencode": "./bin/opencode",
},
@@ -347,7 +347,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@@ -367,7 +367,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.0.213",
"version": "1.0.214",
"devDependencies": {
"@hey-api/openapi-ts": "0.88.1",
"@tsconfig/node22": "catalog:",
@@ -378,7 +378,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -391,7 +391,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -429,7 +429,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"zod": "catalog:",
},
@@ -440,7 +440,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.0.213",
"version": "1.0.214",
"description": "",
"type": "module",
"exports": {
@@ -46,6 +46,25 @@ export function DialogSelectServer() {
const current = createMemo(() => items().find((x) => x === server.url) ?? items()[0])
const sortedItems = createMemo(() => {
const list = items()
if (!list.length) return list
const active = current()
const order = new Map(list.map((url, index) => [url, index] as const))
const rank = (value?: ServerStatus) => {
if (value?.healthy === true) return 0
if (value?.healthy === false) return 2
return 1
}
return list.slice().sort((a, b) => {
if (a === active) return -1
if (b === active) return 1
const diff = rank(store.status[a]) - rank(store.status[b])
if (diff !== 0) return diff
return (order.get(a) ?? 0) - (order.get(b) ?? 0)
})
})
async function refreshHealth() {
const results: Record<string, ServerStatus> = {}
await Promise.all(
@@ -63,9 +82,14 @@ export function DialogSelectServer() {
onCleanup(() => clearInterval(interval))
})
function select(value: string) {
if (store.status[value]?.healthy === false) return
function select(value: string, persist?: boolean) {
if (!persist && store.status[value]?.healthy === false) return
dialog.close()
if (persist) {
server.add(value)
navigate("/")
return
}
server.setActive(value)
navigate("/")
}
@@ -87,7 +111,7 @@ export function DialogSelectServer() {
}
setStore("url", "")
select(value)
select(value, true)
}
return (
@@ -96,7 +120,7 @@ export function DialogSelectServer() {
<List
search={{ placeholder: "Search servers", autofocus: true }}
emptyMessage="No servers yet"
items={items}
items={sortedItems}
key={(x) => x}
current={current()}
onSelect={(x) => {
@@ -1321,6 +1321,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onInput={handleInput}
onKeyDown={handleKeyDown}
classList={{
"select-text": true,
"w-full px-5 py-3 pr-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
"[&_[data-type=file]]:text-icon-info-active": true,
"font-mono!": store.mode === "shell",
+1
View File
@@ -233,6 +233,7 @@ export const Terminal = (props: TerminalProps) => {
style={{ "background-color": terminalColors().background }}
classList={{
...(local.classList ?? {}),
"select-text": true,
"size-full px-6 py-3 font-mono": true,
[local.class ?? ""]: !!local.class,
}}
+45 -29
View File
@@ -23,6 +23,13 @@ export function serverDisplayName(url: string) {
.split("/")[0]
}
function projectsKey(url: string) {
if (!url) return ""
const host = url.replace(/^https?:\/\//, "").split(":")[0]
if (host === "localhost" || host === "127.0.0.1") return "local"
return url
}
export const { use: useServer, provider: ServerProvider } = createSimpleContext({
name: "Server",
init: (props: { defaultUrl: string }) => {
@@ -41,6 +48,19 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
function setActive(input: string) {
const url = normalizeServerUrl(input)
if (!url) return
setActiveRaw(url)
}
function add(input: string) {
const url = normalizeServerUrl(input)
if (!url) return
const fallback = normalizeServerUrl(props.defaultUrl)
if (fallback && url === fallback) {
setActiveRaw(url)
return
}
batch(() => {
if (!store.list.includes(url)) {
setStore("list", store.list.length, url)
@@ -64,15 +84,10 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
createEffect(() => {
if (!ready()) return
if (active()) return
const url = normalizeServerUrl(props.defaultUrl)
if (!url) return
batch(() => {
if (!store.list.includes(url)) {
setStore("list", store.list.length, url)
}
setActiveRaw(url)
})
setActiveRaw(url)
})
const isReady = createMemo(() => ready() && !!active())
@@ -100,7 +115,8 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
onCleanup(() => clearInterval(interval))
})
const projectsList = createMemo(() => store.projects[active()] ?? [])
const origin = createMemo(() => projectsKey(active()))
const projectsList = createMemo(() => store.projects[origin()] ?? [])
return {
ready: isReady,
@@ -115,51 +131,51 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
return store.list
},
setActive,
add: setActive,
add,
remove,
projects: {
list: projectsList,
open(directory: string) {
const url = active()
if (!url) return
const current = store.projects[url] ?? []
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
if (current.find((x) => x.worktree === directory)) return
setStore("projects", url, [{ worktree: directory, expanded: true }, ...current])
setStore("projects", key, [{ worktree: directory, expanded: true }, ...current])
},
close(directory: string) {
const url = active()
if (!url) return
const current = store.projects[url] ?? []
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
setStore(
"projects",
url,
key,
current.filter((x) => x.worktree !== directory),
)
},
expand(directory: string) {
const url = active()
if (!url) return
const current = store.projects[url] ?? []
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
const index = current.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", url, index, "expanded", true)
if (index !== -1) setStore("projects", key, index, "expanded", true)
},
collapse(directory: string) {
const url = active()
if (!url) return
const current = store.projects[url] ?? []
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
const index = current.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", url, index, "expanded", false)
if (index !== -1) setStore("projects", key, index, "expanded", false)
},
move(directory: string, toIndex: number) {
const url = active()
if (!url) return
const current = store.projects[url] ?? []
const key = origin()
if (!key) return
const current = store.projects[key] ?? []
const fromIndex = current.findIndex((x) => x.worktree === directory)
if (fromIndex === -1 || fromIndex === toIndex) return
const result = [...current]
const [item] = result.splice(fromIndex, 1)
result.splice(toIndex, 0, item)
setStore("projects", url, result)
setStore("projects", key, result)
},
},
}
+1 -1
View File
@@ -1015,7 +1015,7 @@ export default function Layout(props: ParentProps) {
}
return (
<div class="relative flex-1 min-h-0 flex flex-col">
<div class="relative flex-1 min-h-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
<Header
navigateToProject={navigateToProject}
navigateToSession={navigateToSession}
+3 -3
View File
@@ -858,7 +858,7 @@ export default function Page() {
</Tabs.List>
</div>
<Show when={diffs().length}>
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden contain-strict">
<Tabs.Content value="review" class="flex flex-col h-full overflow-hidden contain-strict">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
<SessionReview
classes={{
@@ -884,7 +884,7 @@ export default function Page() {
},
)
return (
<Tabs.Content value={tab} class="select-text mt-3">
<Tabs.Content value={tab} class="mt-3">
<Switch>
<Match when={file()}>
{(f) => (
@@ -896,7 +896,7 @@ export default function Page() {
cacheKey: checksum(f().content?.content ?? ""),
}}
overflow="scroll"
class="pb-40"
class="select-text pb-40"
/>
)}
</Match>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.0.213",
"version": "1.0.214",
"private": true,
"type": "module",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.0.213",
"version": "1.0.214",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.0.213",
"version": "1.0.214",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"scripts": {
"typecheck": "tsgo -b",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.0.213",
"version": "1.0.214",
"private": true,
"type": "module",
"scripts": {
+8 -5
View File
@@ -3,6 +3,7 @@ import { FileRoutes } from "@solidjs/start/router"
import { Font } from "@opencode-ai/ui/font"
import { MetaProvider } from "@solidjs/meta"
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { Suspense } from "solid-js"
import "./app.css"
import { Favicon } from "@opencode-ai/ui/favicon"
@@ -12,11 +13,13 @@ export default function App() {
<Router
root={(props) => (
<MetaProvider>
<MarkedProvider>
<Favicon />
<Font />
<Suspense>{props.children}</Suspense>
</MarkedProvider>
<DialogProvider>
<MarkedProvider>
<Favicon />
<Font />
<Suspense>{props.children}</Suspense>
</MarkedProvider>
</DialogProvider>
</MetaProvider>
)}
>
@@ -162,11 +162,20 @@ export default function () {
return (
<ErrorBoundary
fallback={(e) => {
fallback={(error) => {
if (SessionDataMissingError.isInstance(error)) {
return <NotFound />
}
console.error(error)
const details = error instanceof Error ? (error.stack ?? error.message) : String(error)
return (
<Show when={e.message === "SessionDataMissingError"}>
<NotFound />
</Show>
<div class="min-h-screen w-full bg-background-base text-text-base flex flex-col items-center justify-center gap-4 p-6 text-center">
<p class="text-16-medium">Unable to render this share.</p>
<p class="text-14-regular text-text-weaker">Check the console for more details.</p>
<pre class="text-12-mono text-left whitespace-pre-wrap break-words w-full max-w-200 bg-background-stronger rounded-md p-4">
{details}
</pre>
</div>
)
}}
>
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.0.213"
version = "1.0.214"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/sst/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.213/opencode-darwin-arm64.zip"
archive = "https://github.com/sst/opencode/releases/download/v1.0.214/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.213/opencode-darwin-x64.zip"
archive = "https://github.com/sst/opencode/releases/download/v1.0.214/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.213/opencode-linux-arm64.tar.gz"
archive = "https://github.com/sst/opencode/releases/download/v1.0.214/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.213/opencode-linux-x64.tar.gz"
archive = "https://github.com/sst/opencode/releases/download/v1.0.214/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.213/opencode-windows-x64.zip"
archive = "https://github.com/sst/opencode/releases/download/v1.0.214/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.0.213",
"version": "1.0.214",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.0.213",
"version": "1.0.214",
"name": "opencode",
"type": "module",
"private": true,
+6
View File
@@ -349,6 +349,12 @@ export const AuthLoginCommand = cmd({
prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token")
}
if (["cloudflare", "cloudflare-ai-gateway"].includes(provider)) {
prompts.log.info(
"Cloudflare AI Gateway can be configured with CLOUDFLARE_GATEWAY_ID, CLOUDFLARE_ACCOUNT_ID, and CLOUDFLARE_API_TOKEN environment variables. Read more: https://opencode.ai/docs/providers/#cloudflare-ai-gateway",
)
}
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
@@ -240,7 +240,7 @@ You are producing plain text that will later be styled by the CLI. Follow these
- Choose descriptive names that fit the content
- Keep headers short (13 words) and in `**Title Case**`. Always start headers with `**` and end with `**`
- Leave no blank line before the first bullet under a header.
- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.
- Section headers should only be used where they genuinely improve scannability; avoid fragmenting the answer.
**Bullets**
@@ -289,7 +289,7 @@ When referencing files in your response, make sure to include the relevant start
- Dont nest bullets or create deep hierarchies.
- Dont output ANSI escape codes directly — the CLI renderer applies them.
- Dont cram unrelated keywords into a single bullet; split for clarity.
- Dont let keyword lists run long — wrap or reformat for scanability.
- Dont let keyword lists run long — wrap or reformat for scannability.
Generally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with whats needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.
@@ -12,7 +12,7 @@ is a critical violation. ZERO exceptions.
## Responsibility
Your current responsibility is to think, read, search, and delegate explore agents to construct a well formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity.
Your current responsibility is to think, read, search, and delegate explore agents to construct a well-formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity.
Ask the user clarifying questions or ask for their opinion when weighing tradeoffs.
@@ -22,5 +22,5 @@ Ask the user clarifying questions or ask for their opinion when weighing tradeof
## Important
The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received.
The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received.
</system-reminder>
+1 -1
View File
@@ -280,7 +280,7 @@ export const BashTool = Tool.define("bash", async () => {
}
if (timedOut) {
resultMetadata.push(`bash tool terminated commmand after exceeding timeout ${timeout} ms`)
resultMetadata.push(`bash tool terminated command after exceeding timeout ${timeout} ms`)
}
if (aborted) {
+1 -1
View File
@@ -1,4 +1,4 @@
Launch a new agent to handle complex, multi-step tasks autonomously.
Launch a new agent to handle complex, multistep tasks autonomously.
Available agent types and the tools they have access to:
{agents}
+1 -1
View File
@@ -4,7 +4,7 @@ It also helps the user understand the progress of the task and overall progress
## When to Use This Tool
Use this tool proactively in these scenarios:
1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
1. Complex multistep tasks - When a task requires 3 or more distinct steps or actions
2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
3. User explicitly requests todo list - When the user directly asks you to use the todo list
4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"scripts": {
"dev": "bun run src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.0.213",
"version": "1.0.214",
"type": "module",
"exports": {
"./*": "./src/components/*.tsx",
@@ -377,6 +377,18 @@
}
}
[data-component="user-message"] [data-slot="user-message-text"],
[data-component="text-part"],
[data-component="reasoning-part"],
[data-component="tool-error"],
[data-component="tool-output"],
[data-component="edit-content"],
[data-component="write-content"],
[data-component="todos"],
[data-component="diagnostics"] {
user-select: text;
}
[data-component="tool-part-wrapper"] {
width: 100%;
@@ -65,6 +65,10 @@
}
}
[data-slot="accordion-content"] {
user-select: text;
}
[data-slot="session-review-trigger-content"] {
display: flex;
align-items: center;
@@ -116,6 +116,11 @@
color: var(--text-weak);
}
[data-slot="session-turn-markdown"],
[data-slot="session-turn-accordion"] [data-slot="accordion-content"] {
user-select: text;
}
[data-slot="session-turn-markdown"] {
&[data-diffs="true"] {
font-size: 15px;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/util",
"version": "1.0.213",
"version": "1.0.214",
"private": true,
"type": "module",
"exports": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/web",
"type": "module",
"version": "1.0.213",
"version": "1.0.214",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
@@ -118,6 +118,29 @@ You can disable a keybind by adding the key to your config with a value of "none
---
## Desktop prompt shortcuts
The OpenCode desktop app prompt input supports common Readline/Emacs-style shortcuts for editing text. These are built-in and currently not configurable via `opencode.json`.
| Shortcut | Action |
| -------- | ---------------------------------------- |
| `ctrl+a` | Move to start of current line |
| `ctrl+e` | Move to end of current line |
| `ctrl+b` | Move cursor back one character |
| `ctrl+f` | Move cursor forward one character |
| `alt+b` | Move cursor back one word |
| `alt+f` | Move cursor forward one word |
| `ctrl+d` | Delete character under cursor |
| `ctrl+k` | Kill to end of line |
| `ctrl+u` | Kill to start of line |
| `ctrl+w` | Kill previous word |
| `alt+d` | Kill next word |
| `ctrl+y` | Yank (paste) last killed text |
| `ctrl+t` | Transpose characters |
| `ctrl+g` | Cancel popovers / abort running response |
---
## Shift+Enter
Some terminals don't send modifier keys with Enter by default. You may need to configure your terminal to send `Shift+Enter` as an escape sequence.
@@ -230,6 +230,10 @@ export const NotificationPlugin = async ({ project, client, $, directory, worktr
We are using `osascript` to run AppleScript on macOS. Here we are using it to send notifications.
:::note
If youre using the OpenCode desktop app, it can send system notifications automatically when a response is ready or when a session errors.
:::
---
### .env protection
+18 -8
View File
@@ -283,13 +283,19 @@ await client.session.prompt({
### Files
| Method | Description | Response |
| ------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------- |
| `find.text({ query })` | Search for text in files | Array of match objects with `path`, `lines`, `line_number`, `absolute_offset`, `submatches` |
| `find.files({ query })` | Find files by name | `string[]` (file paths) |
| `find.symbols({ query })` | Find workspace symbols | <a href={typesUrl}><code>Symbol[]</code></a> |
| `file.read({ query })` | Read a file | `{ type: "raw" \| "patch", content: string }` |
| `file.status({ query? })` | Get status for tracked files | <a href={typesUrl}><code>File[]</code></a> |
| Method | Description | Response |
| ------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------- |
| `find.text({ query })` | Search for text in files | Array of match objects with `path`, `lines`, `line_number`, `absolute_offset`, `submatches` |
| `find.files({ query })` | Find files and directories by name | `string[]` (paths) |
| `find.symbols({ query })` | Find workspace symbols | <a href={typesUrl}><code>Symbol[]</code></a> |
| `file.read({ query })` | Read a file | `{ type: "raw" \| "patch", content: string }` |
| `file.status({ query? })` | Get status for tracked files | <a href={typesUrl}><code>File[]</code></a> |
`find.files` supports a few optional query fields:
- `type`: `"file"` or `"directory"`
- `directory`: override the project root for the search
- `limit`: max results (1200)
---
@@ -302,7 +308,11 @@ const textResults = await client.find.text({
})
const files = await client.find.files({
query: { query: "*.ts" },
query: { query: "*.ts", type: "file" },
})
const directories = await client.find.files({
query: { query: "packages", type: "directory", limit: 20 },
})
const content = await client.file.read({
+16 -8
View File
@@ -173,14 +173,22 @@ The opencode server exposes the following APIs.
### Files
| Method | Path | Description | Response |
| ------ | ------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------- |
| `GET` | `/find?pattern=<pat>` | Search for text in files | Array of match objects with `path`, `lines`, `line_number`, `absolute_offset`, `submatches` |
| `GET` | `/find/file?query=<q>` | Find files by name | `string[]` (file paths) |
| `GET` | `/find/symbol?query=<q>` | Find workspace symbols | <a href={typesUrl}><code>Symbol[]</code></a> |
| `GET` | `/file?path=<path>` | List files and directories | <a href={typesUrl}><code>FileNode[]</code></a> |
| `GET` | `/file/content?path=<p>` | Read a file | <a href={typesUrl}><code>FileContent</code></a> |
| `GET` | `/file/status` | Get status for tracked files | <a href={typesUrl}><code>File[]</code></a> |
| Method | Path | Description | Response |
| ------ | ------------------------ | ---------------------------------- | ------------------------------------------------------------------------------------------- |
| `GET` | `/find?pattern=<pat>` | Search for text in files | Array of match objects with `path`, `lines`, `line_number`, `absolute_offset`, `submatches` |
| `GET` | `/find/file?query=<q>` | Find files and directories by name | `string[]` (paths) |
| `GET` | `/find/symbol?query=<q>` | Find workspace symbols | <a href={typesUrl}><code>Symbol[]</code></a> |
| `GET` | `/file?path=<path>` | List files and directories | <a href={typesUrl}><code>FileNode[]</code></a> |
| `GET` | `/file/content?path=<p>` | Read a file | <a href={typesUrl}><code>FileContent</code></a> |
| `GET` | `/file/status` | Get status for tracked files | <a href={typesUrl}><code>File[]</code></a> |
#### `/find/file` query parameters
- `query` (required) — search string (fuzzy match)
- `type` (optional) — limit results to `"file"` or `"directory"`
- `directory` (optional) — override the project root for the search
- `limit` (optional) — max results (1200)
- `dirs` (optional) — legacy flag (`"false"` returns only files)
---
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "opencode",
"displayName": "opencode",
"description": "opencode for VS Code",
"version": "1.0.213",
"version": "1.0.214",
"publisher": "sst-dev",
"repository": {
"type": "git",