mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
Merge branch 'dev' into docs-snapshot-config
This commit is contained in:
commit
672c179746
37 changed files with 237 additions and 132 deletions
1
STATS.md
1
STATS.md
|
|
@ -151,3 +151,4 @@
|
|||
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
|
||||
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
|
||||
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
|
||||
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
|
||||
|
|
|
|||
34
bun.lock
34
bun.lock
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
|
|
@ -191,7 +191,7 @@
|
|||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
|
|
@ -207,7 +207,7 @@
|
|||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
|
|
@ -314,7 +314,7 @@
|
|||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
|
|
@ -325,7 +325,7 @@
|
|||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
|
|
@ -338,7 +338,7 @@
|
|||
},
|
||||
"packages/tauri": {
|
||||
"name": "@opencode-ai/tauri",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
|
|
@ -351,7 +351,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -383,7 +383,7 @@
|
|||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
|
@ -439,7 +439,7 @@
|
|||
"@hono/zod-validator": "0.4.2",
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@pierre/precision-diffs": "0.5.5",
|
||||
"@pierre/precision-diffs": "0.5.7",
|
||||
"@solidjs/meta": "0.29.4",
|
||||
"@solidjs/router": "0.15.4",
|
||||
"@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
|
||||
|
|
@ -1214,7 +1214,7 @@
|
|||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
|
||||
|
||||
"@pierre/precision-diffs": ["@pierre/precision-diffs@0.5.5", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/transformers": "3.15.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.15.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-mmDHEWWQ6fmXY5qRNHqodzOxHPwLqVNbbnO/MOpXteOTjd0nVIGy5IcaNwU2WSxhxQRwaUepKyx5+wwPcZLEmw=="],
|
||||
"@pierre/precision-diffs": ["@pierre/precision-diffs@0.5.7", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/transformers": "3.15.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.15.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-Y+e4kJ9pT2I4NS5fE39KdoiXtwMkVPRvrwLM6O2IqO7PDCRWLBS7CYxcSgSyngEndccUll2krx66I2QnfO0Ovg=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
|
|
|
|||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1763934636,
|
||||
"narHash": "sha256-9glbI7f1uU+yzQCq5LwLgdZqx6svOhZWkd4JRY265fc=",
|
||||
"lastModified": 1764081664,
|
||||
"narHash": "sha256-sUoHmPr/EwXzRMpv1u/kH+dXuvJEyyF2Q7muE+t0EU4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ee09932cedcef15aaf476f9343d1dea2cb77e261",
|
||||
"rev": "dc205f7b4fdb04c8b7877b43edb7b73be7730081",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"nodeModules": "sha256-cieNNPXZd4Bg9bZtRq2H8L99e24U8p5d+d76SE7SeJc="
|
||||
"nodeModules": "sha256-XFJXjBWbHIUWKdSOZkGW7VmUPBVtGRgLHM2/1xVThFc="
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"@tsconfig/bun": "1.0.9",
|
||||
"@cloudflare/workers-types": "4.20251008.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@pierre/precision-diffs": "0.5.5",
|
||||
"@pierre/precision-diffs": "0.5.7",
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"diff": "8.0.2",
|
||||
"ai": "5.0.97",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ export default function Page() {
|
|||
flex: layout.review.state() === "pane",
|
||||
}}
|
||||
>
|
||||
<div class="relative shrink-0 px-6 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-2xl mx-auto">
|
||||
<div class="relative shrink-0 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-2xl mx-auto">
|
||||
<Switch>
|
||||
<Match when={session.id}>
|
||||
<div class="flex items-start justify-start h-full min-h-0">
|
||||
|
|
@ -364,7 +364,7 @@ export default function Page() {
|
|||
<SessionTurn
|
||||
sessionID={session.id!}
|
||||
messageID={session.messages.active()?.id!}
|
||||
classes={{ root: "pb-20 flex-1 min-w-0", content: "pb-20" }}
|
||||
classes={{ root: "pb-20 flex-1 min-w-0", content: "pb-20", container: "px-6" }}
|
||||
/>
|
||||
</div>
|
||||
</Match>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ export default function () {
|
|||
)
|
||||
|
||||
const turns = () => (
|
||||
<div class="relative mt-2 pt-6 pb-8 px-4 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
|
||||
{title()}
|
||||
<div class="relative mt-2 pt-6 pb-8 min-w-0 w-full h-full overflow-y-auto no-scrollbar">
|
||||
<div class="px-4">{title()}</div>
|
||||
<div class="flex flex-col gap-15 items-start justify-start mt-4">
|
||||
<For each={messages()}>
|
||||
{(message) => (
|
||||
|
|
@ -202,18 +202,20 @@ export default function () {
|
|||
root: "min-w-0 w-full relative",
|
||||
content:
|
||||
"flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
|
||||
container: "px-4",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pt-20 pb-8 shrink-0">
|
||||
<div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0">
|
||||
<Logo class="w-58.5 opacity-12" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const wide = createMemo(() => diffs().length === 0)
|
||||
const columnPadding = () => (wide() ? "px-6" : "px-21 @4xl:px-6")
|
||||
|
||||
return (
|
||||
<div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
|
||||
|
|
@ -245,11 +247,10 @@ export default function () {
|
|||
<div
|
||||
classList={{
|
||||
"@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true,
|
||||
"px-21 @4xl:px-6 max-w-2xl": !wide(),
|
||||
"px-6 max-w-2xl": wide(),
|
||||
"max-w-2xl": true,
|
||||
}}
|
||||
>
|
||||
{title()}
|
||||
<div class={columnPadding()}>{title()}</div>
|
||||
<div class="flex items-start justify-start h-full min-h-0">
|
||||
<Show when={messages().length > 1}>
|
||||
<>
|
||||
|
|
@ -285,9 +286,13 @@ export default function () {
|
|||
<SessionTurn
|
||||
sessionID={data().sessionID}
|
||||
messageID={store.messageId ?? firstUserMessage()!.id!}
|
||||
classes={{ root: "grow", content: "flex flex-col justify-between", container: "pb-20" }}
|
||||
classes={{
|
||||
root: "grow",
|
||||
content: "flex flex-col justify-between",
|
||||
container: `${columnPadding()} pb-20`,
|
||||
}}
|
||||
>
|
||||
<div class="flex items-center justify-center pb-8 shrink-0">
|
||||
<div class={`${columnPadding()} flex items-center justify-center pb-8 shrink-0`}>
|
||||
<Logo class="w-58.5 opacity-12" />
|
||||
</div>
|
||||
</SessionTurn>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The AI coding agent built for the terminal"
|
||||
version = "1.0.114"
|
||||
version = "1.0.115"
|
||||
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.114/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.115/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.114/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.115/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.114/opencode-linux-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.115/opencode-linux-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.114/opencode-linux-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.115/opencode-linux-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.114/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.115/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ function App() {
|
|||
<box paddingLeft={1} paddingRight={1}>
|
||||
<text fg={theme.textMuted}>
|
||||
{process.cwd().replace(Global.Path.home, "~")}
|
||||
{sync.data.vcs?.vcs?.branch ? `:${sync.data.vcs.vcs.branch}` : ""}
|
||||
{sync.data.vcs?.branch ? `:${sync.data.vcs.branch}` : ""}
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
|
|
|
|||
|
|
@ -496,6 +496,40 @@ export function Prompt(props: PromptProps) {
|
|||
}
|
||||
const exit = useExit()
|
||||
|
||||
function pasteText(text: string, virtualText: string) {
|
||||
const currentOffset = input.visualCursor.offset
|
||||
const extmarkStart = currentOffset
|
||||
const extmarkEnd = extmarkStart + virtualText.length
|
||||
|
||||
input.insertText(virtualText + " ")
|
||||
|
||||
const extmarkId = input.extmarks.create({
|
||||
start: extmarkStart,
|
||||
end: extmarkEnd,
|
||||
virtual: true,
|
||||
styleId: pasteStyleId,
|
||||
typeId: promptPartTypeId,
|
||||
})
|
||||
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
const partIndex = draft.prompt.parts.length
|
||||
draft.prompt.parts.push({
|
||||
type: "text" as const,
|
||||
text,
|
||||
source: {
|
||||
text: {
|
||||
start: extmarkStart,
|
||||
end: extmarkEnd,
|
||||
value: virtualText,
|
||||
},
|
||||
},
|
||||
})
|
||||
draft.extmarkToPartIndex.set(extmarkId, partIndex)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async function pasteImage(file: { filename?: string; content: string; mime: string }) {
|
||||
const currentOffset = input.visualCursor.offset
|
||||
const extmarkStart = currentOffset
|
||||
|
|
@ -709,12 +743,21 @@ export function Prompt(props: PromptProps) {
|
|||
if (!isUrl) {
|
||||
try {
|
||||
const file = Bun.file(filepath)
|
||||
// Handle SVG as raw text content, not as base64 image
|
||||
if (file.type === "image/svg+xml") {
|
||||
event.preventDefault()
|
||||
const content = await file.text().catch(() => {})
|
||||
if (content) {
|
||||
pasteText(content, `[SVG: ${file.name ?? "image"}]`)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (file.type.startsWith("image/")) {
|
||||
event.preventDefault()
|
||||
const content = await file
|
||||
.arrayBuffer()
|
||||
.then((buffer) => Buffer.from(buffer).toString("base64"))
|
||||
.catch(console.error)
|
||||
.catch(() => {})
|
||||
if (content) {
|
||||
await pasteImage({
|
||||
filename: file.name,
|
||||
|
|
@ -733,41 +776,7 @@ export function Prompt(props: PromptProps) {
|
|||
!sync.data.config.experimental?.disable_paste_summary
|
||||
) {
|
||||
event.preventDefault()
|
||||
const currentOffset = input.visualCursor.offset
|
||||
const virtualText = `[Pasted ~${lineCount} lines]`
|
||||
const textToInsert = virtualText + " "
|
||||
const extmarkStart = currentOffset
|
||||
const extmarkEnd = extmarkStart + virtualText.length
|
||||
|
||||
input.insertText(textToInsert)
|
||||
|
||||
const extmarkId = input.extmarks.create({
|
||||
start: extmarkStart,
|
||||
end: extmarkEnd,
|
||||
virtual: true,
|
||||
styleId: pasteStyleId,
|
||||
typeId: promptPartTypeId,
|
||||
})
|
||||
|
||||
const part = {
|
||||
type: "text" as const,
|
||||
text: pastedContent,
|
||||
source: {
|
||||
text: {
|
||||
start: extmarkStart,
|
||||
end: extmarkEnd,
|
||||
value: virtualText,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
setStore(
|
||||
produce((draft) => {
|
||||
const partIndex = draft.prompt.parts.length
|
||||
draft.prompt.parts.push(part)
|
||||
draft.extmarkToPartIndex.set(extmarkId, partIndex)
|
||||
}),
|
||||
)
|
||||
pasteText(pastedContent, `[Pasted ~${lineCount} lines]`)
|
||||
return
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useRenderer } from "@opentui/solid"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { FormatError } from "@/cli/error"
|
||||
import { FormatError, FormatUnknownError } from "@/cli/error"
|
||||
|
||||
export const { use: useExit, provider: ExitProvider } = createSimpleContext({
|
||||
name: "Exit",
|
||||
|
|
@ -10,8 +10,10 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
|
|||
renderer.destroy()
|
||||
await input.onExit?.()
|
||||
if (reason) {
|
||||
const formatted = FormatError(reason) ?? JSON.stringify(reason)
|
||||
process.stderr.write(formatted + "\n")
|
||||
const formatted = FormatError(reason) ?? FormatUnknownError(reason)
|
||||
if (formatted) {
|
||||
process.stderr.write(formatted + "\n")
|
||||
}
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { createSimpleContext } from "./helper"
|
|||
import type { Snapshot } from "@/snapshot"
|
||||
import { useExit } from "./exit"
|
||||
import { batch, onMount } from "solid-js"
|
||||
import { Log } from "@/util/log"
|
||||
|
||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
name: "Sync",
|
||||
|
|
@ -243,7 +244,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
}
|
||||
|
||||
case "vcs.branch.updated": {
|
||||
setStore("vcs", "vcs", { branch: event.properties.branch })
|
||||
setStore("vcs", { branch: event.properties.branch })
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -290,6 +291,11 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
})
|
||||
})
|
||||
.catch(async (e) => {
|
||||
Log.Default.error("tui bootstrap failed", {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
name: e instanceof Error ? e.name : undefined,
|
||||
stack: e instanceof Error ? e.stack : undefined,
|
||||
})
|
||||
await exit(e)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,3 +38,18 @@ export function FormatError(input: unknown) {
|
|||
|
||||
if (UI.CancelledError.isInstance(input)) return ""
|
||||
}
|
||||
|
||||
export function FormatUnknownError(input: unknown): string {
|
||||
if (input instanceof Error) {
|
||||
return input.stack ?? `${input.name}: ${input.message}`
|
||||
}
|
||||
|
||||
if (typeof input === "object" && input !== null) {
|
||||
try {
|
||||
const json = JSON.stringify(input, null, 2)
|
||||
if (json && json !== "{}") return json
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return String(input)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ export namespace Vcs {
|
|||
),
|
||||
}
|
||||
|
||||
export const Info = z
|
||||
.object({
|
||||
branch: z.string(),
|
||||
})
|
||||
.meta({
|
||||
ref: "VcsInfo",
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
async function currentBranch() {
|
||||
return $`git rev-parse --abbrev-ref HEAD`
|
||||
.quiet()
|
||||
|
|
|
|||
|
|
@ -376,22 +376,7 @@ export namespace Server {
|
|||
description: "VCS info",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(
|
||||
z
|
||||
.object({
|
||||
worktree: z.string(),
|
||||
directory: z.string(),
|
||||
projectID: z.string(),
|
||||
vcs: z
|
||||
.object({
|
||||
branch: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.meta({
|
||||
ref: "VcsInfo",
|
||||
}),
|
||||
),
|
||||
schema: resolver(Vcs.Info),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -400,10 +385,7 @@ export namespace Server {
|
|||
async (c) => {
|
||||
const branch = await Vcs.branch()
|
||||
return c.json({
|
||||
worktree: Instance.worktree,
|
||||
directory: Instance.directory,
|
||||
projectID: Instance.project.id,
|
||||
vcs: Instance.project.vcs ? { branch } : undefined,
|
||||
branch,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -150,9 +150,12 @@ export namespace SessionPrompt {
|
|||
},
|
||||
]
|
||||
const files = ConfigMarkdown.files(template)
|
||||
const seen = new Set<string>()
|
||||
await Promise.all(
|
||||
files.map(async (match) => {
|
||||
const name = match[1]
|
||||
if (seen.has(name)) return
|
||||
seen.add(name)
|
||||
const filepath = name.startsWith("~/")
|
||||
? path.join(os.homedir(), name.slice(2))
|
||||
: path.resolve(Instance.worktree, name)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
|
|
|||
|
|
@ -1264,12 +1264,7 @@ export type Path = {
|
|||
}
|
||||
|
||||
export type VcsInfo = {
|
||||
worktree: string
|
||||
directory: string
|
||||
projectID: string
|
||||
vcs?: {
|
||||
branch: string
|
||||
}
|
||||
branch: string
|
||||
}
|
||||
|
||||
export type NotFoundError = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/tauri",
|
||||
"private": true,
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./*": "./src/components/*.tsx",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { AssistantMessage as AssistantMessageType, ToolPart } from "@openco
|
|||
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) {
|
||||
const data = useData()
|
||||
const sanitizer = createMemo(() => (data.directory ? new RegExp(`${data.directory}/`, "g") : undefined))
|
||||
const parts = createMemo(() => props.assistantMessages().flatMap((m) => data.part[m.id]))
|
||||
const parts = createMemo(() => props.assistantMessages().flatMap((m) => data.store.part[m.id]))
|
||||
const done = createMemo(() => props.done ?? false)
|
||||
const currentTask = createMemo(
|
||||
() =>
|
||||
|
|
@ -27,8 +27,10 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||
let resolved = parts()
|
||||
const task = currentTask()
|
||||
if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) {
|
||||
const messages = data.message[task.state.metadata.sessionId as string]?.filter((m) => m.role === "assistant")
|
||||
resolved = messages?.flatMap((m) => data.part[m.id]) ?? parts()
|
||||
const messages = data.store.message[task.state.metadata.sessionId as string]?.filter(
|
||||
(m) => m.role === "assistant",
|
||||
)
|
||||
resolved = messages?.flatMap((m) => data.store.part[m.id]) ?? parts()
|
||||
}
|
||||
return resolved
|
||||
})
|
||||
|
|
@ -149,7 +151,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||
{(p) => {
|
||||
const part = p() as ToolPart
|
||||
const message = createMemo(() =>
|
||||
data.message[part.sessionID].find((m) => m.id === part.messageID),
|
||||
data.store.message[part.sessionID].find((m) => m.id === part.messageID),
|
||||
)
|
||||
return (
|
||||
<div data-slot="message-progress-item">
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ export function SessionTurn(
|
|||
}>,
|
||||
) {
|
||||
const data = useData()
|
||||
const match = Binary.search(data.session, props.sessionID, (s) => s.id)
|
||||
const match = Binary.search(data.store.session, props.sessionID, (s) => s.id)
|
||||
if (!match.found) throw new Error(`Session ${props.sessionID} not found`)
|
||||
|
||||
const sanitizer = createMemo(() => (data.directory ? new RegExp(`${data.directory}/`, "g") : undefined))
|
||||
const messages = createMemo(() => (props.sessionID ? (data.message[props.sessionID] ?? []) : []))
|
||||
const messages = createMemo(() => (props.sessionID ? (data.store.message[props.sessionID] ?? []) : []))
|
||||
const userMessages = createMemo(() =>
|
||||
messages()
|
||||
.filter((m) => m.role === "user")
|
||||
|
|
@ -45,7 +45,7 @@ export function SessionTurn(
|
|||
|
||||
const status = createMemo(
|
||||
() =>
|
||||
data.session_status[props.sessionID] ?? {
|
||||
data.store.session_status[props.sessionID] ?? {
|
||||
type: "idle",
|
||||
},
|
||||
)
|
||||
|
|
@ -65,9 +65,9 @@ export function SessionTurn(
|
|||
const assistantMessages = createMemo(() => {
|
||||
return messages()?.filter((m) => m.role === "assistant" && m.parentID == msg().id) as AssistantMessage[]
|
||||
})
|
||||
const assistantMessageParts = createMemo(() => assistantMessages()?.flatMap((m) => data.part[m.id]))
|
||||
const assistantMessageParts = createMemo(() => assistantMessages()?.flatMap((m) => data.store.part[m.id]))
|
||||
const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error)
|
||||
const parts = createMemo(() => data.part[msg().id])
|
||||
const parts = createMemo(() => data.store.part[msg().id])
|
||||
const lastTextPart = createMemo(() =>
|
||||
assistantMessageParts()
|
||||
.filter((p) => p?.type === "text")
|
||||
|
|
@ -212,7 +212,7 @@ export function SessionTurn(
|
|||
<div data-slot="session-turn-collapsible-content-inner">
|
||||
<For each={assistantMessages()}>
|
||||
{(assistantMessage) => {
|
||||
const parts = createMemo(() => data.part[assistantMessage.id])
|
||||
const parts = createMemo(() => data.store.part[assistantMessage.id])
|
||||
const last = createMemo(() =>
|
||||
parts()
|
||||
.filter((p) => p?.type === "text")
|
||||
|
|
|
|||
|
|
@ -24,6 +24,6 @@ type Data = {
|
|||
export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
name: "Data",
|
||||
init: (props: { data: Data; directory: string }) => {
|
||||
return { ...props.data, directory: props.directory }
|
||||
return { store: props.data, directory: props.directory }
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
|
|
|||
|
|
@ -652,6 +652,82 @@ The `global` region improves availability and reduces errors at no extra cost. U
|
|||
|
||||
---
|
||||
|
||||
### llama.cpp
|
||||
|
||||
You can configure opencode to use local models through [llama.cpp's](https://github.com/ggml-org/llama.cpp) llama-server utility
|
||||
|
||||
```json title="opencode.json" "llama.cpp" {5, 6, 8, 10-14}
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"provider": {
|
||||
"llama.cpp": {
|
||||
"npm": "@ai-sdk/openai-compatible",
|
||||
"name": "llama-server (local)",
|
||||
"options": {
|
||||
"baseURL": "http://127.0.0.1:8080/v1"
|
||||
},
|
||||
"models": {
|
||||
"qwen3-coder:a3b": {
|
||||
"name": "Qwen3-Coder: a3b-30b (local)"
|
||||
}
|
||||
},
|
||||
"limit": {
|
||||
"context": 128000,
|
||||
"output": 65536
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
- `llama.cpp` is the custom provider ID. This can be any string you want.
|
||||
- `npm` specifies the package to use for this provider. Here, `@ai-sdk/openai-compatible` is used for any OpenAI-compatible API.
|
||||
- `name` is the display name for the provider in the UI.
|
||||
- `options.baseURL` is the endpoint for the local server.
|
||||
- `models` is a map of model IDs to their configurations. The model name will be displayed in the model selection list.
|
||||
|
||||
---
|
||||
|
||||
### IO.NET
|
||||
|
||||
IO.NET offers 17 models optimized for various use cases:
|
||||
|
||||
1. Head over to the [IO.NET console](https://ai.io.net/), create an account, and generate an API key.
|
||||
|
||||
2. Run `opencode auth login` and select **IO.NET**.
|
||||
|
||||
```bash
|
||||
$ opencode auth login
|
||||
|
||||
┌ Add credential
|
||||
│
|
||||
◆ Select provider
|
||||
│ ● IO.NET
|
||||
│ ...
|
||||
└
|
||||
```
|
||||
|
||||
3. Enter your IO.NET API key.
|
||||
|
||||
```bash
|
||||
$ opencode auth login
|
||||
|
||||
┌ Add credential
|
||||
│
|
||||
◇ Select provider
|
||||
│ IO.NET
|
||||
│
|
||||
◇ Enter your API key
|
||||
│ _
|
||||
└
|
||||
```
|
||||
|
||||
4. Run the `/models` command to select a model.
|
||||
|
||||
---
|
||||
|
||||
### LM Studio
|
||||
|
||||
You can configure opencode to use local models through LM Studio.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.0.114",
|
||||
"version": "1.0.115",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue