From c08fa5675fd57daad60a6cf9bbb6f593e2c66f7a Mon Sep 17 00:00:00 2001 From: Yuxin Dong Date: Sun, 5 Apr 2026 02:30:57 +0800 Subject: [PATCH 01/25] refactor: remove redundant Kimi skill section (#20393) Co-authored-by: dongyuxin Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/session/prompt/kimi.txt | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/opencode/src/session/prompt/kimi.txt b/packages/opencode/src/session/prompt/kimi.txt index 5d90c61f2c..beff6755f9 100644 --- a/packages/opencode/src/session/prompt/kimi.txt +++ b/packages/opencode/src/session/prompt/kimi.txt @@ -82,25 +82,6 @@ If the `AGENTS.md` is empty or insufficient, you may check `README`/`README.md` If you modified any files/styles/structures/configurations/workflows/... mentioned in `AGENTS.md` files, you MUST update the corresponding `AGENTS.md` files to keep them up-to-date. -# Skills - -Skills are reusable, composable capabilities that enhance your abilities. Each skill is a self-contained directory with a `SKILL.md` file that contains instructions, examples, and/or reference material. - -## What are skills? - -Skills are modular extensions that provide: - -- Specialized knowledge: Domain-specific expertise (e.g., PDF processing, data analysis) -- Workflow patterns: Best practices for common tasks -- Tool integrations: Pre-configured tool chains for specific operations -- Reference material: Documentation, templates, and examples - -## How to use skills - -Identify the skills that are likely to be useful for the tasks you are currently working on, use the `skill` tool to load a skill for detailed instructions, guidelines, scripts and more. - -Only load skill details when needed to conserve the context window. - # Ultimate Reminders At any time, you should be HELPFUL, CONCISE, and ACCURATE. Be thorough in your actions — test what you build, verify what you change — not in your explanations. From 629e866ff06f7470f403b085f9da54d07899a84a Mon Sep 17 00:00:00 2001 From: Dax Date: Sat, 4 Apr 2026 16:27:20 -0400 Subject: [PATCH 02/25] =?UTF-8?q?fix(npm):=20Arborist=20reify=20fails=20on?= =?UTF-8?q?=20compiled=20binary=20=E2=80=94=20Bun=20pre-resolves=20node-gy?= =?UTF-8?q?p=20path=20at=20build=20time=20(#21040)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/opencode/script/build.ts | 1 + packages/opencode/src/npm/index.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index b104dd2677..9c3d9bb5b7 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -209,6 +209,7 @@ for (const item of targets) { conditions: ["browser"], tsconfig: "./tsconfig.json", plugins: [plugin], + external: ["node-gyp"], compile: { autoloadBunfig: false, autoloadDotenv: false, diff --git a/packages/opencode/src/npm/index.ts b/packages/opencode/src/npm/index.ts index 9b913b3278..69bb2ca528 100644 --- a/packages/opencode/src/npm/index.ts +++ b/packages/opencode/src/npm/index.ts @@ -67,6 +67,7 @@ export namespace Npm { binLinks: true, progress: false, savePrefix: "", + ignoreScripts: true, }) const tree = await arborist.loadVirtual().catch(() => {}) if (tree) { @@ -106,6 +107,7 @@ export namespace Npm { binLinks: true, progress: false, savePrefix: "", + ignoreScripts: true, }) await arb.reify().catch(() => {}) } From 930e94a3ea098b9d56fcb6d08b545256d2546a82 Mon Sep 17 00:00:00 2001 From: opencode Date: Sat, 4 Apr 2026 20:54:20 +0000 Subject: [PATCH 03/25] release: v1.3.15 --- bun.lock | 32 +++++++++++++------------- packages/app/package.json | 2 +- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop-electron/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++----- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index 19b019c36b..cdf44a5d84 100644 --- a/bun.lock +++ b/bun.lock @@ -26,7 +26,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -80,7 +80,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -114,7 +114,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -141,7 +141,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/openai": "3.0.48", @@ -165,7 +165,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -189,7 +189,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -222,7 +222,7 @@ }, "packages/desktop-electron": { "name": "@opencode-ai/desktop-electron", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -254,7 +254,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -283,7 +283,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -299,7 +299,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.3.14", + "version": "1.3.15", "bin": { "opencode": "./bin/opencode", }, @@ -428,7 +428,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -462,7 +462,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "cross-spawn": "catalog:", }, @@ -477,7 +477,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -512,7 +512,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -560,7 +560,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "zod": "catalog:", }, @@ -571,7 +571,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index 28438c8b47..3d71ec1e7f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.3.14", + "version": "1.3.15", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 5e38840e9e..6b52b23dc2 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 11c9daa142..3a1fa330f6 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.3.14", + "version": "1.3.15", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index adad91313b..b630e41abc 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.3.14", + "version": "1.3.15", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index b5352c6d2b..ff81ab8873 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.3.14", + "version": "1.3.15", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 26c4b8f8e5..64af9910e2 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop-electron", "private": true, - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "homepage": "https://opencode.ai", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 1fe44524e5..eb0c8034cd 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 136c9029ae..aee89a1504 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.3.14", + "version": "1.3.15", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 4283d0ed25..dc4a944ba1 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.3.14" +version = "1.3.15" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.14/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.14/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.14/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.14/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.14/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 1abb67cb16..637adefec2 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.3.14", + "version": "1.3.15", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 0dd57dcbb7..d7f12549c0 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.3.14", + "version": "1.3.15", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 0769195668..f4bd4e401c 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 7d36bd7d37..04fb5194ad 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 26d27be7a4..3de4f3b615 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index d3f32ccde5..16268d9c74 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.3.14", + "version": "1.3.15", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index 9f2055917b..7b3cf21590 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.3.14", + "version": "1.3.15", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index eff4a1c1dd..24bb2354c3 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.3.14", + "version": "1.3.15", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index bdcfa727ff..36e9ec5540 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.3.14", + "version": "1.3.15", "publisher": "sst-dev", "repository": { "type": "git", From 280eb16e7704791b031481bc5415fbecfdec0319 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:29:53 -0700 Subject: [PATCH 04/25] fix: ensure reasoning tokens arent double counted when calculating usage (#21047) --- packages/opencode/src/session/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 41fad1a9d4..65032de962 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -278,7 +278,7 @@ export namespace Session { const tokens = { total, input: adjustedInputTokens, - output: outputTokens, + output: outputTokens - reasoningTokens, reasoning: reasoningTokens, cache: { write: cacheWriteInputTokens, From 6ea108a03b36dcb532f3a0c9fddf38b098fb2bad Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 4 Apr 2026 19:05:45 -0400 Subject: [PATCH 05/25] feat(tui): show console-managed providers (#20956) --- packages/opencode/src/account/index.ts | 55 +++++- packages/opencode/src/cli/cmd/tui/app.tsx | 14 ++ .../cmd/tui/component/dialog-console-org.tsx | 103 +++++++++++ .../cli/cmd/tui/component/dialog-model.tsx | 17 +- .../cli/cmd/tui/component/dialog-provider.tsx | 169 ++++++++++-------- .../cli/cmd/tui/component/prompt/index.tsx | 22 ++- .../opencode/src/cli/cmd/tui/context/sync.tsx | 18 +- .../src/cli/cmd/tui/ui/dialog-select.tsx | 14 +- .../src/cli/cmd/tui/util/provider-origin.ts | 20 +++ packages/opencode/src/config/config.ts | 45 +++-- packages/opencode/src/config/console-state.ts | 13 ++ .../src/server/routes/experimental.ts | 99 ++++++++++ packages/opencode/test/config/config.test.ts | 16 ++ packages/sdk/js/src/v2/gen/sdk.gen.ts | 151 +++++++++++++--- packages/sdk/js/src/v2/gen/types.gen.ts | 74 ++++++++ 15 files changed, 707 insertions(+), 123 deletions(-) create mode 100644 packages/opencode/src/cli/cmd/tui/component/dialog-console-org.tsx create mode 100644 packages/opencode/src/cli/cmd/tui/util/provider-origin.ts create mode 100644 packages/opencode/src/config/console-state.ts diff --git a/packages/opencode/src/account/index.ts b/packages/opencode/src/account/index.ts index 2a8d35bfa8..a1bb614ce4 100644 --- a/packages/opencode/src/account/index.ts +++ b/packages/opencode/src/account/index.ts @@ -52,6 +52,11 @@ export type AccountOrgs = { orgs: readonly Org[] } +export type ActiveOrg = { + account: Info + org: Org +} + class RemoteConfig extends Schema.Class("RemoteConfig")({ config: Schema.Record(Schema.String, Schema.Json), }) {} @@ -137,6 +142,7 @@ const mapAccountServiceError = export namespace Account { export interface Interface { readonly active: () => Effect.Effect, AccountError> + readonly activeOrg: () => Effect.Effect, AccountError> readonly list: () => Effect.Effect readonly orgsByAccount: () => Effect.Effect readonly remove: (accountID: AccountID) => Effect.Effect @@ -279,19 +285,31 @@ export namespace Account { resolveAccess(accountID).pipe(Effect.map(Option.map((r) => r.accessToken))), ) + const activeOrg = Effect.fn("Account.activeOrg")(function* () { + const activeAccount = yield* repo.active() + if (Option.isNone(activeAccount)) return Option.none() + + const account = activeAccount.value + if (!account.active_org_id) return Option.none() + + const accountOrgs = yield* orgs(account.id) + const org = accountOrgs.find((item) => item.id === account.active_org_id) + if (!org) return Option.none() + + return Option.some({ account, org }) + }) + const orgsByAccount = Effect.fn("Account.orgsByAccount")(function* () { const accounts = yield* repo.list() - const [errors, results] = yield* Effect.partition( + return yield* Effect.forEach( accounts, - (account) => orgs(account.id).pipe(Effect.map((orgs) => ({ account, orgs }))), + (account) => + orgs(account.id).pipe( + Effect.catch(() => Effect.succeed([] as readonly Org[])), + Effect.map((orgs) => ({ account, orgs })), + ), { concurrency: 3 }, ) - for (const error of errors) { - yield* Effect.logWarning("failed to fetch orgs for account").pipe( - Effect.annotateLogs({ error: String(error) }), - ) - } - return results }) const orgs = Effect.fn("Account.orgs")(function* (accountID: AccountID) { @@ -396,6 +414,7 @@ export namespace Account { return Service.of({ active: repo.active, + activeOrg, list: repo.list, orgsByAccount, remove: repo.remove, @@ -417,6 +436,26 @@ export namespace Account { return Option.getOrUndefined(await runPromise((service) => service.active())) } + export async function list(): Promise { + return runPromise((service) => service.list()) + } + + export async function activeOrg(): Promise { + return Option.getOrUndefined(await runPromise((service) => service.activeOrg())) + } + + export async function orgsByAccount(): Promise { + return runPromise((service) => service.orgsByAccount()) + } + + export async function orgs(accountID: AccountID): Promise { + return runPromise((service) => service.orgs(accountID)) + } + + export async function switchOrg(accountID: AccountID, orgID: OrgID) { + return runPromise((service) => service.use(accountID, Option.some(orgID))) + } + export async function token(accountID: AccountID): Promise { const t = await runPromise((service) => service.token(accountID)) return Option.getOrUndefined(t) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 93d1fc19ae..8ce7382929 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -36,6 +36,7 @@ import { CommandProvider, useCommandDialog } from "@tui/component/dialog-command import { DialogAgent } from "@tui/component/dialog-agent" import { DialogSessionList } from "@tui/component/dialog-session-list" import { DialogWorkspaceList } from "@tui/component/dialog-workspace-list" +import { DialogConsoleOrg } from "@tui/component/dialog-console-org" import { KeybindProvider, useKeybind } from "@tui/context/keybind" import { ThemeProvider, useTheme } from "@tui/context/theme" import { Home } from "@tui/routes/home" @@ -629,6 +630,19 @@ function App(props: { onSnapshot?: () => Promise }) { }, category: "Provider", }, + { + title: "Switch org", + value: "console.org.switch", + suggested: Boolean(sync.data.console_state.activeOrgName), + slash: { + name: "org", + aliases: ["orgs", "switch-org"], + }, + onSelect: () => { + dialog.replace(() => ) + }, + category: "Provider", + }, { title: "View status", keybind: "status_view", diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-console-org.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-console-org.tsx new file mode 100644 index 0000000000..eaf3450196 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-console-org.tsx @@ -0,0 +1,103 @@ +import { createResource, createMemo } from "solid-js" +import { DialogSelect } from "@tui/ui/dialog-select" +import { useSDK } from "@tui/context/sdk" +import { useDialog } from "@tui/ui/dialog" +import { useToast } from "@tui/ui/toast" +import { useTheme } from "@tui/context/theme" +import type { ExperimentalConsoleListOrgsResponse } from "@opencode-ai/sdk/v2" + +type OrgOption = ExperimentalConsoleListOrgsResponse["orgs"][number] + +const accountHost = (url: string) => { + try { + return new URL(url).host + } catch { + return url + } +} + +const accountLabel = (item: Pick) => + `${item.accountEmail} ${accountHost(item.accountUrl)}` + +export function DialogConsoleOrg() { + const sdk = useSDK() + const dialog = useDialog() + const toast = useToast() + const { theme } = useTheme() + + const [orgs] = createResource(async () => { + const result = await sdk.client.experimental.console.listOrgs({}, { throwOnError: true }) + return result.data?.orgs ?? [] + }) + + const current = createMemo(() => orgs()?.find((item) => item.active)) + + const options = createMemo(() => { + const listed = orgs() + if (listed === undefined) { + return [ + { + title: "Loading orgs...", + value: "loading", + onSelect: () => {}, + }, + ] + } + + if (listed.length === 0) { + return [ + { + title: "No orgs found", + value: "empty", + onSelect: () => {}, + }, + ] + } + + return listed + .toSorted((a, b) => { + const activeAccountA = a.active ? 0 : 1 + const activeAccountB = b.active ? 0 : 1 + if (activeAccountA !== activeAccountB) return activeAccountA - activeAccountB + + const accountCompare = accountLabel(a).localeCompare(accountLabel(b)) + if (accountCompare !== 0) return accountCompare + + return a.orgName.localeCompare(b.orgName) + }) + .map((item) => ({ + title: item.orgName, + value: item, + category: accountLabel(item), + categoryView: ( + + {item.accountEmail} + {accountHost(item.accountUrl)} + + ), + onSelect: async () => { + if (item.active) { + dialog.clear() + return + } + + await sdk.client.experimental.console.switchOrg( + { + accountID: item.accountID, + orgID: item.orgID, + }, + { throwOnError: true }, + ) + + await sdk.client.instance.dispose() + toast.show({ + message: `Switched to ${item.orgName}`, + variant: "info", + }) + dialog.clear() + }, + })) + }) + + return title="Switch org" options={options()} current={current()} /> +} diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 549165f51a..1fd1c130c6 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -8,6 +8,7 @@ import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { DialogVariant } from "./dialog-variant" import { useKeybind } from "../context/keybind" import * as fuzzysort from "fuzzysort" +import { consoleManagedProviderLabel } from "@tui/util/provider-origin" export function useConnected() { const sync = useSync() @@ -46,7 +47,11 @@ export function DialogModel(props: { providerID?: string }) { key: item, value: { providerID: provider.id, modelID: model.id }, title: model.name ?? item.modelID, - description: provider.name, + description: consoleManagedProviderLabel( + sync.data.console_state.consoleManagedProviders, + provider.id, + provider.name, + ), category, disabled: provider.id === "opencode" && model.id.includes("-nano"), footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, @@ -84,7 +89,9 @@ export function DialogModel(props: { providerID?: string }) { description: favorites.some((item) => item.providerID === provider.id && item.modelID === model) ? "(Favorite)" : undefined, - category: connected() ? provider.name : undefined, + category: connected() + ? consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, provider.id, provider.name) + : undefined, disabled: provider.id === "opencode" && model.includes("-nano"), footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, onSelect() { @@ -132,7 +139,11 @@ export function DialogModel(props: { providerID?: string }) { props.providerID ? sync.data.provider.find((x) => x.id === props.providerID) : null, ) - const title = createMemo(() => provider()?.name ?? "Select model") + const title = createMemo(() => { + const value = provider() + if (!value) return "Select model" + return consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, value.id, value.name) + }) function onSelect(providerID: string, modelID: string) { local.model.set({ providerID, modelID }, { recent: true }) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx index 635ed71f5b..8add73dd6e 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx @@ -13,6 +13,7 @@ import { DialogModel } from "./dialog-model" import { useKeyboard } from "@opentui/solid" import { Clipboard } from "@tui/util/clipboard" import { useToast } from "../ui/toast" +import { CONSOLE_MANAGED_ICON, isConsoleManagedProvider } from "@tui/util/provider-origin" const PROVIDER_PRIORITY: Record = { opencode: 0, @@ -28,87 +29,111 @@ export function createDialogProviderOptions() { const dialog = useDialog() const sdk = useSDK() const toast = useToast() + const { theme } = useTheme() const options = createMemo(() => { return pipe( sync.data.provider_next.all, sortBy((x) => PROVIDER_PRIORITY[x.id] ?? 99), - map((provider) => ({ - title: provider.name, - value: provider.id, - description: { - opencode: "(Recommended)", - anthropic: "(API key)", - openai: "(ChatGPT Plus/Pro or API key)", - "opencode-go": "Low cost subscription for everyone", - }[provider.id], - category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other", - async onSelect() { - const methods = sync.data.provider_auth[provider.id] ?? [ - { - type: "api", - label: "API key", - }, - ] - let index: number | null = 0 - if (methods.length > 1) { - index = await new Promise((resolve) => { - dialog.replace( - () => ( - ({ - title: x.label, - value: index, - }))} - onSelect={(option) => resolve(option.value)} - /> - ), - () => resolve(null), - ) - }) - } - if (index == null) return - const method = methods[index] - if (method.type === "oauth") { - let inputs: Record | undefined - if (method.prompts?.length) { - const value = await PromptsMethod({ - dialog, - prompts: method.prompts, - }) - if (!value) return - inputs = value - } + map((provider) => { + const consoleManaged = isConsoleManagedProvider(sync.data.console_state.consoleManagedProviders, provider.id) + const connected = sync.data.provider_next.connected.includes(provider.id) - const result = await sdk.client.provider.oauth.authorize({ - providerID: provider.id, - method: index, - inputs, - }) - if (result.error) { - toast.show({ - variant: "error", - message: JSON.stringify(result.error), + return { + title: provider.name, + value: provider.id, + description: { + opencode: "(Recommended)", + anthropic: "(API key)", + openai: "(ChatGPT Plus/Pro or API key)", + "opencode-go": "Low cost subscription for everyone", + }[provider.id], + footer: consoleManaged ? sync.data.console_state.activeOrgName : undefined, + category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other", + gutter: consoleManaged ? ( + {CONSOLE_MANAGED_ICON} + ) : connected ? ( + + ) : undefined, + async onSelect() { + if (consoleManaged) return + + const methods = sync.data.provider_auth[provider.id] ?? [ + { + type: "api", + label: "API key", + }, + ] + let index: number | null = 0 + if (methods.length > 1) { + index = await new Promise((resolve) => { + dialog.replace( + () => ( + ({ + title: x.label, + value: index, + }))} + onSelect={(option) => resolve(option.value)} + /> + ), + () => resolve(null), + ) }) - dialog.clear() - return } - if (result.data?.method === "code") { - dialog.replace(() => ( - - )) + if (index == null) return + const method = methods[index] + if (method.type === "oauth") { + let inputs: Record | undefined + if (method.prompts?.length) { + const value = await PromptsMethod({ + dialog, + prompts: method.prompts, + }) + if (!value) return + inputs = value + } + + const result = await sdk.client.provider.oauth.authorize({ + providerID: provider.id, + method: index, + inputs, + }) + if (result.error) { + toast.show({ + variant: "error", + message: JSON.stringify(result.error), + }) + dialog.clear() + return + } + if (result.data?.method === "code") { + dialog.replace(() => ( + + )) + } + if (result.data?.method === "auto") { + dialog.replace(() => ( + + )) + } } - if (result.data?.method === "auto") { - dialog.replace(() => ( - - )) + if (method.type === "api") { + return dialog.replace(() => ) } - } - if (method.type === "api") { - return dialog.replace(() => ) - } - }, - })), + }, + } + }), ) }) return options diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 382bd2806e..55bf1d5630 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -35,6 +35,7 @@ import { useToast } from "../../ui/toast" import { useKV } from "../../context/kv" import { useTextareaKeybindings } from "../textarea-keybindings" import { DialogSkill } from "../dialog-skill" +import { CONSOLE_MANAGED_ICON, consoleManagedProviderLabel } from "@tui/util/provider-origin" export type PromptProps = { sessionID?: string @@ -94,6 +95,14 @@ export function Prompt(props: PromptProps) { const list = createMemo(() => props.placeholders?.normal ?? []) const shell = createMemo(() => props.placeholders?.shell ?? []) const [auto, setAuto] = createSignal() + const activeOrgName = createMemo(() => sync.data.console_state.activeOrgName) + const currentProviderLabel = createMemo(() => { + const current = local.model.current() + const provider = local.model.parsed().provider + if (!current) return provider + return consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, current.providerID, provider) + }) + const hasRightContent = createMemo(() => Boolean(props.right || activeOrgName())) function promptModelWarning() { toast.show({ @@ -1095,7 +1104,7 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} - {local.model.parsed().provider} + {currentProviderLabel()} · @@ -1105,7 +1114,16 @@ export function Prompt(props: PromptProps) { - {props.right} + + + {props.right} + + command.trigger("console.org.switch")}> + {`${CONSOLE_MANAGED_ICON} ${activeOrgName()}`} + + + + diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 3b296a927a..11336d5002 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -29,6 +29,7 @@ import { batch, onMount } from "solid-js" import { Log } from "@/util/log" import type { Path } from "@opencode-ai/sdk" import type { Workspace } from "@opencode-ai/sdk/v2" +import { ConsoleState, emptyConsoleState, type ConsoleState as ConsoleStateType } from "@/config/console-state" export const { use: useSync, provider: SyncProvider } = createSimpleContext({ name: "Sync", @@ -38,6 +39,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ provider: Provider[] provider_default: Record provider_next: ProviderListResponse + console_state: ConsoleStateType provider_auth: Record agent: Agent[] command: Command[] @@ -81,6 +83,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ default: {}, connected: [], }, + console_state: emptyConsoleState, provider_auth: {}, config: {}, status: "loading", @@ -365,6 +368,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ // blocking - include session.list when continuing a session const providersPromise = sdk.client.config.providers({}, { throwOnError: true }) const providerListPromise = sdk.client.provider.list({}, { throwOnError: true }) + const consoleStatePromise = sdk.client.experimental.console + .get({}, { throwOnError: true }) + .then((x) => ConsoleState.parse(x.data)) + .catch(() => emptyConsoleState) const agentsPromise = sdk.client.app.agents({}, { throwOnError: true }) const configPromise = sdk.client.config.get({}, { throwOnError: true }) const blockingRequests: Promise[] = [ @@ -379,6 +386,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ .then(() => { const providersResponse = providersPromise.then((x) => x.data!) const providerListResponse = providerListPromise.then((x) => x.data!) + const consoleStateResponse = consoleStatePromise const agentsResponse = agentsPromise.then((x) => x.data ?? []) const configResponse = configPromise.then((x) => x.data!) const sessionListResponse = args.continue ? sessionListPromise : undefined @@ -386,20 +394,23 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ return Promise.all([ providersResponse, providerListResponse, + consoleStateResponse, agentsResponse, configResponse, ...(sessionListResponse ? [sessionListResponse] : []), ]).then((responses) => { const providers = responses[0] const providerList = responses[1] - const agents = responses[2] - const config = responses[3] - const sessions = responses[4] + const consoleState = responses[2] + const agents = responses[3] + const config = responses[4] + const sessions = responses[5] batch(() => { setStore("provider", reconcile(providers.providers)) setStore("provider_default", reconcile(providers.default)) setStore("provider_next", reconcile(providerList)) + setStore("console_state", reconcile(consoleState)) setStore("agent", reconcile(agents)) setStore("config", reconcile(config)) if (sessions !== undefined) setStore("session", reconcile(sessions)) @@ -411,6 +422,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ // non-blocking Promise.all([ ...(args.continue ? [] : [sessionListPromise.then((sessions) => setStore("session", reconcile(sessions)))]), + consoleStatePromise.then((consoleState) => setStore("console_state", reconcile(consoleState))), sdk.client.command.list().then((x) => setStore("command", reconcile(x.data ?? []))), sdk.client.lsp.status().then((x) => setStore("lsp", reconcile(x.data!))), sdk.client.mcp.status().then((x) => setStore("mcp", reconcile(x.data!))), diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 30cf3b9543..46821cccec 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -38,6 +38,7 @@ export interface DialogSelectOption { description?: string footer?: JSX.Element | string category?: string + categoryView?: JSX.Element disabled?: boolean bg?: RGBA gutter?: JSX.Element @@ -291,9 +292,16 @@ export function DialogSelect(props: DialogSelectProps) { <> 0 ? 1 : 0} paddingLeft={3}> - - {category} - + + {category} + + } + > + {options[0]?.categoryView} + diff --git a/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts b/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts new file mode 100644 index 0000000000..7ec345ff52 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts @@ -0,0 +1,20 @@ +export const CONSOLE_MANAGED_ICON = "⌂" + +const contains = (consoleManagedProviders: string[] | ReadonlySet, providerID: string) => + Array.isArray(consoleManagedProviders) + ? consoleManagedProviders.includes(providerID) + : consoleManagedProviders.has(providerID) + +export const isConsoleManagedProvider = (consoleManagedProviders: string[] | ReadonlySet, providerID: string) => + contains(consoleManagedProviders, providerID) + +export const consoleManagedProviderSuffix = ( + consoleManagedProviders: string[] | ReadonlySet, + providerID: string, +) => (contains(consoleManagedProviders, providerID) ? ` ${CONSOLE_MANAGED_ICON}` : "") + +export const consoleManagedProviderLabel = ( + consoleManagedProviders: string[] | ReadonlySet, + providerID: string, + providerName: string, +) => `${providerName}${consoleManagedProviderSuffix(consoleManagedProviders, providerID)}` diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 850bcc28bc..83e677bcb7 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -33,6 +33,7 @@ import { Account } from "@/account" import { isRecord } from "@/util/record" import { ConfigPaths } from "./paths" import { Filesystem } from "@/util/filesystem" +import type { ConsoleState } from "./console-state" import { AppFileSystem } from "@/filesystem" import { InstanceState } from "@/effect/instance-state" import { makeRuntime } from "@/effect/run-service" @@ -1050,11 +1051,13 @@ export namespace Config { config: Info directories: string[] deps: Promise[] + consoleState: ConsoleState } export interface Interface { readonly get: () => Effect.Effect readonly getGlobal: () => Effect.Effect + readonly getConsoleState: () => Effect.Effect readonly update: (config: Info) => Effect.Effect readonly updateGlobal: (config: Info) => Effect.Effect readonly invalidate: (wait?: boolean) => Effect.Effect @@ -1260,6 +1263,8 @@ export namespace Config { const auth = yield* authSvc.all().pipe(Effect.orDie) let result: Info = {} + const consoleManagedProviders = new Set() + let activeOrgName: string | undefined const scope = (source: string): PluginScope => { if (source.startsWith("http://") || source.startsWith("https://")) return "global" @@ -1371,26 +1376,31 @@ export namespace Config { log.debug("loaded custom config from OPENCODE_CONFIG_CONTENT") } - const active = Option.getOrUndefined(yield* accountSvc.active().pipe(Effect.orDie)) - if (active?.active_org_id) { + const activeOrg = Option.getOrUndefined( + yield* accountSvc.activeOrg().pipe(Effect.catch(() => Effect.succeed(Option.none()))), + ) + if (activeOrg) { yield* Effect.gen(function* () { const [configOpt, tokenOpt] = yield* Effect.all( - [accountSvc.config(active.id, active.active_org_id!), accountSvc.token(active.id)], + [accountSvc.config(activeOrg.account.id, activeOrg.org.id), accountSvc.token(activeOrg.account.id)], { concurrency: 2 }, ) - const token = Option.getOrUndefined(tokenOpt) - if (token) { - process.env["OPENCODE_CONSOLE_TOKEN"] = token - Env.set("OPENCODE_CONSOLE_TOKEN", token) + if (Option.isSome(tokenOpt)) { + process.env["OPENCODE_CONSOLE_TOKEN"] = tokenOpt.value + Env.set("OPENCODE_CONSOLE_TOKEN", tokenOpt.value) } - const config = Option.getOrUndefined(configOpt) - if (config) { - const source = `${active.url}/api/config` - const next = yield* loadConfig(JSON.stringify(config), { + activeOrgName = activeOrg.org.name + + if (Option.isSome(configOpt)) { + const source = `${activeOrg.account.url}/api/config` + const next = yield* loadConfig(JSON.stringify(configOpt.value), { dir: path.dirname(source), source, }) + for (const providerID of Object.keys(next.provider ?? {})) { + consoleManagedProviders.add(providerID) + } merge(source, next, "global") } }).pipe( @@ -1456,6 +1466,10 @@ export namespace Config { config: result, directories, deps, + consoleState: { + consoleManagedProviders: Array.from(consoleManagedProviders), + activeOrgName, + }, } }) @@ -1473,6 +1487,10 @@ export namespace Config { return yield* InstanceState.use(state, (s) => s.directories) }) + const getConsoleState = Effect.fn("Config.getConsoleState")(function* () { + return yield* InstanceState.use(state, (s) => s.consoleState) + }) + const waitForDependencies = Effect.fn("Config.waitForDependencies")(function* () { yield* InstanceState.useEffect(state, (s) => Effect.promise(() => Promise.all(s.deps).then(() => undefined))) }) @@ -1528,6 +1546,7 @@ export namespace Config { return Service.of({ get, getGlobal, + getConsoleState, update, updateGlobal, invalidate, @@ -1553,6 +1572,10 @@ export namespace Config { return runPromise((svc) => svc.getGlobal()) } + export async function getConsoleState() { + return runPromise((svc) => svc.getConsoleState()) + } + export async function update(config: Info) { return runPromise((svc) => svc.update(config)) } diff --git a/packages/opencode/src/config/console-state.ts b/packages/opencode/src/config/console-state.ts new file mode 100644 index 0000000000..a5d1f6d269 --- /dev/null +++ b/packages/opencode/src/config/console-state.ts @@ -0,0 +1,13 @@ +import z from "zod" + +export const ConsoleState = z.object({ + consoleManagedProviders: z.array(z.string()), + activeOrgName: z.string().optional(), +}) + +export type ConsoleState = z.infer + +export const emptyConsoleState: ConsoleState = { + consoleManagedProviders: [], + activeOrgName: undefined, +} diff --git a/packages/opencode/src/server/routes/experimental.ts b/packages/opencode/src/server/routes/experimental.ts index a41b21a1fe..a4b1f4d084 100644 --- a/packages/opencode/src/server/routes/experimental.ts +++ b/packages/opencode/src/server/routes/experimental.ts @@ -8,13 +8,112 @@ import { Instance } from "../../project/instance" import { Project } from "../../project/project" import { MCP } from "../../mcp" import { Session } from "../../session" +import { Config } from "../../config/config" +import { ConsoleState } from "../../config/console-state" +import { Account, AccountID, OrgID } from "../../account" import { zodToJsonSchema } from "zod-to-json-schema" import { errors } from "../error" import { lazy } from "../../util/lazy" import { WorkspaceRoutes } from "./workspace" +const ConsoleOrgOption = z.object({ + accountID: z.string(), + accountEmail: z.string(), + accountUrl: z.string(), + orgID: z.string(), + orgName: z.string(), + active: z.boolean(), +}) + +const ConsoleOrgList = z.object({ + orgs: z.array(ConsoleOrgOption), +}) + +const ConsoleSwitchBody = z.object({ + accountID: z.string(), + orgID: z.string(), +}) + export const ExperimentalRoutes = lazy(() => new Hono() + .get( + "/console", + describeRoute({ + summary: "Get active Console provider metadata", + description: "Get the active Console org name and the set of provider IDs managed by that Console org.", + operationId: "experimental.console.get", + responses: { + 200: { + description: "Active Console provider metadata", + content: { + "application/json": { + schema: resolver(ConsoleState), + }, + }, + }, + }, + }), + async (c) => { + return c.json(await Config.getConsoleState()) + }, + ) + .get( + "/console/orgs", + describeRoute({ + summary: "List switchable Console orgs", + description: "Get the available Console orgs across logged-in accounts, including the current active org.", + operationId: "experimental.console.listOrgs", + responses: { + 200: { + description: "Switchable Console orgs", + content: { + "application/json": { + schema: resolver(ConsoleOrgList), + }, + }, + }, + }, + }), + async (c) => { + const [groups, active] = await Promise.all([Account.orgsByAccount(), Account.active()]) + + const orgs = groups.flatMap((group) => + group.orgs.map((org) => ({ + accountID: group.account.id, + accountEmail: group.account.email, + accountUrl: group.account.url, + orgID: org.id, + orgName: org.name, + active: !!active && active.id === group.account.id && active.active_org_id === org.id, + })), + ) + return c.json({ orgs }) + }, + ) + .post( + "/console/switch", + describeRoute({ + summary: "Switch active Console org", + description: "Persist a new active Console account/org selection for the current local OpenCode state.", + operationId: "experimental.console.switchOrg", + responses: { + 200: { + description: "Switch success", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + }, + }), + validator("json", ConsoleSwitchBody), + async (c) => { + const body = c.req.valid("json") + await Account.switchOrg(AccountID.make(body.accountID), OrgID.make(body.orgID)) + return c.json(true) + }, + ) .get( "/tool/ids", describeRoute({ diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 9c631360b6..0ac61aee71 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -25,6 +25,7 @@ import { Npm } from "../../src/npm" const emptyAccount = Layer.mock(Account.Service)({ active: () => Effect.succeed(Option.none()), + activeOrg: () => Effect.succeed(Option.none()), }) const emptyAuth = Layer.mock(Auth.Service)({ @@ -282,6 +283,21 @@ test("resolves env templates in account config with account token", async () => active_org_id: OrgID.make("org-1"), }), ), + activeOrg: () => + Effect.succeed( + Option.some({ + account: { + id: AccountID.make("account-1"), + email: "user@example.com", + url: "https://control.example.com", + active_org_id: OrgID.make("org-1"), + }, + org: { + id: OrgID.make("org-1"), + name: "Example Org", + }, + }), + ), config: () => Effect.succeed( Option.some({ diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 3a780e234d..b2e37db59b 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -24,6 +24,9 @@ import type { EventTuiPromptAppend, EventTuiSessionSelect, EventTuiToastShow, + ExperimentalConsoleGetResponses, + ExperimentalConsoleListOrgsResponses, + ExperimentalConsoleSwitchOrgResponses, ExperimentalResourceListResponses, ExperimentalSessionListResponses, ExperimentalWorkspaceCreateErrors, @@ -981,13 +984,13 @@ export class Config2 extends HeyApiClient { } } -export class Tool extends HeyApiClient { +export class Console extends HeyApiClient { /** - * List tool IDs + * Get active Console provider metadata * - * Get a list of all available tool IDs, including both built-in tools and dynamically registered tools. + * Get the active Console org name and the set of provider IDs managed by that Console org. */ - public ids( + public get( parameters?: { directory?: string workspace?: string @@ -1005,24 +1008,22 @@ export class Tool extends HeyApiClient { }, ], ) - return (options?.client ?? this.client).get({ - url: "/experimental/tool/ids", + return (options?.client ?? this.client).get({ + url: "/experimental/console", ...options, ...params, }) } /** - * List tools + * List switchable Console orgs * - * Get a list of available tools with their JSON schema parameters for a specific provider and model combination. + * Get the available Console orgs across logged-in accounts, including the current active org. */ - public list( - parameters: { + public listOrgs( + parameters?: { directory?: string workspace?: string - provider: string - model: string }, options?: Options, ) { @@ -1033,18 +1034,55 @@ export class Tool extends HeyApiClient { args: [ { in: "query", key: "directory" }, { in: "query", key: "workspace" }, - { in: "query", key: "provider" }, - { in: "query", key: "model" }, ], }, ], ) - return (options?.client ?? this.client).get({ - url: "/experimental/tool", + return (options?.client ?? this.client).get({ + url: "/experimental/console/orgs", ...options, ...params, }) } + + /** + * Switch active Console org + * + * Persist a new active Console account/org selection for the current local OpenCode state. + */ + public switchOrg( + parameters?: { + directory?: string + workspace?: string + accountID?: string + orgID?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "accountID" }, + { in: "body", key: "orgID" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/experimental/console/switch", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } } export class Workspace extends HeyApiClient { @@ -1239,6 +1277,11 @@ export class Resource extends HeyApiClient { } export class Experimental extends HeyApiClient { + private _console?: Console + get console(): Console { + return (this._console ??= new Console({ client: this.client })) + } + private _workspace?: Workspace get workspace(): Workspace { return (this._workspace ??= new Workspace({ client: this.client })) @@ -1255,6 +1298,72 @@ export class Experimental extends HeyApiClient { } } +export class Tool extends HeyApiClient { + /** + * List tool IDs + * + * Get a list of all available tool IDs, including both built-in tools and dynamically registered tools. + */ + public ids( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/experimental/tool/ids", + ...options, + ...params, + }) + } + + /** + * List tools + * + * Get a list of available tools with their JSON schema parameters for a specific provider and model combination. + */ + public list( + parameters: { + directory?: string + workspace?: string + provider: string + model: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "query", key: "provider" }, + { in: "query", key: "model" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/experimental/tool", + ...options, + ...params, + }) + } +} + export class Worktree extends HeyApiClient { /** * Remove worktree @@ -4017,16 +4126,16 @@ export class OpencodeClient extends HeyApiClient { return (this._config ??= new Config2({ client: this.client })) } - private _tool?: Tool - get tool(): Tool { - return (this._tool ??= new Tool({ client: this.client })) - } - private _experimental?: Experimental get experimental(): Experimental { return (this._experimental ??= new Experimental({ client: this.client })) } + private _tool?: Tool + get tool(): Tool { + return (this._tool ??= new Tool({ client: this.client })) + } + private _worktree?: Worktree get worktree(): Worktree { return (this._worktree ??= new Worktree({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index d517abf2c6..4c348573f6 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2653,6 +2653,80 @@ export type ConfigProvidersResponses = { export type ConfigProvidersResponse = ConfigProvidersResponses[keyof ConfigProvidersResponses] +export type ExperimentalConsoleGetData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/experimental/console" +} + +export type ExperimentalConsoleGetResponses = { + /** + * Active Console provider metadata + */ + 200: { + consoleManagedProviders: Array + activeOrgName?: string + } +} + +export type ExperimentalConsoleGetResponse = ExperimentalConsoleGetResponses[keyof ExperimentalConsoleGetResponses] + +export type ExperimentalConsoleListOrgsData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/experimental/console/orgs" +} + +export type ExperimentalConsoleListOrgsResponses = { + /** + * Switchable Console orgs + */ + 200: { + orgs: Array<{ + accountID: string + accountEmail: string + accountUrl: string + orgID: string + orgName: string + active: boolean + }> + } +} + +export type ExperimentalConsoleListOrgsResponse = + ExperimentalConsoleListOrgsResponses[keyof ExperimentalConsoleListOrgsResponses] + +export type ExperimentalConsoleSwitchOrgData = { + body?: { + accountID: string + orgID: string + } + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/experimental/console/switch" +} + +export type ExperimentalConsoleSwitchOrgResponses = { + /** + * Switch success + */ + 200: boolean +} + +export type ExperimentalConsoleSwitchOrgResponse = + ExperimentalConsoleSwitchOrgResponses[keyof ExperimentalConsoleSwitchOrgResponses] + export type ToolIdsData = { body?: never path?: never From c796b9a19e8698c07d2e03a12a41fa81126b1be9 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 4 Apr 2026 19:06:00 -0400 Subject: [PATCH 06/25] refactor(effect): move read tool onto defineEffect (#21016) --- packages/opencode/specs/effect-migration.md | 18 +- packages/opencode/src/filesystem/index.ts | 14 +- .../opencode/src/tool/external-directory.ts | 15 +- packages/opencode/src/tool/read.ts | 420 +++++---- packages/opencode/src/tool/registry.ts | 325 +++---- packages/opencode/test/tool/read.test.ts | 856 ++++++++---------- 6 files changed, 829 insertions(+), 819 deletions(-) diff --git a/packages/opencode/specs/effect-migration.md b/packages/opencode/specs/effect-migration.md index 9f862d3b9a..5882f09fe5 100644 --- a/packages/opencode/specs/effect-migration.md +++ b/packages/opencode/specs/effect-migration.md @@ -235,11 +235,27 @@ Once individual tools are effectified, change `Tool.Info` (`tool/tool.ts`) so `i 2. Update `Tool.define()` factory to work with Effects 3. Update `SessionPrompt` to `yield*` tool results instead of `await`ing +### Tool migration details + +Until the tool interface itself returns `Effect`, use this transitional pattern for migrated tools: + +- `Tool.defineEffect(...)` should `yield*` the services the tool depends on and close over them in the returned tool definition. +- Keep the bridge at the Promise boundary only. Prefer a single `Effect.runPromise(...)` in the temporary `async execute(...)` implementation, and move the inner logic into `Effect.fn(...)` helpers instead of scattering `runPromise` islands through the tool body. +- If a tool starts requiring new services, wire them into `ToolRegistry.defaultLayer` so production callers resolve the same dependencies as tests. + +Tool tests should use the existing Effect helpers in `packages/opencode/test/lib/effect.ts`: + +- Use `testEffect(...)` / `it.live(...)` instead of creating fake local wrappers around effectful tools. +- Yield the real tool export, then initialize it: `const info = yield* ReadTool`, `const tool = yield* Effect.promise(() => info.init())`. +- Run tests inside a real instance with `provideTmpdirInstance(...)` or `provideInstance(tmpdirScoped(...))` so instance-scoped services resolve exactly as they do in production. + +This keeps migrated tool tests aligned with the production service graph today, and makes the eventual `Tool.Info` → `Effect` cleanup mostly mechanical later. + Individual tools, ordered by value: - [ ] `apply_patch.ts` — HIGH: multi-step orchestration, error accumulation, Bus events - [ ] `bash.ts` — HIGH: shell orchestration, quoting, timeout handling, output capture -- [ ] `read.ts` — HIGH: streaming I/O, readline, binary detection → FileSystem + Stream +- [x] `read.ts` — HIGH: streaming I/O, readline, binary detection → FileSystem + Stream - [ ] `edit.ts` — HIGH: multi-step diff/format/publish pipeline, FileWatcher lock - [ ] `grep.ts` — MEDIUM: spawns ripgrep → ChildProcessSpawner, timeout handling - [ ] `write.ts` — MEDIUM: permission checks, diagnostics polling, Bus events diff --git a/packages/opencode/src/filesystem/index.ts b/packages/opencode/src/filesystem/index.ts index 45231d43f6..01fdcd2e5e 100644 --- a/packages/opencode/src/filesystem/index.ts +++ b/packages/opencode/src/filesystem/index.ts @@ -188,13 +188,23 @@ export namespace AppFileSystem { export function normalizePath(p: string): string { if (process.platform !== "win32") return p + const resolved = pathResolve(windowsPath(p)) try { - return realpathSync.native(p) + return realpathSync.native(resolved) } catch { - return p + return resolved } } + export function normalizePathPattern(p: string): string { + if (process.platform !== "win32") return p + if (p === "*") return p + const match = p.match(/^(.*)[\\/]\*$/) + if (!match) return normalizePath(p) + const dir = /^[A-Za-z]:$/.test(match[1]) ? match[1] + "\\" : match[1] + return join(normalizePath(dir), "*") + } + export function resolve(p: string): string { const resolved = pathResolve(windowsPath(p)) try { diff --git a/packages/opencode/src/tool/external-directory.ts b/packages/opencode/src/tool/external-directory.ts index 66eba438bc..f11455cf59 100644 --- a/packages/opencode/src/tool/external-directory.ts +++ b/packages/opencode/src/tool/external-directory.ts @@ -1,7 +1,8 @@ import path from "path" +import { Effect } from "effect" import type { Tool } from "./tool" import { Instance } from "../project/instance" -import { Filesystem } from "@/util/filesystem" +import { AppFileSystem } from "../filesystem" type Kind = "file" | "directory" @@ -15,14 +16,14 @@ export async function assertExternalDirectory(ctx: Tool.Context, target?: string if (options?.bypass) return - const full = process.platform === "win32" ? Filesystem.normalizePath(target) : target + const full = process.platform === "win32" ? AppFileSystem.normalizePath(target) : target if (Instance.containsPath(full)) return const kind = options?.kind ?? "file" const dir = kind === "directory" ? full : path.dirname(full) const glob = process.platform === "win32" - ? Filesystem.normalizePathPattern(path.join(dir, "*")) + ? AppFileSystem.normalizePathPattern(path.join(dir, "*")) : path.join(dir, "*").replaceAll("\\", "/") await ctx.ask({ @@ -35,3 +36,11 @@ export async function assertExternalDirectory(ctx: Tool.Context, target?: string }, }) } + +export const assertExternalDirectoryEffect = Effect.fn("Tool.assertExternalDirectory")(function* ( + ctx: Tool.Context, + target?: string, + options?: Options, +) { + yield* Effect.promise(() => assertExternalDirectory(ctx, target, options)) +}) diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 18520c2a6f..366993020b 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -1,16 +1,17 @@ import z from "zod" +import { Effect, Scope } from "effect" import { createReadStream } from "fs" -import * as fs from "fs/promises" +import { open } from "fs/promises" import * as path from "path" import { createInterface } from "readline" import { Tool } from "./tool" +import { AppFileSystem } from "../filesystem" import { LSP } from "../lsp" import { FileTime } from "../file/time" import DESCRIPTION from "./read.txt" import { Instance } from "../project/instance" -import { assertExternalDirectory } from "./external-directory" +import { assertExternalDirectoryEffect } from "./external-directory" import { Instruction } from "../session/instruction" -import { Filesystem } from "../util/filesystem" const DEFAULT_READ_LIMIT = 2000 const MAX_LINE_LENGTH = 2000 @@ -18,222 +19,257 @@ const MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)` const MAX_BYTES = 50 * 1024 const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB` -export const ReadTool = Tool.define("read", { - description: DESCRIPTION, - parameters: z.object({ - filePath: z.string().describe("The absolute path to the file or directory to read"), - offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(), - limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(), - }), - async execute(params, ctx) { - if (params.offset !== undefined && params.offset < 1) { - throw new Error("offset must be greater than or equal to 1") - } - let filepath = params.filePath - if (!path.isAbsolute(filepath)) { - filepath = path.resolve(Instance.directory, filepath) - } - if (process.platform === "win32") { - filepath = Filesystem.normalizePath(filepath) - } - const title = path.relative(Instance.worktree, filepath) +const parameters = z.object({ + filePath: z.string().describe("The absolute path to the file or directory to read"), + offset: z.coerce.number().describe("The line number to start reading from (1-indexed)").optional(), + limit: z.coerce.number().describe("The maximum number of lines to read (defaults to 2000)").optional(), +}) - const stat = Filesystem.stat(filepath) +export const ReadTool = Tool.defineEffect( + "read", + Effect.gen(function* () { + const fs = yield* AppFileSystem.Service + const instruction = yield* Instruction.Service + const lsp = yield* LSP.Service + const time = yield* FileTime.Service + const scope = yield* Scope.Scope - await assertExternalDirectory(ctx, filepath, { - bypass: Boolean(ctx.extra?.["bypassCwdCheck"]), - kind: stat?.isDirectory() ? "directory" : "file", - }) - - await ctx.ask({ - permission: "read", - patterns: [filepath], - always: ["*"], - metadata: {}, - }) - - if (!stat) { + const miss = Effect.fn("ReadTool.miss")(function* (filepath: string) { const dir = path.dirname(filepath) const base = path.basename(filepath) - - const suggestions = await fs - .readdir(dir) - .then((entries) => - entries + const items = yield* fs.readDirectory(dir).pipe( + Effect.map((items) => + items .filter( - (entry) => - entry.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(entry.toLowerCase()), + (item) => + item.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(item.toLowerCase()), ) - .map((entry) => path.join(dir, entry)) + .map((item) => path.join(dir, item)) .slice(0, 3), - ) - .catch(() => []) + ), + Effect.catch(() => Effect.succeed([] as string[])), + ) - if (suggestions.length > 0) { - throw new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`) + if (items.length > 0) { + return yield* Effect.fail( + new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${items.join("\n")}`), + ) } - throw new Error(`File not found: ${filepath}`) - } + return yield* Effect.fail(new Error(`File not found: ${filepath}`)) + }) - if (stat.isDirectory()) { - const dirents = await fs.readdir(filepath, { withFileTypes: true }) - const entries = await Promise.all( - dirents.map(async (dirent) => { - if (dirent.isDirectory()) return dirent.name + "/" - if (dirent.isSymbolicLink()) { - const target = await fs.stat(path.join(filepath, dirent.name)).catch(() => undefined) - if (target?.isDirectory()) return dirent.name + "/" - } - return dirent.name + const list = Effect.fn("ReadTool.list")(function* (filepath: string) { + const items = yield* fs.readDirectoryEntries(filepath) + return yield* Effect.forEach( + items, + Effect.fnUntraced(function* (item) { + if (item.type === "directory") return item.name + "/" + if (item.type !== "symlink") return item.name + + const target = yield* fs + .stat(path.join(filepath, item.name)) + .pipe(Effect.catch(() => Effect.succeed(undefined))) + if (target?.type === "Directory") return item.name + "/" + return item.name + }), + { concurrency: "unbounded" }, + ).pipe(Effect.map((items: string[]) => items.sort((a, b) => a.localeCompare(b)))) + }) + + const warm = Effect.fn("ReadTool.warm")(function* (filepath: string, sessionID: Tool.Context["sessionID"]) { + yield* lsp.touchFile(filepath, false).pipe(Effect.ignore, Effect.forkIn(scope)) + yield* time.read(sessionID, filepath) + }) + + const run = Effect.fn("ReadTool.execute")(function* (params: z.infer, ctx: Tool.Context) { + if (params.offset !== undefined && params.offset < 1) { + return yield* Effect.fail(new Error("offset must be greater than or equal to 1")) + } + + let filepath = params.filePath + if (!path.isAbsolute(filepath)) { + filepath = path.resolve(Instance.directory, filepath) + } + if (process.platform === "win32") { + filepath = AppFileSystem.normalizePath(filepath) + } + const title = path.relative(Instance.worktree, filepath) + + const stat = yield* fs.stat(filepath).pipe( + Effect.catchIf( + (err) => "reason" in err && err.reason._tag === "NotFound", + () => Effect.succeed(undefined), + ), + ) + + yield* assertExternalDirectoryEffect(ctx, filepath, { + bypass: Boolean(ctx.extra?.["bypassCwdCheck"]), + kind: stat?.type === "Directory" ? "directory" : "file", + }) + + yield* Effect.promise(() => + ctx.ask({ + permission: "read", + patterns: [filepath], + always: ["*"], + metadata: {}, }), ) - entries.sort((a, b) => a.localeCompare(b)) - const limit = params.limit ?? DEFAULT_READ_LIMIT - const offset = params.offset ?? 1 - const start = offset - 1 - const sliced = entries.slice(start, start + limit) - const truncated = start + sliced.length < entries.length + if (!stat) return yield* miss(filepath) - const output = [ - `${filepath}`, - `directory`, - ``, - sliced.join("\n"), - truncated - ? `\n(Showing ${sliced.length} of ${entries.length} entries. Use 'offset' parameter to read beyond entry ${offset + sliced.length})` - : `\n(${entries.length} entries)`, - ``, - ].join("\n") + if (stat.type === "Directory") { + const items = yield* list(filepath) + const limit = params.limit ?? DEFAULT_READ_LIMIT + const offset = params.offset ?? 1 + const start = offset - 1 + const sliced = items.slice(start, start + limit) + const truncated = start + sliced.length < items.length + + return { + title, + output: [ + `${filepath}`, + `directory`, + ``, + sliced.join("\n"), + truncated + ? `\n(Showing ${sliced.length} of ${items.length} entries. Use 'offset' parameter to read beyond entry ${offset + sliced.length})` + : `\n(${items.length} entries)`, + ``, + ].join("\n"), + metadata: { + preview: sliced.slice(0, 20).join("\n"), + truncated, + loaded: [] as string[], + }, + } + } + + const loaded = yield* instruction.resolve(ctx.messages, filepath, ctx.messageID) + + const mime = AppFileSystem.mimeType(filepath) + const isImage = mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" + const isPdf = mime === "application/pdf" + if (isImage || isPdf) { + const msg = `${isImage ? "Image" : "PDF"} read successfully` + return { + title, + output: msg, + metadata: { + preview: msg, + truncated: false, + loaded: loaded.map((item) => item.filepath), + }, + attachments: [ + { + type: "file" as const, + mime, + url: `data:${mime};base64,${Buffer.from(yield* fs.readFile(filepath)).toString("base64")}`, + }, + ], + } + } + + if (yield* Effect.promise(() => isBinaryFile(filepath, Number(stat.size)))) { + return yield* Effect.fail(new Error(`Cannot read binary file: ${filepath}`)) + } + + const file = yield* Effect.promise(() => + lines(filepath, { limit: params.limit ?? DEFAULT_READ_LIMIT, offset: params.offset ?? 1 }), + ) + if (file.count < file.offset && !(file.count === 0 && file.offset === 1)) { + return yield* Effect.fail( + new Error(`Offset ${file.offset} is out of range for this file (${file.count} lines)`), + ) + } + + let output = [`${filepath}`, `file`, ""].join("\n") + output += file.raw.map((line, i) => `${i + file.offset}: ${line}`).join("\n") + + const last = file.offset + file.raw.length - 1 + const next = last + 1 + const truncated = file.more || file.cut + if (file.cut) { + output += `\n\n(Output capped at ${MAX_BYTES_LABEL}. Showing lines ${file.offset}-${last}. Use offset=${next} to continue.)` + } else if (file.more) { + output += `\n\n(Showing lines ${file.offset}-${last} of ${file.count}. Use offset=${next} to continue.)` + } else { + output += `\n\n(End of file - total ${file.count} lines)` + } + output += "\n" + + yield* warm(filepath, ctx.sessionID) + + if (loaded.length > 0) { + output += `\n\n\n${loaded.map((item) => item.content).join("\n\n")}\n` + } return { title, output, metadata: { - preview: sliced.slice(0, 20).join("\n"), + preview: file.raw.slice(0, 20).join("\n"), truncated, - loaded: [] as string[], + loaded: loaded.map((item) => item.filepath), }, } - } - - const instructions = await Instruction.resolve(ctx.messages, filepath, ctx.messageID) - - // Exclude SVG (XML-based) and vnd.fastbidsheet (.fbs extension, commonly FlatBuffers schema files) - const mime = Filesystem.mimeType(filepath) - const isImage = mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet" - const isPdf = mime === "application/pdf" - if (isImage || isPdf) { - const msg = `${isImage ? "Image" : "PDF"} read successfully` - return { - title, - output: msg, - metadata: { - preview: msg, - truncated: false, - loaded: instructions.map((i) => i.filepath), - }, - attachments: [ - { - type: "file", - mime, - url: `data:${mime};base64,${Buffer.from(await Filesystem.readBytes(filepath)).toString("base64")}`, - }, - ], - } - } - - const isBinary = await isBinaryFile(filepath, Number(stat.size)) - if (isBinary) throw new Error(`Cannot read binary file: ${filepath}`) - - const stream = createReadStream(filepath, { encoding: "utf8" }) - const rl = createInterface({ - input: stream, - // Note: we use the crlfDelay option to recognize all instances of CR LF - // ('\r\n') in file as a single line break. - crlfDelay: Infinity, }) - const limit = params.limit ?? DEFAULT_READ_LIMIT - const offset = params.offset ?? 1 - const start = offset - 1 - const raw: string[] = [] - let bytes = 0 - let lines = 0 - let truncatedByBytes = false - let hasMoreLines = false - try { - for await (const text of rl) { - lines += 1 - if (lines <= start) continue - - if (raw.length >= limit) { - hasMoreLines = true - continue - } - - const line = text.length > MAX_LINE_LENGTH ? text.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX : text - const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0) - if (bytes + size > MAX_BYTES) { - truncatedByBytes = true - hasMoreLines = true - break - } - - raw.push(line) - bytes += size - } - } finally { - rl.close() - stream.destroy() - } - - if (lines < offset && !(lines === 0 && offset === 1)) { - throw new Error(`Offset ${offset} is out of range for this file (${lines} lines)`) - } - - const content = raw.map((line, index) => { - return `${index + offset}: ${line}` - }) - const preview = raw.slice(0, 20).join("\n") - - let output = [`${filepath}`, `file`, ""].join("\n") - output += content.join("\n") - - const totalLines = lines - const lastReadLine = offset + raw.length - 1 - const nextOffset = lastReadLine + 1 - const truncated = hasMoreLines || truncatedByBytes - - if (truncatedByBytes) { - output += `\n\n(Output capped at ${MAX_BYTES_LABEL}. Showing lines ${offset}-${lastReadLine}. Use offset=${nextOffset} to continue.)` - } else if (hasMoreLines) { - output += `\n\n(Showing lines ${offset}-${lastReadLine} of ${totalLines}. Use offset=${nextOffset} to continue.)` - } else { - output += `\n\n(End of file - total ${totalLines} lines)` - } - output += "\n" - - // just warms the lsp client - LSP.touchFile(filepath, false) - await FileTime.read(ctx.sessionID, filepath) - - if (instructions.length > 0) { - output += `\n\n\n${instructions.map((i) => i.content).join("\n\n")}\n` - } - return { - title, - output, - metadata: { - preview, - truncated, - loaded: instructions.map((i) => i.filepath), + description: DESCRIPTION, + parameters, + async execute(params: z.infer, ctx) { + return Effect.runPromise(run(params, ctx).pipe(Effect.orDie)) }, } - }, -}) + }), +) + +async function lines(filepath: string, opts: { limit: number; offset: number }) { + const stream = createReadStream(filepath, { encoding: "utf8" }) + const rl = createInterface({ + input: stream, + // Note: we use the crlfDelay option to recognize all instances of CR LF + // ('\r\n') in file as a single line break. + crlfDelay: Infinity, + }) + + const start = opts.offset - 1 + const raw: string[] = [] + let bytes = 0 + let count = 0 + let cut = false + let more = false + try { + for await (const text of rl) { + count += 1 + if (count <= start) continue + + if (raw.length >= opts.limit) { + more = true + continue + } + + const line = text.length > MAX_LINE_LENGTH ? text.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX : text + const size = Buffer.byteLength(line, "utf-8") + (raw.length > 0 ? 1 : 0) + if (bytes + size > MAX_BYTES) { + cut = true + more = true + break + } + + raw.push(line) + bytes += size + } + } finally { + rl.close() + stream.destroy() + } + + return { raw, count, cut, more, offset: opts.offset } +} async function isBinaryFile(filepath: string, fileSize: number): Promise { const ext = path.extname(filepath).toLowerCase() @@ -274,7 +310,7 @@ async function isBinaryFile(filepath: string, fileSize: number): Promise()("@opencode/ToolRegistry") {} - export const layer: Layer.Layer = - Layer.effect( - Service, - Effect.gen(function* () { - const config = yield* Config.Service - const plugin = yield* Plugin.Service + export const layer: Layer.Layer< + Service, + never, + | Config.Service + | Plugin.Service + | Question.Service + | Todo.Service + | LSP.Service + | FileTime.Service + | Instruction.Service + | AppFileSystem.Service + > = Layer.effect( + Service, + Effect.gen(function* () { + const config = yield* Config.Service + const plugin = yield* Plugin.Service - const build = (tool: T | Effect.Effect) => - Effect.isEffect(tool) ? tool : Effect.succeed(tool) + const build = (tool: T | Effect.Effect) => + Effect.isEffect(tool) ? tool : Effect.succeed(tool) - const state = yield* InstanceState.make( - Effect.fn("ToolRegistry.state")(function* (ctx) { - const custom: Tool.Info[] = [] + const state = yield* InstanceState.make( + Effect.fn("ToolRegistry.state")(function* (ctx) { + const custom: Tool.Info[] = [] - function fromPlugin(id: string, def: ToolDefinition): Tool.Info { - return { - id, - init: async (initCtx) => ({ - parameters: z.object(def.args), - description: def.description, - execute: async (args, toolCtx) => { - const pluginCtx = { - ...toolCtx, - directory: ctx.directory, - worktree: ctx.worktree, - } as unknown as PluginToolContext - const result = await def.execute(args as any, pluginCtx) - const out = await Truncate.output(result, {}, initCtx?.agent) - return { - title: "", - output: out.truncated ? out.content : result, - metadata: { truncated: out.truncated, outputPath: out.truncated ? out.outputPath : undefined }, - } - }, - }), - } + function fromPlugin(id: string, def: ToolDefinition): Tool.Info { + return { + id, + init: async (initCtx) => ({ + parameters: z.object(def.args), + description: def.description, + execute: async (args, toolCtx) => { + const pluginCtx = { + ...toolCtx, + directory: ctx.directory, + worktree: ctx.worktree, + } as unknown as PluginToolContext + const result = await def.execute(args as any, pluginCtx) + const out = await Truncate.output(result, {}, initCtx?.agent) + return { + title: "", + output: out.truncated ? out.content : result, + metadata: { truncated: out.truncated, outputPath: out.truncated ? out.outputPath : undefined }, + } + }, + }), } + } - const dirs = yield* config.directories() - const matches = dirs.flatMap((dir) => - Glob.scanSync("{tool,tools}/*.{js,ts}", { cwd: dir, absolute: true, dot: true, symlink: true }), - ) - if (matches.length) yield* config.waitForDependencies() - for (const match of matches) { - const namespace = path.basename(match, path.extname(match)) - const mod = yield* Effect.promise( - () => import(process.platform === "win32" ? match : pathToFileURL(match).href), - ) - for (const [id, def] of Object.entries(mod)) { - custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def)) - } - } - - const plugins = yield* plugin.list() - for (const p of plugins) { - for (const [id, def] of Object.entries(p.tool ?? {})) { - custom.push(fromPlugin(id, def)) - } - } - - return { custom } - }), - ) - - const invalid = yield* build(InvalidTool) - const ask = yield* build(QuestionTool) - const bash = yield* build(BashTool) - const read = yield* build(ReadTool) - const glob = yield* build(GlobTool) - const grep = yield* build(GrepTool) - const edit = yield* build(EditTool) - const write = yield* build(WriteTool) - const task = yield* build(TaskTool) - const fetch = yield* build(WebFetchTool) - const todo = yield* build(TodoWriteTool) - const search = yield* build(WebSearchTool) - const code = yield* build(CodeSearchTool) - const skill = yield* build(SkillTool) - const patch = yield* build(ApplyPatchTool) - const lsp = yield* build(LspTool) - const batch = yield* build(BatchTool) - const plan = yield* build(PlanExitTool) - - const all = Effect.fn("ToolRegistry.all")(function* (custom: Tool.Info[]) { - const cfg = yield* config.get() - const question = - ["app", "cli", "desktop"].includes(Flag.OPENCODE_CLIENT) || Flag.OPENCODE_ENABLE_QUESTION_TOOL - - return [ - invalid, - ...(question ? [ask] : []), - bash, - read, - glob, - grep, - edit, - write, - task, - fetch, - todo, - search, - code, - skill, - patch, - ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [lsp] : []), - ...(cfg.experimental?.batch_tool === true ? [batch] : []), - ...(Flag.OPENCODE_EXPERIMENTAL_PLAN_MODE && Flag.OPENCODE_CLIENT === "cli" ? [plan] : []), - ...custom, - ] - }) - - const ids = Effect.fn("ToolRegistry.ids")(function* () { - const s = yield* InstanceState.get(state) - const tools = yield* all(s.custom) - return tools.map((t) => t.id) - }) - - const tools = Effect.fn("ToolRegistry.tools")(function* ( - model: { providerID: ProviderID; modelID: ModelID }, - agent?: Agent.Info, - ) { - const s = yield* InstanceState.get(state) - const allTools = yield* all(s.custom) - const filtered = allTools.filter((tool) => { - if (tool.id === "codesearch" || tool.id === "websearch") { - return model.providerID === ProviderID.opencode || Flag.OPENCODE_ENABLE_EXA - } - - const usePatch = - !!Env.get("OPENCODE_E2E_LLM_URL") || - (model.modelID.includes("gpt-") && !model.modelID.includes("oss") && !model.modelID.includes("gpt-4")) - if (tool.id === "apply_patch") return usePatch - if (tool.id === "edit" || tool.id === "write") return !usePatch - - return true - }) - return yield* Effect.forEach( - filtered, - Effect.fnUntraced(function* (tool: Tool.Info) { - using _ = log.time(tool.id) - const next = yield* Effect.promise(() => tool.init({ agent })) - const output = { - description: next.description, - parameters: next.parameters, - } - yield* plugin.trigger("tool.definition", { toolID: tool.id }, output) - return { - id: tool.id, - description: output.description, - parameters: output.parameters, - execute: next.execute, - formatValidationError: next.formatValidationError, - } - }), - { concurrency: "unbounded" }, + const dirs = yield* config.directories() + const matches = dirs.flatMap((dir) => + Glob.scanSync("{tool,tools}/*.{js,ts}", { cwd: dir, absolute: true, dot: true, symlink: true }), ) - }) + if (matches.length) yield* config.waitForDependencies() + for (const match of matches) { + const namespace = path.basename(match, path.extname(match)) + const mod = yield* Effect.promise( + () => import(process.platform === "win32" ? match : pathToFileURL(match).href), + ) + for (const [id, def] of Object.entries(mod)) { + custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def)) + } + } - return Service.of({ ids, named: { task, read }, tools }) - }), - ) + const plugins = yield* plugin.list() + for (const p of plugins) { + for (const [id, def] of Object.entries(p.tool ?? {})) { + custom.push(fromPlugin(id, def)) + } + } + + return { custom } + }), + ) + + const invalid = yield* build(InvalidTool) + const ask = yield* build(QuestionTool) + const bash = yield* build(BashTool) + const read = yield* build(ReadTool) + const glob = yield* build(GlobTool) + const grep = yield* build(GrepTool) + const edit = yield* build(EditTool) + const write = yield* build(WriteTool) + const task = yield* build(TaskTool) + const fetch = yield* build(WebFetchTool) + const todo = yield* build(TodoWriteTool) + const search = yield* build(WebSearchTool) + const code = yield* build(CodeSearchTool) + const skill = yield* build(SkillTool) + const patch = yield* build(ApplyPatchTool) + const lsp = yield* build(LspTool) + const batch = yield* build(BatchTool) + const plan = yield* build(PlanExitTool) + + const all = Effect.fn("ToolRegistry.all")(function* (custom: Tool.Info[]) { + const cfg = yield* config.get() + const question = ["app", "cli", "desktop"].includes(Flag.OPENCODE_CLIENT) || Flag.OPENCODE_ENABLE_QUESTION_TOOL + + return [ + invalid, + ...(question ? [ask] : []), + bash, + read, + glob, + grep, + edit, + write, + task, + fetch, + todo, + search, + code, + skill, + patch, + ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [lsp] : []), + ...(cfg.experimental?.batch_tool === true ? [batch] : []), + ...(Flag.OPENCODE_EXPERIMENTAL_PLAN_MODE && Flag.OPENCODE_CLIENT === "cli" ? [plan] : []), + ...custom, + ] + }) + + const ids = Effect.fn("ToolRegistry.ids")(function* () { + const s = yield* InstanceState.get(state) + const tools = yield* all(s.custom) + return tools.map((t) => t.id) + }) + + const tools = Effect.fn("ToolRegistry.tools")(function* ( + model: { providerID: ProviderID; modelID: ModelID }, + agent?: Agent.Info, + ) { + const s = yield* InstanceState.get(state) + const allTools = yield* all(s.custom) + const filtered = allTools.filter((tool) => { + if (tool.id === "codesearch" || tool.id === "websearch") { + return model.providerID === ProviderID.opencode || Flag.OPENCODE_ENABLE_EXA + } + + const usePatch = + !!Env.get("OPENCODE_E2E_LLM_URL") || + (model.modelID.includes("gpt-") && !model.modelID.includes("oss") && !model.modelID.includes("gpt-4")) + if (tool.id === "apply_patch") return usePatch + if (tool.id === "edit" || tool.id === "write") return !usePatch + + return true + }) + return yield* Effect.forEach( + filtered, + Effect.fnUntraced(function* (tool: Tool.Info) { + using _ = log.time(tool.id) + const next = yield* Effect.promise(() => tool.init({ agent })) + const output = { + description: next.description, + parameters: next.parameters, + } + yield* plugin.trigger("tool.definition", { toolID: tool.id }, output) + return { + id: tool.id, + description: output.description, + parameters: output.parameters, + execute: next.execute, + formatValidationError: next.formatValidationError, + } + }), + { concurrency: "unbounded" }, + ) + }) + + return Service.of({ ids, named: { task, read }, tools }) + }), + ) export const defaultLayer = Layer.unwrap( Effect.sync(() => @@ -226,6 +239,10 @@ export namespace ToolRegistry { Layer.provide(Plugin.defaultLayer), Layer.provide(Question.defaultLayer), Layer.provide(Todo.defaultLayer), + Layer.provide(LSP.defaultLayer), + Layer.provide(FileTime.defaultLayer), + Layer.provide(Instruction.defaultLayer), + Layer.provide(AppFileSystem.defaultLayer), ), ), ) diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts index d58565f433..12345266b3 100644 --- a/packages/opencode/test/tool/read.test.ts +++ b/packages/opencode/test/tool/read.test.ts @@ -1,12 +1,20 @@ -import { afterEach, describe, expect, test } from "bun:test" +import { afterEach, describe, expect } from "bun:test" +import { Cause, Effect, Exit, Layer } from "effect" import path from "path" -import { ReadTool } from "../../src/tool/read" -import { Instance } from "../../src/project/instance" -import { Filesystem } from "../../src/util/filesystem" -import { tmpdir } from "../fixture/fixture" -import { Permission } from "../../src/permission" import { Agent } from "../../src/agent/agent" +import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner" +import { AppFileSystem } from "../../src/filesystem" +import { FileTime } from "../../src/file/time" +import { LSP } from "../../src/lsp" +import { Permission } from "../../src/permission" +import { Instance } from "../../src/project/instance" import { SessionID, MessageID } from "../../src/session/schema" +import { Instruction } from "../../src/session/instruction" +import { ReadTool } from "../../src/tool/read" +import { Tool } from "../../src/tool/tool" +import { Filesystem } from "../../src/util/filesystem" +import { provideInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" const FIXTURES_DIR = path.join(import.meta.dir, "fixtures") @@ -25,173 +33,171 @@ const ctx = { ask: async () => {}, } +const it = testEffect( + Layer.mergeAll( + Agent.defaultLayer, + AppFileSystem.defaultLayer, + CrossSpawnSpawner.defaultLayer, + FileTime.defaultLayer, + Instruction.defaultLayer, + LSP.defaultLayer, + ), +) + +const init = Effect.fn("ReadToolTest.init")(function* () { + const info = yield* ReadTool + return yield* Effect.promise(() => info.init()) +}) + +const run = Effect.fn("ReadToolTest.run")(function* ( + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + const tool = yield* init() + return yield* Effect.promise(() => tool.execute(args, next)) +}) + +const exec = Effect.fn("ReadToolTest.exec")(function* ( + dir: string, + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + return yield* provideInstance(dir)(run(args, next)) +}) + +const fail = Effect.fn("ReadToolTest.fail")(function* ( + dir: string, + args: Tool.InferParameters, + next: Tool.Context = ctx, +) { + const exit = yield* exec(dir, args, next).pipe(Effect.exit) + if (Exit.isFailure(exit)) { + const err = Cause.squash(exit.cause) + return err instanceof Error ? err : new Error(String(err)) + } + throw new Error("expected read to fail") +}) + const full = (p: string) => (process.platform === "win32" ? Filesystem.normalizePath(p) : p) const glob = (p: string) => process.platform === "win32" ? Filesystem.normalizePathPattern(p) : p.replaceAll("\\", "/") +const put = Effect.fn("ReadToolTest.put")(function* (p: string, content: string | Buffer | Uint8Array) { + const fs = yield* AppFileSystem.Service + yield* fs.writeWithDirs(p, content) +}) +const load = Effect.fn("ReadToolTest.load")(function* (p: string) { + const fs = yield* AppFileSystem.Service + return yield* fs.readFileString(p) +}) +const asks = () => { + const items: Array> = [] + return { + items, + next: { + ...ctx, + ask: async (req: Omit) => { + items.push(req) + }, + }, + } +} describe("tool.read external_directory permission", () => { - test("allows reading absolute path inside project directory", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "test.txt"), "hello world") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "test.txt") }, ctx) - expect(result.output).toContain("hello world") - }, - }) - }) + it.live("allows reading absolute path inside project directory", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "test.txt"), "hello world") - test("allows reading file in subdirectory inside project directory", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "subdir", "test.txt"), "nested content") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "subdir", "test.txt") }, ctx) - expect(result.output).toContain("nested content") - }, - }) - }) + const result = yield* exec(dir, { filePath: path.join(dir, "test.txt") }) + expect(result.output).toContain("hello world") + }), + ) - test("asks for external_directory permission when reading absolute path outside project", async () => { - await using outerTmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "secret.txt"), "secret data") - }, - }) - await using tmp = await tmpdir({ git: true }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const requests: Array> = [] - const testCtx = { - ...ctx, - ask: async (req: Omit) => { - requests.push(req) - }, - } - await read.execute({ filePath: path.join(outerTmp.path, "secret.txt") }, testCtx) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(glob(path.join(outerTmp.path, "*"))) - }, - }) - }) + it.live("allows reading file in subdirectory inside project directory", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "subdir", "test.txt"), "nested content") + + const result = yield* exec(dir, { filePath: path.join(dir, "subdir", "test.txt") }) + expect(result.output).toContain("nested content") + }), + ) + + it.live("asks for external_directory permission when reading absolute path outside project", () => + Effect.gen(function* () { + const outer = yield* tmpdirScoped() + const dir = yield* tmpdirScoped({ git: true }) + yield* put(path.join(outer, "secret.txt"), "secret data") + + const { items, next } = asks() + + yield* exec(dir, { filePath: path.join(outer, "secret.txt") }, next) + const ext = items.find((item) => item.permission === "external_directory") + expect(ext).toBeDefined() + expect(ext!.patterns).toContain(glob(path.join(outer, "*"))) + }), + ) if (process.platform === "win32") { - test("normalizes read permission paths on Windows", async () => { - await using tmp = await tmpdir({ - git: true, - init: async (dir) => { - await Bun.write(path.join(dir, "test.txt"), "hello world") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const requests: Array> = [] - const testCtx = { - ...ctx, - ask: async (req: Omit) => { - requests.push(req) - }, - } - const target = path.join(tmp.path, "test.txt") - const alt = target - .replace(/^[A-Za-z]:/, "") - .replaceAll("\\", "/") - .toLowerCase() - await read.execute({ filePath: alt }, testCtx) - const readReq = requests.find((r) => r.permission === "read") - expect(readReq).toBeDefined() - expect(readReq!.patterns).toEqual([full(target)]) - }, - }) - }) + it.live("normalizes read permission paths on Windows", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped({ git: true }) + yield* put(path.join(dir, "test.txt"), "hello world") + + const { items, next } = asks() + const target = path.join(dir, "test.txt") + const alt = target + .replace(/^[A-Za-z]:/, "") + .replaceAll("\\", "/") + .toLowerCase() + + yield* exec(dir, { filePath: alt }, next) + const read = items.find((item) => item.permission === "read") + expect(read).toBeDefined() + expect(read!.patterns).toEqual([full(target)]) + }), + ) } - test("asks for directory-scoped external_directory permission when reading external directory", async () => { - await using outerTmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "external", "a.txt"), "a") - }, - }) - await using tmp = await tmpdir({ git: true }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const requests: Array> = [] - const testCtx = { - ...ctx, - ask: async (req: Omit) => { - requests.push(req) - }, - } - await read.execute({ filePath: path.join(outerTmp.path, "external") }, testCtx) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - expect(extDirReq!.patterns).toContain(glob(path.join(outerTmp.path, "external", "*"))) - }, - }) - }) + it.live("asks for directory-scoped external_directory permission when reading external directory", () => + Effect.gen(function* () { + const outer = yield* tmpdirScoped() + const dir = yield* tmpdirScoped({ git: true }) + yield* put(path.join(outer, "external", "a.txt"), "a") - test("asks for external_directory permission when reading relative path outside project", async () => { - await using tmp = await tmpdir({ git: true }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const requests: Array> = [] - const testCtx = { - ...ctx, - ask: async (req: Omit) => { - requests.push(req) - }, - } - // This will fail because file doesn't exist, but we can check if permission was asked - await read.execute({ filePath: "../outside.txt" }, testCtx).catch(() => {}) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeDefined() - }, - }) - }) + const { items, next } = asks() - test("does not ask for external_directory permission when reading inside project", async () => { - await using tmp = await tmpdir({ - git: true, - init: async (dir) => { - await Bun.write(path.join(dir, "internal.txt"), "internal content") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const requests: Array> = [] - const testCtx = { - ...ctx, - ask: async (req: Omit) => { - requests.push(req) - }, - } - await read.execute({ filePath: path.join(tmp.path, "internal.txt") }, testCtx) - const extDirReq = requests.find((r) => r.permission === "external_directory") - expect(extDirReq).toBeUndefined() - }, - }) - }) + yield* exec(dir, { filePath: path.join(outer, "external") }, next) + const ext = items.find((item) => item.permission === "external_directory") + expect(ext).toBeDefined() + expect(ext!.patterns).toContain(glob(path.join(outer, "external", "*"))) + }), + ) + + it.live("asks for external_directory permission when reading relative path outside project", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped({ git: true }) + + const { items, next } = asks() + + yield* fail(dir, { filePath: "../outside.txt" }, next) + const ext = items.find((item) => item.permission === "external_directory") + expect(ext).toBeDefined() + }), + ) + + it.live("does not ask for external_directory permission when reading inside project", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped({ git: true }) + yield* put(path.join(dir, "internal.txt"), "internal content") + + const { items, next } = asks() + + yield* exec(dir, { filePath: path.join(dir, "internal.txt") }, next) + const ext = items.find((item) => item.permission === "external_directory") + expect(ext).toBeUndefined() + }), + ) }) describe("tool.read env file permissions", () => { @@ -205,261 +211,204 @@ describe("tool.read env file permissions", () => { ["environment.ts", false], ] - describe.each(["build", "plan"])("agent=%s", (agentName) => { - test.each(cases)("%s asks=%s", async (filename, shouldAsk) => { - await using tmp = await tmpdir({ - init: (dir) => Bun.write(path.join(dir, filename), "content"), - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const agent = await Agent.get(agentName) - let askedForEnv = false - const ctxWithPermissions = { - ...ctx, - ask: async (req: Omit) => { - for (const pattern of req.patterns) { - const rule = Permission.evaluate(req.permission, pattern, agent.permission) - if (rule.action === "ask" && req.permission === "read") { - askedForEnv = true + for (const agentName of ["build", "plan"] as const) { + describe(`agent=${agentName}`, () => { + for (const [filename, shouldAsk] of cases) { + it.live(`${filename} asks=${shouldAsk}`, () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, filename), "content") + + const asked = yield* provideInstance(dir)( + Effect.gen(function* () { + const agent = yield* Agent.Service + const info = yield* agent.get(agentName) + let asked = false + const next = { + ...ctx, + ask: async (req: Omit) => { + for (const pattern of req.patterns) { + const rule = Permission.evaluate(req.permission, pattern, info.permission) + if (rule.action === "ask" && req.permission === "read") { + asked = true + } + if (rule.action === "deny") { + throw new Permission.DeniedError({ ruleset: info.permission }) + } + } + }, } - if (rule.action === "deny") { - throw new Permission.DeniedError({ ruleset: agent.permission }) - } - } - }, - } - const read = await ReadTool.init() - await read.execute({ filePath: path.join(tmp.path, filename) }, ctxWithPermissions) - expect(askedForEnv).toBe(shouldAsk) - }, - }) + + yield* run({ filePath: path.join(dir, filename) }, next) + return asked + }), + ) + + expect(asked).toBe(shouldAsk) + }), + ) + } }) - }) + } }) describe("tool.read truncation", () => { - test("truncates large file by bytes and sets truncated metadata", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const base = await Filesystem.readText(path.join(FIXTURES_DIR, "models-api.json")) - const target = 60 * 1024 - const content = base.length >= target ? base : base.repeat(Math.ceil(target / base.length)) - await Filesystem.write(path.join(dir, "large.json"), content) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "large.json") }, ctx) - expect(result.metadata.truncated).toBe(true) - expect(result.output).toContain("Output capped at") - expect(result.output).toContain("Use offset=") - }, - }) - }) + it.live("truncates large file by bytes and sets truncated metadata", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const base = yield* load(path.join(FIXTURES_DIR, "models-api.json")) + const target = 60 * 1024 + const content = base.length >= target ? base : base.repeat(Math.ceil(target / base.length)) + yield* put(path.join(dir, "large.json"), content) - test("truncates by line count when limit is specified", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n") - await Bun.write(path.join(dir, "many-lines.txt"), lines) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "many-lines.txt"), limit: 10 }, ctx) - expect(result.metadata.truncated).toBe(true) - expect(result.output).toContain("Showing lines 1-10 of 100") - expect(result.output).toContain("Use offset=11") - expect(result.output).toContain("line0") - expect(result.output).toContain("line9") - expect(result.output).not.toContain("line10") - }, - }) - }) + const result = yield* exec(dir, { filePath: path.join(dir, "large.json") }) + expect(result.metadata.truncated).toBe(true) + expect(result.output).toContain("Output capped at") + expect(result.output).toContain("Use offset=") + }), + ) - test("does not truncate small file", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "small.txt"), "hello world") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "small.txt") }, ctx) - expect(result.metadata.truncated).toBe(false) - expect(result.output).toContain("End of file") - }, - }) - }) + it.live("truncates by line count when limit is specified", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n") + yield* put(path.join(dir, "many-lines.txt"), lines) - test("respects offset parameter", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const lines = Array.from({ length: 20 }, (_, i) => `line${i + 1}`).join("\n") - await Bun.write(path.join(dir, "offset.txt"), lines) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "offset.txt"), offset: 10, limit: 5 }, ctx) - expect(result.output).toContain("10: line10") - expect(result.output).toContain("14: line14") - expect(result.output).not.toContain("9: line10") - expect(result.output).not.toContain("15: line15") - expect(result.output).toContain("line10") - expect(result.output).toContain("line14") - expect(result.output).not.toContain("line0") - expect(result.output).not.toContain("line15") - }, - }) - }) + const result = yield* exec(dir, { filePath: path.join(dir, "many-lines.txt"), limit: 10 }) + expect(result.metadata.truncated).toBe(true) + expect(result.output).toContain("Showing lines 1-10 of 100") + expect(result.output).toContain("Use offset=11") + expect(result.output).toContain("line0") + expect(result.output).toContain("line9") + expect(result.output).not.toContain("line10") + }), + ) - test("throws when offset is beyond end of file", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const lines = Array.from({ length: 3 }, (_, i) => `line${i + 1}`).join("\n") - await Bun.write(path.join(dir, "short.txt"), lines) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - await expect( - read.execute({ filePath: path.join(tmp.path, "short.txt"), offset: 4, limit: 5 }, ctx), - ).rejects.toThrow("Offset 4 is out of range for this file (3 lines)") - }, - }) - }) + it.live("does not truncate small file", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "small.txt"), "hello world") - test("allows reading empty file at default offset", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "empty.txt"), "") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "empty.txt") }, ctx) - expect(result.metadata.truncated).toBe(false) - expect(result.output).toContain("End of file - total 0 lines") - }, - }) - }) + const result = yield* exec(dir, { filePath: path.join(dir, "small.txt") }) + expect(result.metadata.truncated).toBe(false) + expect(result.output).toContain("End of file") + }), + ) - test("throws when offset > 1 for empty file", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "empty.txt"), "") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - await expect(read.execute({ filePath: path.join(tmp.path, "empty.txt"), offset: 2 }, ctx)).rejects.toThrow( - "Offset 2 is out of range for this file (0 lines)", - ) - }, - }) - }) + it.live("respects offset parameter", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const lines = Array.from({ length: 20 }, (_, i) => `line${i + 1}`).join("\n") + yield* put(path.join(dir, "offset.txt"), lines) - test("does not mark final directory page as truncated", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Promise.all( - Array.from({ length: 10 }, (_, i) => Bun.write(path.join(dir, "dir", `file-${i + 1}.txt`), `line${i}`)), - ) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "dir"), offset: 6, limit: 5 }, ctx) - expect(result.metadata.truncated).toBe(false) - expect(result.output).not.toContain("Showing 5 of 10 entries") - }, - }) - }) + const result = yield* exec(dir, { filePath: path.join(dir, "offset.txt"), offset: 10, limit: 5 }) + expect(result.output).toContain("10: line10") + expect(result.output).toContain("14: line14") + expect(result.output).not.toContain("9: line10") + expect(result.output).not.toContain("15: line15") + expect(result.output).toContain("line10") + expect(result.output).toContain("line14") + expect(result.output).not.toContain("line0") + expect(result.output).not.toContain("line15") + }), + ) - test("truncates long lines", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const longLine = "x".repeat(3000) - await Bun.write(path.join(dir, "long-line.txt"), longLine) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "long-line.txt") }, ctx) - expect(result.output).toContain("(line truncated to 2000 chars)") - expect(result.output.length).toBeLessThan(3000) - }, - }) - }) + it.live("throws when offset is beyond end of file", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const lines = Array.from({ length: 3 }, (_, i) => `line${i + 1}`).join("\n") + yield* put(path.join(dir, "short.txt"), lines) - test("image files set truncated to false", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // 1x1 red PNG - const png = Buffer.from( - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==", - "base64", - ) - await Bun.write(path.join(dir, "image.png"), png) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "image.png") }, ctx) - expect(result.metadata.truncated).toBe(false) - expect(result.attachments).toBeDefined() - expect(result.attachments?.length).toBe(1) - expect(result.attachments?.[0]).not.toHaveProperty("id") - expect(result.attachments?.[0]).not.toHaveProperty("sessionID") - expect(result.attachments?.[0]).not.toHaveProperty("messageID") - }, - }) - }) + const err = yield* fail(dir, { filePath: path.join(dir, "short.txt"), offset: 4, limit: 5 }) + expect(err.message).toContain("Offset 4 is out of range for this file (3 lines)") + }), + ) - test("large image files are properly attached without error", async () => { - await Instance.provide({ - directory: FIXTURES_DIR, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(FIXTURES_DIR, "large-image.png") }, ctx) - expect(result.metadata.truncated).toBe(false) - expect(result.attachments).toBeDefined() - expect(result.attachments?.length).toBe(1) - expect(result.attachments?.[0].type).toBe("file") - expect(result.attachments?.[0]).not.toHaveProperty("id") - expect(result.attachments?.[0]).not.toHaveProperty("sessionID") - expect(result.attachments?.[0]).not.toHaveProperty("messageID") - }, - }) - }) + it.live("allows reading empty file at default offset", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "empty.txt"), "") - test(".fbs files (FlatBuffers schema) are read as text, not images", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // FlatBuffers schema content - const fbsContent = `namespace MyGame; + const result = yield* exec(dir, { filePath: path.join(dir, "empty.txt") }) + expect(result.metadata.truncated).toBe(false) + expect(result.output).toContain("End of file - total 0 lines") + }), + ) + + it.live("throws when offset > 1 for empty file", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "empty.txt"), "") + + const err = yield* fail(dir, { filePath: path.join(dir, "empty.txt"), offset: 2 }) + expect(err.message).toContain("Offset 2 is out of range for this file (0 lines)") + }), + ) + + it.live("does not mark final directory page as truncated", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* Effect.forEach( + Array.from({ length: 10 }, (_, i) => i), + (i) => put(path.join(dir, "dir", `file-${i + 1}.txt`), `line${i}`), + { + concurrency: "unbounded", + }, + ) + + const result = yield* exec(dir, { filePath: path.join(dir, "dir"), offset: 6, limit: 5 }) + expect(result.metadata.truncated).toBe(false) + expect(result.output).not.toContain("Showing 5 of 10 entries") + }), + ) + + it.live("truncates long lines", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "long-line.txt"), "x".repeat(3000)) + + const result = yield* exec(dir, { filePath: path.join(dir, "long-line.txt") }) + expect(result.output).toContain("(line truncated to 2000 chars)") + expect(result.output.length).toBeLessThan(3000) + }), + ) + + it.live("image files set truncated to false", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const png = Buffer.from( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==", + "base64", + ) + yield* put(path.join(dir, "image.png"), png) + + const result = yield* exec(dir, { filePath: path.join(dir, "image.png") }) + expect(result.metadata.truncated).toBe(false) + expect(result.attachments).toBeDefined() + expect(result.attachments?.length).toBe(1) + expect(result.attachments?.[0]).not.toHaveProperty("id") + expect(result.attachments?.[0]).not.toHaveProperty("sessionID") + expect(result.attachments?.[0]).not.toHaveProperty("messageID") + }), + ) + + it.live("large image files are properly attached without error", () => + Effect.gen(function* () { + const result = yield* exec(FIXTURES_DIR, { filePath: path.join(FIXTURES_DIR, "large-image.png") }) + expect(result.metadata.truncated).toBe(false) + expect(result.attachments).toBeDefined() + expect(result.attachments?.length).toBe(1) + expect(result.attachments?.[0].type).toBe("file") + expect(result.attachments?.[0]).not.toHaveProperty("id") + expect(result.attachments?.[0]).not.toHaveProperty("sessionID") + expect(result.attachments?.[0]).not.toHaveProperty("messageID") + }), + ) + + it.live(".fbs files (FlatBuffers schema) are read as text, not images", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const fbs = `namespace MyGame; table Monster { pos:Vec3; @@ -468,79 +417,52 @@ table Monster { } root_type Monster;` - await Bun.write(path.join(dir, "schema.fbs"), fbsContent) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "schema.fbs") }, ctx) - // Should be read as text, not as image - expect(result.attachments).toBeUndefined() - expect(result.output).toContain("namespace MyGame") - expect(result.output).toContain("table Monster") - }, - }) - }) + yield* put(path.join(dir, "schema.fbs"), fbs) + + const result = yield* exec(dir, { filePath: path.join(dir, "schema.fbs") }) + expect(result.attachments).toBeUndefined() + expect(result.output).toContain("namespace MyGame") + expect(result.output).toContain("table Monster") + }), + ) }) describe("tool.read loaded instructions", () => { - test("loads AGENTS.md from parent directory and includes in metadata", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "subdir", "AGENTS.md"), "# Test Instructions\nDo something special.") - await Bun.write(path.join(dir, "subdir", "nested", "test.txt"), "test content") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - const result = await read.execute({ filePath: path.join(tmp.path, "subdir", "nested", "test.txt") }, ctx) - expect(result.output).toContain("test content") - expect(result.output).toContain("system-reminder") - expect(result.output).toContain("Test Instructions") - expect(result.metadata.loaded).toBeDefined() - expect(result.metadata.loaded).toContain(path.join(tmp.path, "subdir", "AGENTS.md")) - }, - }) - }) + it.live("loads AGENTS.md from parent directory and includes in metadata", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "subdir", "AGENTS.md"), "# Test Instructions\nDo something special.") + yield* put(path.join(dir, "subdir", "nested", "test.txt"), "test content") + + const result = yield* exec(dir, { filePath: path.join(dir, "subdir", "nested", "test.txt") }) + expect(result.output).toContain("test content") + expect(result.output).toContain("system-reminder") + expect(result.output).toContain("Test Instructions") + expect(result.metadata.loaded).toBeDefined() + expect(result.metadata.loaded).toContain(path.join(dir, "subdir", "AGENTS.md")) + }), + ) }) describe("tool.read binary detection", () => { - test("rejects text extension files with null bytes", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - const bytes = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64]) - await Bun.write(path.join(dir, "null-byte.txt"), bytes) - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - await expect(read.execute({ filePath: path.join(tmp.path, "null-byte.txt") }, ctx)).rejects.toThrow( - "Cannot read binary file", - ) - }, - }) - }) + it.live("rejects text extension files with null bytes", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const bytes = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64]) + yield* put(path.join(dir, "null-byte.txt"), bytes) - test("rejects known binary extensions", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - await Bun.write(path.join(dir, "module.wasm"), "not really wasm") - }, - }) - await Instance.provide({ - directory: tmp.path, - fn: async () => { - const read = await ReadTool.init() - await expect(read.execute({ filePath: path.join(tmp.path, "module.wasm") }, ctx)).rejects.toThrow( - "Cannot read binary file", - ) - }, - }) - }) + const err = yield* fail(dir, { filePath: path.join(dir, "null-byte.txt") }) + expect(err.message).toContain("Cannot read binary file") + }), + ) + + it.live("rejects known binary extensions", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + yield* put(path.join(dir, "module.wasm"), "not really wasm") + + const err = yield* fail(dir, { filePath: path.join(dir, "module.wasm") }) + expect(err.message).toContain("Cannot read binary file") + }), + ) }) From 985663620f3068d07ebd4dda44cb365cc3db4dd7 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sat, 4 Apr 2026 23:07:01 +0000 Subject: [PATCH 07/25] chore: generate --- packages/sdk/openapi.json | 183 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 735c34c8ac..02370f75fc 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -1220,6 +1220,189 @@ ] } }, + "/experimental/console": { + "get": { + "operationId": "experimental.console.get", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "Get active Console provider metadata", + "description": "Get the active Console org name and the set of provider IDs managed by that Console org.", + "responses": { + "200": { + "description": "Active Console provider metadata", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "consoleManagedProviders": { + "type": "array", + "items": { + "type": "string" + } + }, + "activeOrgName": { + "type": "string" + } + }, + "required": ["consoleManagedProviders"] + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.console.get({\n ...\n})" + } + ] + } + }, + "/experimental/console/orgs": { + "get": { + "operationId": "experimental.console.listOrgs", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "List switchable Console orgs", + "description": "Get the available Console orgs across logged-in accounts, including the current active org.", + "responses": { + "200": { + "description": "Switchable Console orgs", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "orgs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "accountID": { + "type": "string" + }, + "accountEmail": { + "type": "string" + }, + "accountUrl": { + "type": "string" + }, + "orgID": { + "type": "string" + }, + "orgName": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": ["accountID", "accountEmail", "accountUrl", "orgID", "orgName", "active"] + } + } + }, + "required": ["orgs"] + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.console.listOrgs({\n ...\n})" + } + ] + } + }, + "/experimental/console/switch": { + "post": { + "operationId": "experimental.console.switchOrg", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "Switch active Console org", + "description": "Persist a new active Console account/org selection for the current local OpenCode state.", + "responses": { + "200": { + "description": "Switch success", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accountID": { + "type": "string" + }, + "orgID": { + "type": "string" + } + }, + "required": ["accountID", "orgID"] + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.console.switchOrg({\n ...\n})" + } + ] + } + }, "/experimental/tool/ids": { "get": { "operationId": "tool.ids", From c6ebc7ff7c8e2dcbc8d09406e764d31f80d40943 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 4 Apr 2026 19:25:37 -0400 Subject: [PATCH 08/25] fix(tui): only show org switch affordances when useful (#21054) --- packages/opencode/src/cli/cmd/tui/app.tsx | 30 +++++++++++-------- .../cli/cmd/tui/component/prompt/index.tsx | 9 +++++- packages/opencode/src/config/config.ts | 1 + packages/opencode/src/config/console-state.ts | 2 ++ .../src/server/routes/experimental.ts | 6 +++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 8ce7382929..6b2633c371 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -630,19 +630,23 @@ function App(props: { onSnapshot?: () => Promise }) { }, category: "Provider", }, - { - title: "Switch org", - value: "console.org.switch", - suggested: Boolean(sync.data.console_state.activeOrgName), - slash: { - name: "org", - aliases: ["orgs", "switch-org"], - }, - onSelect: () => { - dialog.replace(() => ) - }, - category: "Provider", - }, + ...(sync.data.console_state.switchableOrgCount > 1 + ? [ + { + title: "Switch org", + value: "console.org.switch", + suggested: Boolean(sync.data.console_state.activeOrgName), + slash: { + name: "org", + aliases: ["orgs", "switch-org"], + }, + onSelect: () => { + dialog.replace(() => ) + }, + category: "Provider", + }, + ] + : []), { title: "View status", keybind: "status_view", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 55bf1d5630..5123cea567 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -96,6 +96,7 @@ export function Prompt(props: PromptProps) { const shell = createMemo(() => props.placeholders?.shell ?? []) const [auto, setAuto] = createSignal() const activeOrgName = createMemo(() => sync.data.console_state.activeOrgName) + const canSwitchOrgs = createMemo(() => sync.data.console_state.switchableOrgCount > 1) const currentProviderLabel = createMemo(() => { const current = local.model.current() const provider = local.model.parsed().provider @@ -1118,7 +1119,13 @@ export function Prompt(props: PromptProps) { {props.right} - command.trigger("console.org.switch")}> + { + if (!canSwitchOrgs()) return + command.trigger("console.org.switch") + }} + > {`${CONSOLE_MANAGED_ICON} ${activeOrgName()}`} diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 83e677bcb7..b11ae83192 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1469,6 +1469,7 @@ export namespace Config { consoleState: { consoleManagedProviders: Array.from(consoleManagedProviders), activeOrgName, + switchableOrgCount: 0, }, } }) diff --git a/packages/opencode/src/config/console-state.ts b/packages/opencode/src/config/console-state.ts index a5d1f6d269..cf96a4e305 100644 --- a/packages/opencode/src/config/console-state.ts +++ b/packages/opencode/src/config/console-state.ts @@ -3,6 +3,7 @@ import z from "zod" export const ConsoleState = z.object({ consoleManagedProviders: z.array(z.string()), activeOrgName: z.string().optional(), + switchableOrgCount: z.number().int().nonnegative(), }) export type ConsoleState = z.infer @@ -10,4 +11,5 @@ export type ConsoleState = z.infer export const emptyConsoleState: ConsoleState = { consoleManagedProviders: [], activeOrgName: undefined, + switchableOrgCount: 0, } diff --git a/packages/opencode/src/server/routes/experimental.ts b/packages/opencode/src/server/routes/experimental.ts index a4b1f4d084..c673393d0e 100644 --- a/packages/opencode/src/server/routes/experimental.ts +++ b/packages/opencode/src/server/routes/experimental.ts @@ -54,7 +54,11 @@ export const ExperimentalRoutes = lazy(() => }, }), async (c) => { - return c.json(await Config.getConsoleState()) + const [consoleState, groups] = await Promise.all([Config.getConsoleState(), Account.orgsByAccount()]) + return c.json({ + ...consoleState, + switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0), + }) }, ) .get( From 6253ef0c27bd9f3f6bcf4ab3e57637b689bbc217 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sat, 4 Apr 2026 23:26:37 +0000 Subject: [PATCH 09/25] chore: generate --- packages/sdk/js/src/v2/gen/types.gen.ts | 1 + packages/sdk/openapi.json | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 4c348573f6..72e549e485 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2670,6 +2670,7 @@ export type ExperimentalConsoleGetResponses = { 200: { consoleManagedProviders: Array activeOrgName?: string + switchableOrgCount: number } } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 02370f75fc..1aa4010e7a 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -1257,9 +1257,14 @@ }, "activeOrgName": { "type": "string" + }, + "switchableOrgCount": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 } }, - "required": ["consoleManagedProviders"] + "required": ["consoleManagedProviders", "switchableOrgCount"] } } } From 8b8d4fa066a1de331f6e478ae4055636a9271707 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:40:28 -0700 Subject: [PATCH 10/25] test: add regression test for double counting bug (#21053) --- .../opencode/test/session/compaction.test.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index 0dac255b10..799bb3e2ae 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -1080,7 +1080,7 @@ describe("session.getUsage", () => { expect(result.tokens.cache.read).toBe(200) }) - test("handles reasoning tokens", () => { + test("separates reasoning tokens from output tokens", () => { const model = createModel({ context: 100_000, output: 32_000 }) const result = Session.getUsage({ model, @@ -1092,7 +1092,35 @@ describe("session.getUsage", () => { }, }) + expect(result.tokens.input).toBe(1000) + expect(result.tokens.output).toBe(400) expect(result.tokens.reasoning).toBe(100) + expect(result.tokens.total).toBe(1500) + }) + + test("does not double count reasoning tokens in cost", () => { + const model = createModel({ + context: 100_000, + output: 32_000, + cost: { + input: 0, + output: 15, + cache: { read: 0, write: 0 }, + }, + }) + const result = Session.getUsage({ + model, + usage: { + inputTokens: 0, + outputTokens: 1_000_000, + totalTokens: 1_000_000, + reasoningTokens: 250_000, + }, + }) + + expect(result.tokens.output).toBe(750_000) + expect(result.tokens.reasoning).toBe(250_000) + expect(result.cost).toBe(15) }) test("handles undefined optional values gracefully", () => { From 66b4e5e0209d7c2e161cf3593f5d2b0e0fe7f727 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 5 Apr 2026 00:35:38 -0400 Subject: [PATCH 11/25] doc: udpate doc --- packages/web/src/content/docs/zen.mdx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/web/src/content/docs/zen.mdx b/packages/web/src/content/docs/zen.mdx index 847654372f..c16fc5270e 100644 --- a/packages/web/src/content/docs/zen.mdx +++ b/packages/web/src/content/docs/zen.mdx @@ -94,8 +94,6 @@ You can also access our models through the following API endpoints. | GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| MiMo V2 Pro Free | mimo-v2-pro-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| MiMo V2 Omni Free | mimo-v2-omni-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Qwen3.6 Plus Free | qwen3.6-plus-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | @@ -122,8 +120,6 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**. | Model | Input | Output | Cached Read | Cached Write | | --------------------------------- | ------ | ------- | ----------- | ------------ | | Big Pickle | Free | Free | Free | - | -| MiMo V2 Pro Free | Free | Free | Free | - | -| MiMo V2 Omni Free | Free | Free | Free | - | | Qwen3.6 Plus Free | Free | Free | Free | - | | Nemotron 3 Super Free | Free | Free | Free | - | | MiniMax M2.5 Free | Free | Free | Free | - | @@ -169,8 +165,6 @@ Credit card fees are passed along at cost (4.4% + $0.30 per transaction); we don The free models: - MiniMax M2.5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. -- MiMo V2 Pro Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. -- MiMo V2 Omni Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - Qwen3.6 Plus Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - Nemotron 3 Super Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - Big Pickle is a stealth model that's free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. @@ -218,8 +212,6 @@ All our models are hosted in the US. Our providers follow a zero-retention polic - Big Pickle: During its free period, collected data may be used to improve the model. - MiniMax M2.5 Free: During its free period, collected data may be used to improve the model. -- MiMo V2 Pro Free: During its free period, collected data may be used to improve the model. -- MiMo V2 Omni Free: During its free period, collected data may be used to improve the model. - Qwen3.6 Plus Free: During its free period, collected data may be used to improve the model. - Nemotron 3 Super Free: During its free period, collected data may be used to improve the model. - OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data). From 3a0e00dd7f9192730f6d0eeee37ae0a5fb023927 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:55:22 -0700 Subject: [PATCH 12/25] tweak: add newline between and first line of read tool output to prevent confusion (#21070) --- packages/opencode/src/tool/read.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 366993020b..0b44c7ad5a 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -185,7 +185,7 @@ export const ReadTool = Tool.defineEffect( ) } - let output = [`${filepath}`, `file`, ""].join("\n") + let output = [`${filepath}`, `file`, "" + "\n"].join("\n") output += file.raw.map((line, i) => `${i + file.offset}: ${line}`).join("\n") const last = file.offset + file.raw.length - 1 From 68f4aa220ea68c539bd1e316a4e96fdd76b93560 Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Mon, 6 Apr 2026 10:26:40 +1000 Subject: [PATCH 13/25] fix(plugin): parse package specifiers with npm-package-arg and sanitize win32 cache paths (#21135) --- bun.lock | 2 + packages/opencode/package.json | 2 + packages/opencode/src/npm/index.ts | 8 +- packages/opencode/src/plugin/shared.ts | 28 +++++-- packages/opencode/test/npm.test.ts | 18 ++++ packages/opencode/test/plugin/shared.test.ts | 88 ++++++++++++++++++++ 6 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 packages/opencode/test/npm.test.ts create mode 100644 packages/opencode/test/plugin/shared.test.ts diff --git a/bun.lock b/bun.lock index cdf44a5d84..d4159c2495 100644 --- a/bun.lock +++ b/bun.lock @@ -371,6 +371,7 @@ "jsonc-parser": "3.3.1", "mime-types": "3.0.2", "minimatch": "10.0.3", + "npm-package-arg": "13.0.2", "open": "10.1.2", "opencode-gitlab-auth": "2.0.1", "opencode-poe-auth": "0.0.1", @@ -412,6 +413,7 @@ "@types/bun": "catalog:", "@types/cross-spawn": "catalog:", "@types/mime-types": "3.0.1", + "@types/npm-package-arg": "6.1.4", "@types/npmcli__arborist": "6.3.3", "@types/semver": "^7.5.8", "@types/turndown": "5.0.5", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d7f12549c0..40a0fed2fa 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -54,6 +54,7 @@ "@types/bun": "catalog:", "@types/cross-spawn": "catalog:", "@types/mime-types": "3.0.1", + "@types/npm-package-arg": "6.1.4", "@types/npmcli__arborist": "6.3.3", "@types/semver": "^7.5.8", "@types/turndown": "5.0.5", @@ -135,6 +136,7 @@ "jsonc-parser": "3.3.1", "mime-types": "3.0.2", "minimatch": "10.0.3", + "npm-package-arg": "13.0.2", "open": "10.1.2", "opencode-gitlab-auth": "2.0.1", "opencode-poe-auth": "0.0.1", diff --git a/packages/opencode/src/npm/index.ts b/packages/opencode/src/npm/index.ts index 69bb2ca528..3568ff20e2 100644 --- a/packages/opencode/src/npm/index.ts +++ b/packages/opencode/src/npm/index.ts @@ -11,6 +11,7 @@ import { Arborist } from "@npmcli/arborist" export namespace Npm { const log = Log.create({ service: "npm" }) + const illegal = process.platform === "win32" ? new Set(["<", ">", ":", '"', "|", "?", "*"]) : undefined export const InstallFailedError = NamedError.create( "NpmInstallFailedError", @@ -19,8 +20,13 @@ export namespace Npm { }), ) + export function sanitize(pkg: string) { + if (!illegal) return pkg + return Array.from(pkg, (char) => (illegal.has(char) || char.charCodeAt(0) < 32 ? "_" : char)).join("") + } + function directory(pkg: string) { - return path.join(Global.Path.cache, "packages", pkg) + return path.join(Global.Path.cache, "packages", sanitize(pkg)) } function resolveEntryPoint(name: string, dir: string) { diff --git a/packages/opencode/src/plugin/shared.ts b/packages/opencode/src/plugin/shared.ts index f92520d05d..6cda49786b 100644 --- a/packages/opencode/src/plugin/shared.ts +++ b/packages/opencode/src/plugin/shared.ts @@ -1,5 +1,6 @@ import path from "path" import { fileURLToPath, pathToFileURL } from "url" +import npa from "npm-package-arg" import semver from "semver" import { Npm } from "@/npm" import { Filesystem } from "@/util/filesystem" @@ -12,11 +13,24 @@ export function isDeprecatedPlugin(spec: string) { return DEPRECATED_PLUGIN_PACKAGES.some((pkg) => spec.includes(pkg)) } +function parse(spec: string) { + try { + return npa(spec) + } catch {} +} + export function parsePluginSpecifier(spec: string) { - const lastAt = spec.lastIndexOf("@") - const pkg = lastAt > 0 ? spec.substring(0, lastAt) : spec - const version = lastAt > 0 ? spec.substring(lastAt + 1) : "latest" - return { pkg, version } + const hit = parse(spec) + if (hit?.type === "alias" && !hit.name) { + const sub = (hit as npa.AliasResult).subSpec + if (sub?.name) { + const version = !sub.rawSpec || sub.rawSpec === "*" ? "latest" : sub.rawSpec + return { pkg: sub.name, version } + } + } + if (!hit?.name) return { pkg: spec, version: "" } + if (hit.raw === hit.name) return { pkg: hit.name, version: "latest" } + return { pkg: hit.name, version: hit.rawSpec } } export type PluginSource = "file" | "npm" @@ -190,9 +204,11 @@ export async function checkPluginCompatibility(target: string, opencodeVersion: } } -export async function resolvePluginTarget(spec: string, parsed = parsePluginSpecifier(spec)) { +export async function resolvePluginTarget(spec: string) { if (isPathPluginSpec(spec)) return resolvePathPluginTarget(spec) - const result = await Npm.add(parsed.pkg + "@" + parsed.version) + const hit = parse(spec) + const pkg = hit?.name && hit.raw === hit.name ? `${hit.name}@latest` : spec + const result = await Npm.add(pkg) return result.directory } diff --git a/packages/opencode/test/npm.test.ts b/packages/opencode/test/npm.test.ts new file mode 100644 index 0000000000..61e3ca6ddf --- /dev/null +++ b/packages/opencode/test/npm.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from "bun:test" +import { Npm } from "../src/npm" + +const win = process.platform === "win32" + +describe("Npm.sanitize", () => { + test("keeps normal scoped package specs unchanged", () => { + expect(Npm.sanitize("@opencode/acme")).toBe("@opencode/acme") + expect(Npm.sanitize("@opencode/acme@1.0.0")).toBe("@opencode/acme@1.0.0") + expect(Npm.sanitize("prettier")).toBe("prettier") + }) + + test("handles git https specs", () => { + const spec = "acme@git+https://github.com/opencode/acme.git" + const expected = win ? "acme@git+https_//github.com/opencode/acme.git" : spec + expect(Npm.sanitize(spec)).toBe(expected) + }) +}) diff --git a/packages/opencode/test/plugin/shared.test.ts b/packages/opencode/test/plugin/shared.test.ts new file mode 100644 index 0000000000..98475b02f4 --- /dev/null +++ b/packages/opencode/test/plugin/shared.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, test } from "bun:test" +import { parsePluginSpecifier } from "../../src/plugin/shared" + +describe("parsePluginSpecifier", () => { + test("parses standard npm package without version", () => { + expect(parsePluginSpecifier("acme")).toEqual({ + pkg: "acme", + version: "latest", + }) + }) + + test("parses standard npm package with version", () => { + expect(parsePluginSpecifier("acme@1.0.0")).toEqual({ + pkg: "acme", + version: "1.0.0", + }) + }) + + test("parses scoped npm package without version", () => { + expect(parsePluginSpecifier("@opencode/acme")).toEqual({ + pkg: "@opencode/acme", + version: "latest", + }) + }) + + test("parses scoped npm package with version", () => { + expect(parsePluginSpecifier("@opencode/acme@1.0.0")).toEqual({ + pkg: "@opencode/acme", + version: "1.0.0", + }) + }) + + test("parses package with git+https url", () => { + expect(parsePluginSpecifier("acme@git+https://github.com/opencode/acme.git")).toEqual({ + pkg: "acme", + version: "git+https://github.com/opencode/acme.git", + }) + }) + + test("parses scoped package with git+https url", () => { + expect(parsePluginSpecifier("@opencode/acme@git+https://github.com/opencode/acme.git")).toEqual({ + pkg: "@opencode/acme", + version: "git+https://github.com/opencode/acme.git", + }) + }) + + test("parses package with git+ssh url containing another @", () => { + expect(parsePluginSpecifier("acme@git+ssh://git@github.com/opencode/acme.git")).toEqual({ + pkg: "acme", + version: "git+ssh://git@github.com/opencode/acme.git", + }) + }) + + test("parses scoped package with git+ssh url containing another @", () => { + expect(parsePluginSpecifier("@opencode/acme@git+ssh://git@github.com/opencode/acme.git")).toEqual({ + pkg: "@opencode/acme", + version: "git+ssh://git@github.com/opencode/acme.git", + }) + }) + + test("parses unaliased git+ssh url", () => { + expect(parsePluginSpecifier("git+ssh://git@github.com/opencode/acme.git")).toEqual({ + pkg: "git+ssh://git@github.com/opencode/acme.git", + version: "", + }) + }) + + test("parses npm alias using the alias name", () => { + expect(parsePluginSpecifier("acme@npm:@opencode/acme@1.0.0")).toEqual({ + pkg: "acme", + version: "npm:@opencode/acme@1.0.0", + }) + }) + + test("parses bare npm protocol specifier using the target package", () => { + expect(parsePluginSpecifier("npm:@opencode/acme@1.0.0")).toEqual({ + pkg: "@opencode/acme", + version: "1.0.0", + }) + }) + + test("parses unversioned npm protocol specifier", () => { + expect(parsePluginSpecifier("npm:@opencode/acme")).toEqual({ + pkg: "@opencode/acme", + version: "latest", + }) + }) +}) From 9e156ea168fd5b21627e341085bba73018454cb1 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Mon, 6 Apr 2026 01:18:03 +0000 Subject: [PATCH 14/25] chore: update nix node_modules hashes --- nix/hashes.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nix/hashes.json b/nix/hashes.json index e1e8e12b70..e09f3cf6d1 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-0jwPCu2Lod433GPQLHN8eEkhfpPviDFfkFJmuvkRdlE=", - "aarch64-linux": "sha256-Qi0IkGkaIBKZsPLTO8kaTbCVL0cEfVOm/Y/6VUVI9TY=", - "aarch64-darwin": "sha256-1eZBBLgYVkjg5RYN/etR1Mb5UjU3VelElBB5ug5hQdc=", - "x86_64-darwin": "sha256-jdXgA+kZb/foFHR40UiPif6rsA2GDVCCVHnJR3jBUGI=" + "x86_64-linux": "sha256-LRhPPrOKCGUSCEWTpAxPdWKTKVNkg82WrvD25cP3jts=", + "aarch64-linux": "sha256-sbNxkil47n+B7v6ds5EYFybLytXUyRlu0Cpka0ZmDx4=", + "aarch64-darwin": "sha256-5+99gtpIHGygMW3VBAexNhmaORgI8LCxPk/Gf1fW/ds=", + "x86_64-darwin": "sha256-LqnvZGGnQaRxIoowOr5gf6lFgDhbgQhVPiAcRTtU6fE=" } } From 4712c18a5833da85cd3946357662b148e78573f7 Mon Sep 17 00:00:00 2001 From: Gautier DI FOLCO Date: Mon, 6 Apr 2026 04:14:11 +0200 Subject: [PATCH 15/25] feat(tui): make the mouse disablable (#6824, #7926) (#13748) --- packages/opencode/src/cli/cmd/tui/app.tsx | 3 +++ packages/opencode/src/config/tui-schema.ts | 1 + packages/opencode/src/flag/flag.ts | 1 + packages/web/src/content/docs/cli.mdx | 1 + packages/web/src/content/docs/config.mdx | 5 ++--- packages/web/src/content/docs/tui.mdx | 4 +++- 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 6b2633c371..d643c0704f 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -125,6 +125,8 @@ import type { EventSource } from "./context/sdk" import { DialogVariant } from "./component/dialog-variant" function rendererConfig(_config: TuiConfig.Info): CliRendererConfig { + const mouseEnabled = !Flag.OPENCODE_DISABLE_MOUSE && (_config.mouse ?? true) + return { externalOutputMode: "passthrough", targetFps: 60, @@ -133,6 +135,7 @@ function rendererConfig(_config: TuiConfig.Info): CliRendererConfig { useKittyKeyboard: { events: process.platform === "win32" }, autoFocus: false, openConsoleOnError: false, + useMouse: mouseEnabled, consoleOptions: { keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }], onCopySelection: (text) => { diff --git a/packages/opencode/src/config/tui-schema.ts b/packages/opencode/src/config/tui-schema.ts index b126d3c96a..a373b4d800 100644 --- a/packages/opencode/src/config/tui-schema.ts +++ b/packages/opencode/src/config/tui-schema.ts @@ -22,6 +22,7 @@ export const TuiOptions = z.object({ .enum(["auto", "stacked"]) .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), + mouse: z.boolean().optional().describe("Enable or disable mouse capture (default: true)"), }) export const TuiInfo = z diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 1ac52dd17f..739009502b 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -31,6 +31,7 @@ export namespace Flag { export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS") export const OPENCODE_DISABLE_AUTOCOMPACT = truthy("OPENCODE_DISABLE_AUTOCOMPACT") export const OPENCODE_DISABLE_MODELS_FETCH = truthy("OPENCODE_DISABLE_MODELS_FETCH") + export const OPENCODE_DISABLE_MOUSE = truthy("OPENCODE_DISABLE_MOUSE") export const OPENCODE_DISABLE_CLAUDE_CODE = truthy("OPENCODE_DISABLE_CLAUDE_CODE") export const OPENCODE_DISABLE_CLAUDE_CODE_PROMPT = OPENCODE_DISABLE_CLAUDE_CODE || truthy("OPENCODE_DISABLE_CLAUDE_CODE_PROMPT") diff --git a/packages/web/src/content/docs/cli.mdx b/packages/web/src/content/docs/cli.mdx index e2ba2404de..579038ad03 100644 --- a/packages/web/src/content/docs/cli.mdx +++ b/packages/web/src/content/docs/cli.mdx @@ -573,6 +573,7 @@ OpenCode can be configured using environment variables. | `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | Disable reading `~/.claude/CLAUDE.md` | | `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | Disable loading `.claude/skills` | | `OPENCODE_DISABLE_MODELS_FETCH` | boolean | Disable fetching models from remote sources | +| `OPENCODE_DISABLE_MOUSE` | boolean | Disable mouse capture in the TUI | | `OPENCODE_FAKE_VCS` | string | Fake VCS provider for testing purposes | | `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | Disable file time checking for optimization | | `OPENCODE_CLIENT` | string | Client identifier (defaults to `cli`) | diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index 39f3cd8ff0..52ee1da0a3 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -272,7 +272,8 @@ Use a dedicated `tui.json` (or `tui.jsonc`) file for TUI-specific settings. "scroll_acceleration": { "enabled": true }, - "diff_style": "auto" + "diff_style": "auto", + "mouse": true } ``` @@ -280,8 +281,6 @@ Use `OPENCODE_TUI_CONFIG` to point to a custom TUI config file. Legacy `theme`, `keybinds`, and `tui` keys in `opencode.json` are deprecated and automatically migrated when possible. -[Learn more about TUI configuration here](/docs/tui#configure). - --- ### Server diff --git a/packages/web/src/content/docs/tui.mdx b/packages/web/src/content/docs/tui.mdx index 6dfa7b3125..e89fb4af39 100644 --- a/packages/web/src/content/docs/tui.mdx +++ b/packages/web/src/content/docs/tui.mdx @@ -368,7 +368,8 @@ You can customize TUI behavior through `tui.json` (or `tui.jsonc`). "scroll_acceleration": { "enabled": true }, - "diff_style": "auto" + "diff_style": "auto", + "mouse": true } ``` @@ -381,6 +382,7 @@ This is separate from `opencode.json`, which configures server/runtime behavior. - `scroll_acceleration.enabled` - Enable macOS-style scroll acceleration for smooth, natural scrolling. When enabled, scroll speed increases with rapid scrolling gestures and stays precise for slower movements. **This setting takes precedence over `scroll_speed` and overrides it when enabled.** - `scroll_speed` - Controls how fast the TUI scrolls when using scroll commands (minimum: `0.001`, supports decimal values). Defaults to `3`. **Note: This is ignored if `scroll_acceleration.enabled` is set to `true`.** - `diff_style` - Controls diff rendering. `"auto"` adapts to terminal width, `"stacked"` always shows a single-column layout. +- `mouse` - Enable or disable mouse capture in the TUI (default: `true`). When disabled, the terminal's native mouse selection/scrolling behavior is preserved. Use `OPENCODE_TUI_CONFIG` to load a custom TUI config path. From f0f1e51c5c48111198612f9eca652aeffbad49d7 Mon Sep 17 00:00:00 2001 From: George Harker Date: Sun, 5 Apr 2026 19:29:34 -0700 Subject: [PATCH 16/25] fix(core): implement proper configOptions for acp (#21134) --- packages/opencode/src/acp/agent.ts | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 96a97be752..6e87e7642d 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -21,6 +21,9 @@ import { type Role, type SessionInfo, type SetSessionModelRequest, + type SessionConfigOption, + type SetSessionConfigOptionRequest, + type SetSessionConfigOptionResponse, type SetSessionModeRequest, type SetSessionModeResponse, type ToolCallContent, @@ -601,6 +604,7 @@ export namespace ACP { return { sessionId, + configOptions: load.configOptions, models: load.models, modes: load.modes, _meta: load._meta, @@ -660,6 +664,11 @@ export namespace ACP { result.modes.currentModeId = lastUser.agent this.sessionManager.setMode(sessionId, lastUser.agent) } + result.configOptions = buildConfigOptions({ + currentModelId: result.models.currentModelId, + availableModels: result.models.availableModels, + modes: result.modes, + }) } for (const msg of messages ?? []) { @@ -1266,6 +1275,11 @@ export namespace ACP { availableModels, }, modes, + configOptions: buildConfigOptions({ + currentModelId: formatModelIdWithVariant(model, currentVariant, availableVariants, true), + availableModels, + modes, + }), _meta: buildVariantMeta({ model, variant: this.sessionManager.getVariant(sessionId), @@ -1305,6 +1319,44 @@ export namespace ACP { this.sessionManager.setMode(params.sessionId, params.modeId) } + async setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise { + const session = this.sessionManager.get(params.sessionId) + const providers = await this.sdk.config + .providers({ directory: session.cwd }, { throwOnError: true }) + .then((x) => x.data!.providers) + const entries = sortProvidersByName(providers) + + if (params.configId === "model") { + if (typeof params.value !== "string") throw RequestError.invalidParams("model value must be a string") + const selection = parseModelSelection(params.value, providers) + this.sessionManager.setModel(session.id, selection.model) + this.sessionManager.setVariant(session.id, selection.variant) + } else if (params.configId === "mode") { + if (typeof params.value !== "string") throw RequestError.invalidParams("mode value must be a string") + const availableModes = await this.loadAvailableModes(session.cwd) + if (!availableModes.some((mode) => mode.id === params.value)) { + throw RequestError.invalidParams(JSON.stringify({ error: `Mode not found: ${params.value}` })) + } + this.sessionManager.setMode(session.id, params.value) + } else { + throw RequestError.invalidParams(JSON.stringify({ error: `Unknown config option: ${params.configId}` })) + } + + const updatedSession = this.sessionManager.get(session.id) + const model = updatedSession.model ?? (await defaultModel(this.config, session.cwd)) + const availableVariants = modelVariantsFromProviders(entries, model) + const currentModelId = formatModelIdWithVariant(model, updatedSession.variant, availableVariants, true) + const availableModels = buildAvailableModels(entries, { includeVariants: true }) + const modeState = await this.resolveModeState(session.cwd, session.id) + const modes = modeState.currentModeId + ? { availableModes: modeState.availableModes, currentModeId: modeState.currentModeId } + : undefined + + return { + configOptions: buildConfigOptions({ currentModelId, availableModels, modes }), + } + } + async prompt(params: PromptRequest) { const sessionID = params.sessionId const session = this.sessionManager.get(sessionID) @@ -1760,4 +1812,36 @@ export namespace ACP { return { model: parsed, variant: undefined } } + + function buildConfigOptions(input: { + currentModelId: string + availableModels: ModelOption[] + modes?: { availableModes: ModeOption[]; currentModeId: string } | undefined + }): SessionConfigOption[] { + const options: SessionConfigOption[] = [ + { + id: "model", + name: "Model", + category: "model", + type: "select", + currentValue: input.currentModelId, + options: input.availableModels.map((m) => ({ value: m.modelId, name: m.name })), + }, + ] + if (input.modes) { + options.push({ + id: "mode", + name: "Session Mode", + category: "mode", + type: "select", + currentValue: input.modes.currentModeId, + options: input.modes.availableModes.map((m) => ({ + value: m.id, + name: m.name, + ...(m.description ? { description: m.description } : {}), + })), + }) + } + return options + } } From 9965d385de42c902282dc1316235d75f036142fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Steenhuis?= Date: Mon, 6 Apr 2026 04:34:53 +0200 Subject: [PATCH 17/25] fix: pass both 'openai' and 'azure' providerOptions keys for @ai-sdk/azure (#20272) Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/provider/transform.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index c402238685..c1617da40b 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -936,6 +936,12 @@ export namespace ProviderTransform { } const key = sdkKey(model.api.npm) ?? model.providerID + // @ai-sdk/azure delegates to OpenAIChatLanguageModel which reads from + // providerOptions["openai"], but OpenAIResponsesLanguageModel checks + // "azure" first. Pass both so model options work on either code path. + if (model.api.npm === "@ai-sdk/azure") { + return { openai: options, azure: options } + } return { [key]: options } } From 77a462c93022b0545a952d903d52f74feaa7105d Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:38:35 +1000 Subject: [PATCH 18/25] fix(tui): default Ctrl+Z to undo on Windows (#21138) --- packages/opencode/src/cli/cmd/tui/app.tsx | 1 + .../tui/feature-plugins/home/tips-view.tsx | 4 +- packages/opencode/src/config/tui.ts | 10 +++- packages/opencode/test/config/tui.test.ts | 48 +++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index d643c0704f..93a5da037f 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -761,6 +761,7 @@ function App(props: { onSnapshot?: () => Promise }) { keybind: "terminal_suspend", category: "System", hidden: true, + enabled: tuiConfig.keybinds?.terminal_suspend !== "none", onSelect: () => { process.once("SIGCONT", () => { renderer.resume() diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx index 08e429617f..a87e4ed2b7 100644 --- a/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx @@ -148,5 +148,7 @@ const TIPS = [ "Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs", "Run {highlight}/help{/highlight} or {highlight}Ctrl+X H{/highlight} to show the help dialog", "Use {highlight}/rename{/highlight} to rename the current session", - "Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell", + ...(process.platform === "win32" + ? ["Press {highlight}Ctrl+Z{/highlight} to undo changes in your prompt"] + : ["Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell"]), ] diff --git a/packages/opencode/src/config/tui.ts b/packages/opencode/src/config/tui.ts index adfb3c7810..fa2022482d 100644 --- a/packages/opencode/src/config/tui.ts +++ b/packages/opencode/src/config/tui.ts @@ -111,7 +111,15 @@ export namespace TuiConfig { } } - acc.result.keybinds = Config.Keybinds.parse(acc.result.keybinds ?? {}) + const keybinds = { ...(acc.result.keybinds ?? {}) } + if (process.platform === "win32") { + // Native Windows terminals do not support POSIX suspend, so prefer prompt undo. + keybinds.terminal_suspend = "none" + keybinds.input_undo ??= unique(["ctrl+z", ...Config.Keybinds.shape.input_undo.parse(undefined).split(",")]).join( + ",", + ) + } + acc.result.keybinds = Config.Keybinds.parse(keybinds) const deps: Promise[] = [] if (acc.result.plugin?.length) { diff --git a/packages/opencode/test/config/tui.test.ts b/packages/opencode/test/config/tui.test.ts index a8d98b66cd..b761d59ea4 100644 --- a/packages/opencode/test/config/tui.test.ts +++ b/packages/opencode/test/config/tui.test.ts @@ -9,6 +9,7 @@ import { Global } from "../../src/global" import { Filesystem } from "../../src/util/filesystem" const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR! +const wintest = process.platform === "win32" ? test : test.skip beforeEach(async () => { await Config.invalidate(true) @@ -441,6 +442,53 @@ test("merges keybind overrides across precedence layers", async () => { }) }) +wintest("defaults Ctrl+Z to input undo on Windows", async () => { + await using tmp = await tmpdir() + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await TuiConfig.get() + expect(config.keybinds?.terminal_suspend).toBe("none") + expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z") + }, + }) +}) + +wintest("keeps explicit input undo overrides on Windows", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { input_undo: "ctrl+y" } })) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await TuiConfig.get() + expect(config.keybinds?.terminal_suspend).toBe("none") + expect(config.keybinds?.input_undo).toBe("ctrl+y") + }, + }) +}) + +wintest("ignores terminal suspend bindings on Windows", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { terminal_suspend: "alt+z" } })) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await TuiConfig.get() + expect(config.keybinds?.terminal_suspend).toBe("none") + expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z") + }, + }) +}) + test("OPENCODE_TUI_CONFIG provides settings when no project config exists", async () => { await using tmp = await tmpdir({ init: async (dir) => { From 342436dfc45062ef8383d356de24418a967eb512 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 6 Apr 2026 03:44:46 +0000 Subject: [PATCH 19/25] release: v1.3.16 --- bun.lock | 32 +++++++++++++------------- packages/app/package.json | 2 +- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop-electron/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++----- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index d4159c2495..b34ef09a41 100644 --- a/bun.lock +++ b/bun.lock @@ -26,7 +26,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -80,7 +80,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -114,7 +114,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -141,7 +141,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/openai": "3.0.48", @@ -165,7 +165,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -189,7 +189,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -222,7 +222,7 @@ }, "packages/desktop-electron": { "name": "@opencode-ai/desktop-electron", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -254,7 +254,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -283,7 +283,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -299,7 +299,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.3.15", + "version": "1.3.16", "bin": { "opencode": "./bin/opencode", }, @@ -430,7 +430,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -464,7 +464,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "cross-spawn": "catalog:", }, @@ -479,7 +479,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -514,7 +514,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -562,7 +562,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "zod": "catalog:", }, @@ -573,7 +573,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index 3d71ec1e7f..f9e3effb50 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.3.15", + "version": "1.3.16", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 6b52b23dc2..7b6be5eca0 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 3a1fa330f6..ac9bdc6235 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.3.15", + "version": "1.3.16", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index b630e41abc..61b3dddd65 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.3.15", + "version": "1.3.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index ff81ab8873..b7a5a6febe 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.3.15", + "version": "1.3.16", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 64af9910e2..41ebacff8c 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop-electron", "private": true, - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "homepage": "https://opencode.ai", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index eb0c8034cd..ca6b2dbeda 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index aee89a1504..13fefec835 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.3.15", + "version": "1.3.16", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index dc4a944ba1..6f1b0a7fe8 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.3.15" +version = "1.3.16" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.15/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 637adefec2..c853251ccd 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.3.15", + "version": "1.3.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 40a0fed2fa..4bc5f2e068 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.3.15", + "version": "1.3.16", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f4bd4e401c..b05d822c65 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 04fb5194ad..ff8255120b 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 3de4f3b615..8b85871809 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 16268d9c74..f769113006 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.3.15", + "version": "1.3.16", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index 7b3cf21590..a84f4c713d 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.3.15", + "version": "1.3.16", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index 24bb2354c3..7d898447d5 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.3.15", + "version": "1.3.16", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 36e9ec5540..564669fc26 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.3.15", + "version": "1.3.16", "publisher": "sst-dev", "repository": { "type": "git", From a8fd0159bee09a6d89cb53c86b3119a668c9ef23 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 5 Apr 2026 23:51:34 -0400 Subject: [PATCH 20/25] zen: remove header check --- packages/console/app/src/routes/zen/util/rateLimiter.ts | 5 ++--- packages/console/core/src/subscription.ts | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/console/app/src/routes/zen/util/rateLimiter.ts b/packages/console/app/src/routes/zen/util/rateLimiter.ts index 042ec51f2c..1606339810 100644 --- a/packages/console/app/src/routes/zen/util/rateLimiter.ts +++ b/packages/console/app/src/routes/zen/util/rateLimiter.ts @@ -17,9 +17,8 @@ export function createRateLimiter( const dict = i18n(localeFromRequest(request)) const limits = Subscription.getFreeLimits() - const headerExists = request.headers.has(limits.checkHeader) - const dailyLimit = !headerExists ? limits.fallbackValue : (rateLimit ?? limits.dailyRequests) - const isDefaultModel = headerExists && !rateLimit + const dailyLimit = rateLimit ?? limits.dailyRequests + const isDefaultModel = !rateLimit const ip = !rawIp.length ? "unknown" : rawIp const now = Date.now() diff --git a/packages/console/core/src/subscription.ts b/packages/console/core/src/subscription.ts index 9d6c3ce2b5..bee5818458 100644 --- a/packages/console/core/src/subscription.ts +++ b/packages/console/core/src/subscription.ts @@ -9,8 +9,6 @@ export namespace Subscription { free: z.object({ promoTokens: z.number().int(), dailyRequests: z.number().int(), - checkHeader: z.string(), - fallbackValue: z.number().int(), }), lite: z.object({ rollingLimit: z.number().int(), From 70b636a360517ddb91658eff8ce0c2bbde45cb9f Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 6 Apr 2026 00:32:55 -0400 Subject: [PATCH 21/25] zen: normalize ipv6 --- packages/console/app/src/routes/zen/util/handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index db5977bc16..3e191918e5 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -90,7 +90,8 @@ export async function handler( const body = await input.request.json() const model = opts.parseModel(url, body) const isStream = opts.parseIsStream(url, body) - const ip = input.request.headers.get("x-real-ip") ?? "" + const rawIp = input.request.headers.get("x-real-ip") ?? "" + const ip = rawIp.includes(":") ? rawIp.split(":").slice(0, 4).join(":") : rawIp const sessionId = input.request.headers.get("x-opencode-session") ?? "" const requestId = input.request.headers.get("x-opencode-request") ?? "" const projectId = input.request.headers.get("x-opencode-project") ?? "" From eaa272ef7f034137746d2ed5d13383d9ef20ca8d Mon Sep 17 00:00:00 2001 From: MC Date: Mon, 6 Apr 2026 01:26:04 -0400 Subject: [PATCH 22/25] fix: show clear error when Cloudflare provider env vars are missing (#20399) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline --- packages/opencode/src/auth/index.ts | 1 + .../cli/cmd/tui/component/dialog-provider.tsx | 12 +++- packages/opencode/src/plugin/cloudflare.ts | 67 +++++++++++++++++++ packages/opencode/src/plugin/index.ts | 10 ++- packages/opencode/src/provider/provider.ts | 45 +++++++++++-- packages/sdk/js/src/v2/gen/types.gen.ts | 3 + packages/sdk/openapi.json | 9 +++ 7 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 packages/opencode/src/plugin/cloudflare.ts diff --git a/packages/opencode/src/auth/index.ts b/packages/opencode/src/auth/index.ts index b6d340cc8d..2a9fb6c19e 100644 --- a/packages/opencode/src/auth/index.ts +++ b/packages/opencode/src/auth/index.ts @@ -24,6 +24,7 @@ export namespace Auth { export class Api extends Schema.Class("ApiAuth")({ type: Schema.Literal("api"), key: Schema.String, + metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)), }) {} export class WellKnown extends Schema.Class("WellKnownAuth")({ diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx index 8add73dd6e..cb7abb8227 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx @@ -129,7 +129,15 @@ export function createDialogProviderOptions() { } } if (method.type === "api") { - return dialog.replace(() => ) + let metadata: Record | undefined + if (method.prompts?.length) { + const value = await PromptsMethod({ dialog, prompts: method.prompts }) + if (!value) return + metadata = value + } + return dialog.replace(() => ( + + )) } }, } @@ -249,6 +257,7 @@ function CodeMethod(props: CodeMethodProps) { interface ApiMethodProps { providerID: string title: string + metadata?: Record } function ApiMethod(props: ApiMethodProps) { const dialog = useDialog() @@ -293,6 +302,7 @@ function ApiMethod(props: ApiMethodProps) { auth: { type: "api", key: value, + ...(props.metadata ? { metadata: props.metadata } : {}), }, }) await sdk.client.instance.dispose() diff --git a/packages/opencode/src/plugin/cloudflare.ts b/packages/opencode/src/plugin/cloudflare.ts new file mode 100644 index 0000000000..e20a488a36 --- /dev/null +++ b/packages/opencode/src/plugin/cloudflare.ts @@ -0,0 +1,67 @@ +import type { Hooks, PluginInput } from "@opencode-ai/plugin" + +export async function CloudflareWorkersAuthPlugin(_input: PluginInput): Promise { + const prompts = [ + ...(!process.env.CLOUDFLARE_ACCOUNT_ID + ? [ + { + type: "text" as const, + key: "accountId", + message: "Enter your Cloudflare Account ID", + placeholder: "e.g. 1234567890abcdef1234567890abcdef", + }, + ] + : []), + ] + + return { + auth: { + provider: "cloudflare-workers-ai", + methods: [ + { + type: "api", + label: "API key", + prompts, + }, + ], + }, + } +} + +export async function CloudflareAIGatewayAuthPlugin(_input: PluginInput): Promise { + const prompts = [ + ...(!process.env.CLOUDFLARE_ACCOUNT_ID + ? [ + { + type: "text" as const, + key: "accountId", + message: "Enter your Cloudflare Account ID", + placeholder: "e.g. 1234567890abcdef1234567890abcdef", + }, + ] + : []), + ...(!process.env.CLOUDFLARE_GATEWAY_ID + ? [ + { + type: "text" as const, + key: "gatewayId", + message: "Enter your Cloudflare AI Gateway ID", + placeholder: "e.g. my-gateway", + }, + ] + : []), + ] + + return { + auth: { + provider: "cloudflare-ai-gateway", + methods: [ + { + type: "api", + label: "Gateway API token", + prompts, + }, + ], + }, + } +} diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index fb60fa096e..df69c8eba7 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -10,6 +10,7 @@ import { NamedError } from "@opencode-ai/util/error" import { CopilotAuthPlugin } from "./github-copilot/copilot" import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth" import { PoeAuthPlugin } from "opencode-poe-auth" +import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare" import { Effect, Layer, ServiceMap, Stream } from "effect" import { InstanceState } from "@/effect/instance-state" import { makeRuntime } from "@/effect/run-service" @@ -46,7 +47,14 @@ export namespace Plugin { export class Service extends ServiceMap.Service()("@opencode/Plugin") {} // Built-in plugins that are directly imported (not installed from npm) - const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin, PoeAuthPlugin] + const INTERNAL_PLUGINS: PluginInstance[] = [ + CodexAuthPlugin, + CopilotAuthPlugin, + GitlabAuthPlugin, + PoeAuthPlugin, + CloudflareWorkersAuthPlugin, + CloudflareAIGatewayAuthPlugin, + ] function isServerPlugin(value: unknown): value is PluginInstance { return typeof value === "function" diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 924d13312a..9ca49bf8f1 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -672,13 +672,26 @@ export namespace Provider { } }), "cloudflare-workers-ai": Effect.fnUntraced(function* (input: Info) { - const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID") - if (!accountId) return { autoload: false } + // When baseURL is already configured (e.g. corporate config routing through a proxy/gateway), + // skip the account ID check because the URL is already fully specified. + if (input.options?.baseURL) return { autoload: false } + + const auth = yield* dep.auth(input.id) + const accountId = + Env.get("CLOUDFLARE_ACCOUNT_ID") || (auth?.type === "api" ? auth.metadata?.accountId : undefined) + if (!accountId) + return { + autoload: false, + async getModel() { + throw new Error( + "CLOUDFLARE_ACCOUNT_ID is missing. Set it with: export CLOUDFLARE_ACCOUNT_ID=", + ) + }, + } const apiKey = yield* Effect.gen(function* () { const envToken = Env.get("CLOUDFLARE_API_KEY") if (envToken) return envToken - const auth = yield* dep.auth(input.id) if (auth?.type === "api") return auth.key return undefined }) @@ -702,16 +715,34 @@ export namespace Provider { } }), "cloudflare-ai-gateway": Effect.fnUntraced(function* (input: Info) { - const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID") - const gateway = Env.get("CLOUDFLARE_GATEWAY_ID") + // When baseURL is already configured (e.g. corporate config), skip the ID checks. + if (input.options?.baseURL) return { autoload: false } - if (!accountId || !gateway) return { autoload: false } + const auth = yield* dep.auth(input.id) + const accountId = + Env.get("CLOUDFLARE_ACCOUNT_ID") || (auth?.type === "api" ? auth.metadata?.accountId : undefined) + const gateway = + Env.get("CLOUDFLARE_GATEWAY_ID") || (auth?.type === "api" ? auth.metadata?.gatewayId : undefined) + + if (!accountId || !gateway) { + const missing = [ + !accountId ? "CLOUDFLARE_ACCOUNT_ID" : undefined, + !gateway ? "CLOUDFLARE_GATEWAY_ID" : undefined, + ].filter((x): x is string => Boolean(x)) + return { + autoload: false, + async getModel() { + throw new Error( + `${missing.join(" and ")} missing. Set with: ${missing.map((x) => `export ${x}=`).join(" && ")}`, + ) + }, + } + } // Get API token from env or auth - required for authenticated gateways const apiToken = yield* Effect.gen(function* () { const envToken = Env.get("CLOUDFLARE_API_TOKEN") || Env.get("CF_AIG_TOKEN") if (envToken) return envToken - const auth = yield* dep.auth(input.id) if (auth?.type === "api") return auth.key return undefined }) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 72e549e485..548ab8363e 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1639,6 +1639,9 @@ export type OAuth = { export type ApiAuth = { type: "api" key: string + metadata?: { + [key: string]: string + } } export type WellKnownAuth = { diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 1aa4010e7a..e21c48e89a 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -11621,6 +11621,15 @@ }, "key": { "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } } }, "required": ["type", "key"] From a4a9ea4ab00fe58d35ea6fc4971bd47cb9e5b0ac Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:04:50 +1000 Subject: [PATCH 23/25] fix(tui): revert kitty keyboard events workaround on windows (#20180) --- packages/opencode/src/cli/cmd/tui/app.tsx | 2 +- .../src/cli/cmd/tui/component/prompt/index.tsx | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 93a5da037f..a80039dd2e 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -132,7 +132,7 @@ function rendererConfig(_config: TuiConfig.Info): CliRendererConfig { targetFps: 60, gatherStats: false, exitOnCtrlC: false, - useKittyKeyboard: { events: process.platform === "win32" }, + useKittyKeyboard: {}, autoFocus: false, openConsoleOnError: false, useMouse: mouseEnabled, diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 5123cea567..087742a979 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -18,7 +18,7 @@ import { usePromptStash } from "./stash" import { DialogStash } from "../dialog-stash" import { type AutocompleteRef, Autocomplete } from "./autocomplete" import { useCommandDialog } from "../dialog-command" -import { useKeyboard, useRenderer, type JSX } from "@opentui/solid" +import { useRenderer, type JSX } from "@opentui/solid" import { Editor } from "@tui/util/editor" import { useExit } from "../../context/exit" import { Clipboard } from "../../util/clipboard" @@ -400,20 +400,6 @@ export function Prompt(props: PromptProps) { ] }) - // Windows Terminal 1.25+ handles Ctrl+V on keydown when kitty events are - // enabled, but still reports the kitty key-release event. Probe on release. - if (process.platform === "win32") { - useKeyboard( - (evt) => { - if (!input.focused) return - if (evt.name === "v" && evt.ctrl && evt.eventType === "release") { - command.trigger("prompt.paste") - } - }, - { release: true }, - ) - } - const ref: PromptRef = { get focused() { return input.focused From 517e6c9aa4c61dbc125e7654fc596f1d529f20d9 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 6 Apr 2026 07:39:18 +0000 Subject: [PATCH 24/25] release: v1.3.17 --- bun.lock | 32 +++++++++++++------------- packages/app/package.json | 2 +- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop-electron/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +++++----- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bun.lock b/bun.lock index b34ef09a41..cfc65b8959 100644 --- a/bun.lock +++ b/bun.lock @@ -26,7 +26,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -80,7 +80,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -114,7 +114,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -141,7 +141,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/openai": "3.0.48", @@ -165,7 +165,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -189,7 +189,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -222,7 +222,7 @@ }, "packages/desktop-electron": { "name": "@opencode-ai/desktop-electron", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -254,7 +254,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -283,7 +283,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -299,7 +299,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.3.16", + "version": "1.3.17", "bin": { "opencode": "./bin/opencode", }, @@ -430,7 +430,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -464,7 +464,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "cross-spawn": "catalog:", }, @@ -479,7 +479,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -514,7 +514,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -562,7 +562,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "zod": "catalog:", }, @@ -573,7 +573,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index f9e3effb50..cb52544eab 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.3.16", + "version": "1.3.17", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 7b6be5eca0..1a01bf21b2 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ac9bdc6235..ae5185e116 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.3.16", + "version": "1.3.17", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 61b3dddd65..e05a516533 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.3.16", + "version": "1.3.17", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index b7a5a6febe..63878f7c55 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.3.16", + "version": "1.3.17", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 41ebacff8c..2e851d6cf0 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop-electron", "private": true, - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "homepage": "https://opencode.ai", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ca6b2dbeda..d4a8244468 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 13fefec835..e046366dc9 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.3.16", + "version": "1.3.17", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 6f1b0a7fe8..2c8e827ba7 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.3.16" +version = "1.3.17" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.16/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.3.17/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index c853251ccd..ae7f1c48d0 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.3.16", + "version": "1.3.17", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 4bc5f2e068..024c73e39b 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.3.16", + "version": "1.3.17", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index b05d822c65..5710476fd2 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index ff8255120b..03ff4b46b3 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 8b85871809..dad6eac206 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index f769113006..64520f707c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.3.16", + "version": "1.3.17", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index a84f4c713d..0eab52cccc 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.3.16", + "version": "1.3.17", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index 7d898447d5..77f54ef67c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.3.16", + "version": "1.3.17", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 564669fc26..3d3f94a819 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.3.16", + "version": "1.3.17", "publisher": "sst-dev", "repository": { "type": "git", From 73b6d9a6c978830de6fd4e4f4dcecfb5cabdfb87 Mon Sep 17 00:00:00 2001 From: Imanol Maiztegui Date: Tue, 14 Apr 2026 09:35:34 +0200 Subject: [PATCH 25/25] refactor: kilo compat for v1.3.17 --- README.ar.md | 4 +- README.bn.md | 4 +- README.br.md | 4 +- README.bs.md | 4 +- README.da.md | 4 +- README.de.md | 4 +- README.es.md | 4 +- README.fr.md | 4 +- README.gr.md | 4 +- README.it.md | 4 +- README.ja.md | 4 +- README.ko.md | 4 +- README.no.md | 4 +- README.pl.md | 4 +- README.ru.md | 4 +- README.th.md | 4 +- README.tr.md | 4 +- README.uk.md | 4 +- README.vi.md | 4 +- README.zh.md | 4 +- README.zht.md | 4 +- github/index.ts | 8 +- github/package.json | 5 +- infra/enterprise.ts | 10 +- package.json | 54 +++--- packages/app/e2e/backend.ts | 12 +- .../app/e2e/prompt/prompt-history.spec.ts | 2 +- packages/app/e2e/prompt/prompt-shell.spec.ts | 2 +- .../app/e2e/prompt/prompt-slash-share.spec.ts | 4 +- packages/app/e2e/session/session.spec.ts | 4 +- packages/app/e2e/utils.ts | 4 +- packages/app/package.json | 11 +- packages/app/playwright.config.ts | 4 +- packages/app/script/e2e-local.ts | 20 +-- .../components/dialog-connect-provider.tsx | 2 +- .../src/components/dialog-custom-provider.tsx | 2 +- packages/app/src/components/dialog-fork.tsx | 2 +- packages/app/src/components/file-tree.tsx | 2 +- .../prompt-input/build-request-parts.ts | 2 +- .../components/prompt-input/submit.test.ts | 4 +- .../app/src/components/prompt-input/submit.ts | 2 +- .../session/session-context-breakdown.test.ts | 2 +- .../session/session-context-breakdown.ts | 2 +- .../session/session-context-metrics.test.ts | 2 +- .../session/session-context-metrics.ts | 2 +- .../session/session-context-tab.tsx | 2 +- .../app/src/components/settings-general.tsx | 2 +- .../app/src/context/file/content-cache.ts | 2 +- packages/app/src/context/file/tree-store.ts | 2 +- packages/app/src/context/file/types.ts | 2 +- packages/app/src/context/file/watcher.ts | 2 +- packages/app/src/context/global-sdk.tsx | 2 +- packages/app/src/context/global-sync.tsx | 6 +- .../app/src/context/global-sync/bootstrap.ts | 10 +- .../src/context/global-sync/child-store.ts | 2 +- .../context/global-sync/event-reducer.test.ts | 2 +- .../src/context/global-sync/event-reducer.ts | 2 +- .../context/global-sync/session-cache.test.ts | 2 +- .../src/context/global-sync/session-cache.ts | 2 +- .../context/global-sync/session-trim.test.ts | 2 +- .../src/context/global-sync/session-trim.ts | 2 +- packages/app/src/context/global-sync/types.ts | 2 +- .../app/src/context/global-sync/utils.test.ts | 2 +- packages/app/src/context/global-sync/utils.ts | 2 +- packages/app/src/context/highlights.tsx | 2 +- packages/app/src/context/layout.tsx | 2 +- packages/app/src/context/notification.tsx | 2 +- .../context/permission-auto-respond.test.ts | 2 +- packages/app/src/context/permission.tsx | 2 +- packages/app/src/context/sdk.tsx | 2 +- .../app/src/context/sync-optimistic.test.ts | 2 +- packages/app/src/context/sync.tsx | 2 +- packages/app/src/entry.tsx | 2 +- packages/app/src/env.d.ts | 4 +- packages/app/src/i18n/ar.ts | 34 ++-- packages/app/src/i18n/br.ts | 34 ++-- packages/app/src/i18n/bs.ts | 34 ++-- packages/app/src/i18n/da.ts | 34 ++-- packages/app/src/i18n/de.ts | 34 ++-- packages/app/src/i18n/en.ts | 34 ++-- packages/app/src/i18n/es.ts | 34 ++-- packages/app/src/i18n/fr.ts | 34 ++-- packages/app/src/i18n/ja.ts | 34 ++-- packages/app/src/i18n/ko.ts | 34 ++-- packages/app/src/i18n/no.ts | 34 ++-- packages/app/src/i18n/pl.ts | 34 ++-- packages/app/src/i18n/ru.ts | 34 ++-- packages/app/src/i18n/th.ts | 34 ++-- packages/app/src/i18n/tr.ts | 34 ++-- packages/app/src/i18n/zh.ts | 34 ++-- packages/app/src/i18n/zht.ts | 34 ++-- packages/app/src/pages/error.tsx | 2 +- packages/app/src/pages/layout.tsx | 4 +- packages/app/src/pages/layout/helpers.test.ts | 2 +- packages/app/src/pages/layout/helpers.ts | 2 +- .../app/src/pages/layout/sidebar-items.tsx | 6 +- .../src/pages/layout/sidebar-workspace.tsx | 2 +- packages/app/src/pages/session.tsx | 2 +- .../composer/session-composer-state.test.ts | 2 +- .../composer/session-composer-state.ts | 2 +- .../composer/session-permission-dock.tsx | 2 +- .../composer/session-question-dock.tsx | 2 +- .../session/composer/session-request-tree.ts | 2 +- .../session/composer/session-todo-dock.tsx | 2 +- .../src/pages/session/message-timeline.tsx | 2 +- packages/app/src/pages/session/review-tab.tsx | 2 +- .../session/session-model-helpers.test.ts | 2 +- .../pages/session/session-model-helpers.ts | 2 +- .../src/pages/session/session-side-panel.tsx | 2 +- .../pages/session/use-session-commands.tsx | 2 +- .../pages/session/use-session-hash-scroll.ts | 2 +- packages/app/src/testing/session-composer.ts | 2 +- packages/app/src/utils/prompt.test.ts | 2 +- packages/app/src/utils/prompt.ts | 2 +- packages/app/src/utils/server.ts | 6 +- packages/console/app/package.json | 2 +- packages/console/app/src/lib/language.ts | 2 +- .../app/src/routes/zen/util/handler.ts | 16 +- .../zen/util/provider/openai-compatible.ts | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/console/resource/package.json | 3 +- .../electron-builder.config.ts | 2 +- .../desktop-electron/electron.vite.config.ts | 4 +- packages/desktop-electron/package.json | 7 +- .../scripts/finalize-latest-yml.ts | 4 +- packages/desktop-electron/scripts/predev.ts | 2 +- packages/desktop-electron/scripts/prepare.ts | 2 +- packages/desktop-electron/scripts/utils.ts | 2 +- packages/desktop-electron/src/main/cli.ts | 10 +- .../desktop-electron/src/main/constants.ts | 2 +- packages/desktop-electron/src/main/env.d.ts | 2 +- packages/desktop-electron/src/main/index.ts | 4 +- .../src/main/shell-env.test.ts | 4 +- packages/desktop-electron/src/main/windows.ts | 2 +- .../desktop-electron/src/renderer/index.tsx | 16 +- .../desktop-electron/src/renderer/updater.ts | 2 +- packages/desktop/package.json | 2 +- .../desktop/scripts/finalize-latest-json.ts | 8 +- packages/desktop/scripts/prepare.ts | 2 +- packages/desktop/src-tauri/Cargo.toml | 4 +- packages/desktop/src-tauri/src/cli.rs | 32 ++-- packages/desktop/src-tauri/src/constants.rs | 2 +- packages/desktop/src-tauri/src/lib.rs | 8 +- .../desktop/src-tauri/src/linux_display.rs | 4 +- packages/desktop/src-tauri/src/main.rs | 2 +- packages/desktop/src-tauri/src/windows.rs | 6 +- packages/desktop/src-tauri/tauri.conf.json | 8 +- .../desktop/src-tauri/tauri.prod.conf.json | 6 +- packages/desktop/src/i18n/ar.ts | 10 +- packages/desktop/src/i18n/br.ts | 10 +- packages/desktop/src/i18n/bs.ts | 10 +- packages/desktop/src/i18n/da.ts | 10 +- packages/desktop/src/i18n/de.ts | 10 +- packages/desktop/src/i18n/en.ts | 10 +- packages/desktop/src/i18n/es.ts | 10 +- packages/desktop/src/i18n/fr.ts | 10 +- packages/desktop/src/i18n/ja.ts | 10 +- packages/desktop/src/i18n/ko.ts | 10 +- packages/desktop/src/i18n/no.ts | 10 +- packages/desktop/src/i18n/pl.ts | 10 +- packages/desktop/src/i18n/ru.ts | 10 +- packages/desktop/src/i18n/zh.ts | 10 +- packages/desktop/src/i18n/zht.ts | 10 +- packages/desktop/src/index.tsx | 12 +- packages/desktop/src/menu.ts | 6 +- packages/desktop/src/updater.ts | 2 +- packages/enterprise/package.json | 4 +- packages/enterprise/src/core/share.ts | 2 +- packages/enterprise/src/core/storage.ts | 18 +- .../enterprise/src/routes/share/[shareID].tsx | 2 +- packages/enterprise/vite.config.ts | 4 +- packages/extensions/zed/extension.toml | 18 +- packages/function/package.json | 2 +- packages/opencode/drizzle.config.ts | 2 +- packages/opencode/package.json | 30 ++-- packages/opencode/script/build-node.ts | 4 +- packages/opencode/script/build.ts | 12 +- packages/opencode/script/publish.ts | 18 +- packages/opencode/specs/tui-plugins.md | 14 +- packages/opencode/src/acp/README.md | 2 +- packages/opencode/src/acp/agent.ts | 10 +- packages/opencode/src/acp/session.ts | 6 +- packages/opencode/src/acp/types.ts | 4 +- packages/opencode/src/cli/cmd/acp.ts | 6 +- packages/opencode/src/cli/cmd/generate.ts | 4 +- packages/opencode/src/cli/cmd/import.ts | 2 +- packages/opencode/src/cli/cmd/providers.ts | 2 +- packages/opencode/src/cli/cmd/run.ts | 20 +-- packages/opencode/src/cli/cmd/serve.ts | 4 +- packages/opencode/src/cli/cmd/session.ts | 4 +- packages/opencode/src/cli/cmd/tui/app.tsx | 14 +- packages/opencode/src/cli/cmd/tui/attach.ts | 4 +- .../cmd/tui/component/dialog-console-org.tsx | 2 +- .../cli/cmd/tui/component/dialog-provider.tsx | 2 +- .../tui/component/dialog-workspace-list.tsx | 4 +- .../cli/cmd/tui/component/prompt/history.tsx | 2 +- .../cli/cmd/tui/component/prompt/index.tsx | 2 +- .../src/cli/cmd/tui/context/route.tsx | 4 +- .../opencode/src/cli/cmd/tui/context/sdk.tsx | 4 +- .../opencode/src/cli/cmd/tui/context/sync.tsx | 6 +- .../src/cli/cmd/tui/context/theme.tsx | 2 +- .../cmd/tui/feature-plugins/home/footer.tsx | 2 +- .../cli/cmd/tui/feature-plugins/home/tips.tsx | 2 +- .../tui/feature-plugins/sidebar/context.tsx | 4 +- .../cmd/tui/feature-plugins/sidebar/files.tsx | 2 +- .../tui/feature-plugins/sidebar/footer.tsx | 2 +- .../cmd/tui/feature-plugins/sidebar/lsp.tsx | 2 +- .../cmd/tui/feature-plugins/sidebar/mcp.tsx | 2 +- .../cmd/tui/feature-plugins/sidebar/todo.tsx | 2 +- .../tui/feature-plugins/system/plugins.tsx | 2 +- .../opencode/src/cli/cmd/tui/plugin/api.tsx | 10 +- .../src/cli/cmd/tui/plugin/internal.ts | 2 +- .../src/cli/cmd/tui/plugin/runtime.ts | 6 +- .../opencode/src/cli/cmd/tui/plugin/slots.tsx | 2 +- .../session/dialog-fork-from-timeline.tsx | 2 +- .../tui/routes/session/dialog-timeline.tsx | 2 +- .../src/cli/cmd/tui/routes/session/index.tsx | 6 +- .../cli/cmd/tui/routes/session/permission.tsx | 2 +- .../cli/cmd/tui/routes/session/question.tsx | 2 +- .../tui/routes/session/subagent-footer.tsx | 2 +- packages/opencode/src/cli/cmd/tui/thread.ts | 8 +- .../opencode/src/cli/cmd/tui/ui/dialog.tsx | 4 +- .../opencode/src/cli/cmd/tui/util/model.ts | 2 +- .../src/cli/cmd/tui/util/transcript.ts | 2 +- packages/opencode/src/cli/cmd/tui/worker.ts | 6 +- packages/opencode/src/cli/cmd/web.ts | 4 +- packages/opencode/src/cli/heap.ts | 2 +- packages/opencode/src/cli/upgrade.ts | 4 +- packages/opencode/src/config/config.ts | 40 ++--- packages/opencode/src/config/paths.ts | 4 +- packages/opencode/src/config/tui-migrate.ts | 4 +- packages/opencode/src/config/tui.ts | 10 +- packages/opencode/src/file/time.ts | 2 +- packages/opencode/src/file/watcher.ts | 8 +- packages/opencode/src/flag/flag.ts | 158 +++++++++--------- packages/opencode/src/format/formatter.ts | 2 +- packages/opencode/src/global/index.ts | 4 +- packages/opencode/src/ide/index.ts | 2 +- packages/opencode/src/index.ts | 6 +- packages/opencode/src/installation/index.ts | 2 +- packages/opencode/src/installation/meta.ts | 8 +- packages/opencode/src/lsp/index.ts | 4 +- packages/opencode/src/lsp/server.ts | 46 ++--- packages/opencode/src/plugin/cloudflare.ts | 2 +- packages/opencode/src/plugin/codex.ts | 2 +- .../src/plugin/github-copilot/copilot.ts | 4 +- .../src/plugin/github-copilot/models.ts | 2 +- packages/opencode/src/plugin/index.ts | 14 +- packages/opencode/src/plugin/meta.ts | 2 +- packages/opencode/src/project/project.ts | 4 +- packages/opencode/src/provider/auth.ts | 2 +- packages/opencode/src/provider/models.ts | 10 +- packages/opencode/src/provider/provider.ts | 2 +- packages/opencode/src/provider/transform.ts | 2 +- packages/opencode/src/pty/index.ts | 2 +- packages/opencode/src/server/instance.ts | 2 +- packages/opencode/src/server/router.ts | 4 +- packages/opencode/src/server/server.ts | 4 +- packages/opencode/src/session/index.ts | 4 +- packages/opencode/src/session/instruction.ts | 18 +- packages/opencode/src/session/llm.ts | 8 +- packages/opencode/src/session/prompt.ts | 2 +- packages/opencode/src/share/share-next.ts | 4 +- packages/opencode/src/shell/shell.ts | 2 +- packages/opencode/src/skill/index.ts | 6 +- packages/opencode/src/storage/db.ts | 20 +-- packages/opencode/src/sync/index.ts | 2 +- packages/opencode/src/tool/bash.ts | 2 +- packages/opencode/src/tool/registry.ts | 10 +- .../test/acp/event-subscription.test.ts | 2 +- packages/opencode/test/agent/agent.test.ts | 6 +- .../test/cli/plugin-auth-picker.test.ts | 2 +- .../opencode/test/cli/tui/plugin-add.test.ts | 8 +- .../test/cli/tui/plugin-install.test.ts | 4 +- .../cli/tui/plugin-loader-entrypoint.test.ts | 32 ++-- .../test/cli/tui/plugin-loader-pure.test.ts | 16 +- .../test/cli/tui/plugin-loader.test.ts | 14 +- .../test/cli/tui/plugin-toggle.test.ts | 8 +- .../opencode/test/cli/tui/transcript.test.ts | 2 +- packages/opencode/test/config/config.test.ts | 122 +++++++------- packages/opencode/test/config/tui.test.ts | 18 +- .../test/effect/cross-spawn-spawner.test.ts | 6 +- packages/opencode/test/file/watcher.test.ts | 4 +- .../test/fixture/plugin-meta-worker.ts | 2 +- packages/opencode/test/fixture/tui-plugin.ts | 4 +- packages/opencode/test/fixture/tui-runtime.ts | 4 +- packages/opencode/test/ide/ide.test.ts | 12 +- .../test/plugin/loader-shared.test.ts | 16 +- packages/opencode/test/plugin/meta.test.ts | 14 +- packages/opencode/test/plugin/trigger.test.ts | 8 +- packages/opencode/test/preload.ts | 14 +- .../test/server/project-init-git.test.ts | 8 +- .../test/server/session-messages.test.ts | 8 +- .../opencode/test/session/instruction.test.ts | 20 +-- packages/opencode/test/session/system.test.ts | 6 +- packages/opencode/test/skill/skill.test.ts | 18 +- packages/opencode/test/storage/db.test.ts | 2 +- packages/opencode/test/sync/index.test.ts | 6 +- packages/opencode/test/tool/bash.test.ts | 2 +- packages/opencode/test/tool/registry.test.ts | 2 +- packages/opencode/test/tool/skill.test.ts | 18 +- packages/opencode/test/util/process.test.ts | 10 +- packages/plugin/package.json | 11 +- packages/plugin/src/index.ts | 8 +- packages/plugin/src/tui.ts | 8 +- packages/script/package.json | 4 +- packages/script/src/index.ts | 20 +-- packages/sdk/js/example/example.ts | 6 +- packages/sdk/js/package.json | 10 +- packages/sdk/js/script/build.ts | 2 +- packages/sdk/js/src/client.ts | 14 +- packages/sdk/js/src/gen/sdk.gen.ts | 2 +- packages/sdk/js/src/index.ts | 10 +- packages/sdk/js/src/process.ts | 2 +- packages/sdk/js/src/server.ts | 8 +- packages/sdk/js/src/v2/client.ts | 20 +-- packages/sdk/js/src/v2/gen/sdk.gen.ts | 8 +- packages/sdk/js/src/v2/index.ts | 10 +- packages/sdk/js/src/v2/server.ts | 8 +- packages/slack/package.json | 4 +- packages/slack/src/index.ts | 4 +- packages/storybook/package.json | 5 +- packages/ui/package.json | 12 +- packages/ui/src/components/favicon.tsx | 2 +- packages/ui/src/components/file-media.tsx | 2 +- packages/ui/src/components/font.stories.tsx | 4 +- packages/ui/src/components/icon.stories.tsx | 2 +- packages/ui/src/components/logo.stories.tsx | 2 +- .../ui/src/components/message-file.test.ts | 2 +- packages/ui/src/components/message-file.ts | 2 +- packages/ui/src/components/message-nav.tsx | 2 +- packages/ui/src/components/message-part.tsx | 2 +- packages/ui/src/components/session-retry.tsx | 2 +- packages/ui/src/components/session-review.tsx | 2 +- packages/ui/src/components/session-turn.tsx | 4 +- .../timeline-playground.stories.tsx | 2 +- .../components/todo-panel-motion.stories.tsx | 2 +- packages/ui/src/context/data.tsx | 2 +- packages/ui/src/context/marked.tsx | 12 +- packages/ui/src/pierre/media.ts | 2 +- packages/ui/vite.config.ts | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- script/duplicate-pr.ts | 4 +- script/raw-changelog.ts | 2 +- script/stats.ts | 4 +- sdks/vscode/package.json | 8 +- sdks/vscode/src/extension.ts | 6 +- turbo.json | 4 +- 351 files changed, 1399 insertions(+), 1347 deletions(-) diff --git a/README.ar.md b/README.ar.md index beb44589e6..850c24b512 100644 --- a/README.ar.md +++ b/README.ar.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop يحترم سكربت التثبيت ترتيب الاولوية التالي لمسار التثبيت: -1. `$OPENCODE_INSTALL_DIR` - مجلد تثبيت مخصص +1. `$KILO_INSTALL_DIR` - مجلد تثبيت مخصص 2. `$XDG_BIN_DIR` - مسار متوافق مع مواصفات XDG Base Directory 3. `$HOME/bin` - مجلد الثنائيات القياسي للمستخدم (ان وجد او امكن انشاؤه) 4. `$HOME/.opencode/bin` - المسار الافتراضي الاحتياطي ```bash # امثلة -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.bn.md b/README.bn.md index c7abc7346a..a59385ceb4 100644 --- a/README.bn.md +++ b/README.bn.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop ইনস্টল স্ক্রিপ্টটি ইনস্টলেশন পাতের জন্য নিম্নলিখিত অগ্রাধিকার ক্রম মেনে চলে: -1. `$OPENCODE_INSTALL_DIR` - কাস্টম ইনস্টলেশন ডিরেক্টরি +1. `$KILO_INSTALL_DIR` - কাস্টম ইনস্টলেশন ডিরেক্টরি 2. `$XDG_BIN_DIR` - XDG বেস ডিরেক্টরি স্পেসিফিকেশন সমর্থিত পাথ 3. `$HOME/bin` - সাধারণ ব্যবহারকারী বাইনারি ডিরেক্টরি (যদি বিদ্যমান থাকে বা তৈরি করা যায়) 4. `$HOME/.opencode/bin` - ডিফল্ট ফলব্যাক ```bash # উদাহরণ -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.br.md b/README.br.md index 6d1de21562..c2508f2b5d 100644 --- a/README.br.md +++ b/README.br.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop O script de instalação respeita a seguinte ordem de prioridade para o caminho de instalação: -1. `$OPENCODE_INSTALL_DIR` - Diretório de instalação personalizado +1. `$KILO_INSTALL_DIR` - Diretório de instalação personalizado 2. `$XDG_BIN_DIR` - Caminho compatível com a especificação XDG Base Directory 3. `$HOME/bin` - Diretório binário padrão do usuário (se existir ou puder ser criado) 4. `$HOME/.opencode/bin` - Fallback padrão ```bash # Exemplos -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.bs.md b/README.bs.md index 2cff8e0279..a4923b8204 100644 --- a/README.bs.md +++ b/README.bs.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Instalacijska skripta koristi sljedeći redoslijed prioriteta za putanju instalacije: -1. `$OPENCODE_INSTALL_DIR` - Prilagođeni instalacijski direktorij +1. `$KILO_INSTALL_DIR` - Prilagođeni instalacijski direktorij 2. `$XDG_BIN_DIR` - Putanja usklađena sa XDG Base Directory specifikacijom 3. `$HOME/bin` - Standardni korisnički bin direktorij (ako postoji ili se može kreirati) 4. `$HOME/.opencode/bin` - Podrazumijevana rezervna lokacija ```bash # Primjeri -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.da.md b/README.da.md index ac522f29c4..47a9f41a05 100644 --- a/README.da.md +++ b/README.da.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Installationsscriptet bruger følgende prioriteringsrækkefølge for installationsstien: -1. `$OPENCODE_INSTALL_DIR` - Tilpasset installationsmappe +1. `$KILO_INSTALL_DIR` - Tilpasset installationsmappe 2. `$XDG_BIN_DIR` - Sti der følger XDG Base Directory Specification 3. `$HOME/bin` - Standard bruger-bin-mappe (hvis den findes eller kan oprettes) 4. `$HOME/.opencode/bin` - Standard fallback ```bash # Eksempler -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.de.md b/README.de.md index 87a670f3fc..ee324385f5 100644 --- a/README.de.md +++ b/README.de.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Das Installationsskript beachtet die folgende Prioritätsreihenfolge für den Installationspfad: -1. `$OPENCODE_INSTALL_DIR` - Benutzerdefiniertes Installationsverzeichnis +1. `$KILO_INSTALL_DIR` - Benutzerdefiniertes Installationsverzeichnis 2. `$XDG_BIN_DIR` - XDG Base Directory Specification-konformer Pfad 3. `$HOME/bin` - Standard-Binärverzeichnis des Users (falls vorhanden oder erstellbar) 4. `$HOME/.opencode/bin` - Standard-Fallback ```bash # Beispiele -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.es.md b/README.es.md index 9e456af1c0..233cec3628 100644 --- a/README.es.md +++ b/README.es.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop El script de instalación respeta el siguiente orden de prioridad para la ruta de instalación: -1. `$OPENCODE_INSTALL_DIR` - Directorio de instalación personalizado +1. `$KILO_INSTALL_DIR` - Directorio de instalación personalizado 2. `$XDG_BIN_DIR` - Ruta compatible con la especificación XDG Base Directory 3. `$HOME/bin` - Directorio binario estándar del usuario (si existe o se puede crear) 4. `$HOME/.opencode/bin` - Alternativa por defecto ```bash # Ejemplos -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.fr.md b/README.fr.md index c1fca23376..a147206047 100644 --- a/README.fr.md +++ b/README.fr.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Le script d'installation respecte l'ordre de priorité suivant pour le chemin d'installation : -1. `$OPENCODE_INSTALL_DIR` - Répertoire d'installation personnalisé +1. `$KILO_INSTALL_DIR` - Répertoire d'installation personnalisé 2. `$XDG_BIN_DIR` - Chemin conforme à la spécification XDG Base Directory 3. `$HOME/bin` - Répertoire binaire utilisateur standard (s'il existe ou peut être créé) 4. `$HOME/.opencode/bin` - Repli par défaut ```bash # Exemples -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.gr.md b/README.gr.md index 2b2c2679d8..2e047ac125 100644 --- a/README.gr.md +++ b/README.gr.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Το script εγκατάστασης τηρεί την ακόλουθη σειρά προτεραιότητας για τη διαδρομή εγκατάστασης: -1. `$OPENCODE_INSTALL_DIR` - Προσαρμοσμένος κατάλογος εγκατάστασης +1. `$KILO_INSTALL_DIR` - Προσαρμοσμένος κατάλογος εγκατάστασης 2. `$XDG_BIN_DIR` - Διαδρομή συμβατή με τις προδιαγραφές XDG Base Directory 3. `$HOME/bin` - Τυπικός κατάλογος εκτελέσιμων αρχείων χρήστη (εάν υπάρχει ή μπορεί να δημιουργηθεί) 4. `$HOME/.opencode/bin` - Προεπιλεγμένη εφεδρική διαδρομή ```bash # Παραδείγματα -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.it.md b/README.it.md index 3e516a9027..d748c4346f 100644 --- a/README.it.md +++ b/README.it.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Lo script di installazione rispetta il seguente ordine di priorità per il percorso di installazione: -1. `$OPENCODE_INSTALL_DIR` – Directory di installazione personalizzata +1. `$KILO_INSTALL_DIR` – Directory di installazione personalizzata 2. `$XDG_BIN_DIR` – Percorso conforme alla XDG Base Directory Specification 3. `$HOME/bin` – Directory binaria standard dell’utente (se esiste o può essere creata) 4. `$HOME/.opencode/bin` – Fallback predefinito ```bash # Esempi -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.ja.md b/README.ja.md index 144dc7b6f8..a8325fc0ec 100644 --- a/README.ja.md +++ b/README.ja.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop インストールスクリプトは、インストール先パスを次の優先順位で決定します。 -1. `$OPENCODE_INSTALL_DIR` - カスタムのインストールディレクトリ +1. `$KILO_INSTALL_DIR` - カスタムのインストールディレクトリ 2. `$XDG_BIN_DIR` - XDG Base Directory Specification に準拠したパス 3. `$HOME/bin` - 標準のユーザー用バイナリディレクトリ(存在する場合、または作成できる場合) 4. `$HOME/.opencode/bin` - デフォルトのフォールバック ```bash # 例 -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.ko.md b/README.ko.md index 32defc0a5e..edd3b96f12 100644 --- a/README.ko.md +++ b/README.ko.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop 설치 스크립트는 설치 경로를 다음 우선순위로 결정합니다. -1. `$OPENCODE_INSTALL_DIR` - 사용자 지정 설치 디렉터리 +1. `$KILO_INSTALL_DIR` - 사용자 지정 설치 디렉터리 2. `$XDG_BIN_DIR` - XDG Base Directory Specification 준수 경로 3. `$HOME/bin` - 표준 사용자 바이너리 디렉터리 (존재하거나 생성 가능할 경우) 4. `$HOME/.opencode/bin` - 기본 폴백 ```bash # 예시 -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.no.md b/README.no.md index c3348286b2..886ff57219 100644 --- a/README.no.md +++ b/README.no.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Installasjonsskriptet bruker følgende prioritet for installasjonsstien: -1. `$OPENCODE_INSTALL_DIR` - Egendefinert installasjonsmappe +1. `$KILO_INSTALL_DIR` - Egendefinert installasjonsmappe 2. `$XDG_BIN_DIR` - Sti som følger XDG Base Directory Specification 3. `$HOME/bin` - Standard brukerbinar-mappe (hvis den finnes eller kan opprettes) 4. `$HOME/.opencode/bin` - Standard fallback ```bash # Eksempler -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.pl.md b/README.pl.md index 4c5a076656..5a60e0b287 100644 --- a/README.pl.md +++ b/README.pl.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Skrypt instalacyjny stosuje następujący priorytet wyboru ścieżki instalacji: -1. `$OPENCODE_INSTALL_DIR` - Własny katalog instalacji +1. `$KILO_INSTALL_DIR` - Własny katalog instalacji 2. `$XDG_BIN_DIR` - Ścieżka zgodna ze specyfikacją XDG Base Directory 3. `$HOME/bin` - Standardowy katalog binarny użytkownika (jeśli istnieje lub można go utworzyć) 4. `$HOME/.opencode/bin` - Domyślny fallback ```bash # Przykłady -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.ru.md b/README.ru.md index e507be70e6..b68a03907b 100644 --- a/README.ru.md +++ b/README.ru.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Скрипт установки выбирает путь установки в следующем порядке приоритета: -1. `$OPENCODE_INSTALL_DIR` - Пользовательский каталог установки +1. `$KILO_INSTALL_DIR` - Пользовательский каталог установки 2. `$XDG_BIN_DIR` - Путь, совместимый со спецификацией XDG Base Directory 3. `$HOME/bin` - Стандартный каталог пользовательских бинарников (если существует или можно создать) 4. `$HOME/.opencode/bin` - Fallback по умолчанию ```bash # Примеры -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.th.md b/README.th.md index 4a4ea62c95..4b90a8b921 100644 --- a/README.th.md +++ b/README.th.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop สคริปต์การติดตั้งจะใช้ลำดับความสำคัญตามเส้นทางการติดตั้ง: -1. `$OPENCODE_INSTALL_DIR` - ไดเรกทอรีการติดตั้งที่กำหนดเอง +1. `$KILO_INSTALL_DIR` - ไดเรกทอรีการติดตั้งที่กำหนดเอง 2. `$XDG_BIN_DIR` - เส้นทางที่สอดคล้องกับ XDG Base Directory Specification 3. `$HOME/bin` - ไดเรกทอรีไบนารีผู้ใช้มาตรฐาน (หากมีอยู่หรือสามารถสร้างได้) 4. `$HOME/.opencode/bin` - ค่าสำรองเริ่มต้น ```bash # ตัวอย่าง -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.tr.md b/README.tr.md index e88b40f875..d3b474a07b 100644 --- a/README.tr.md +++ b/README.tr.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Kurulum betiği (install script), kurulum yolu (installation path) için aşağıdaki öncelik sırasını takip eder: -1. `$OPENCODE_INSTALL_DIR` - Özel kurulum dizini +1. `$KILO_INSTALL_DIR` - Özel kurulum dizini 2. `$XDG_BIN_DIR` - XDG Base Directory Specification uyumlu yol 3. `$HOME/bin` - Standart kullanıcı binary dizini (varsa veya oluşturulabiliyorsa) 4. `$HOME/.opencode/bin` - Varsayılan yedek konum ```bash # Örnekler -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.uk.md b/README.uk.md index a1a0259b6d..1471aa6fdd 100644 --- a/README.uk.md +++ b/README.uk.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Скрипт встановлення дотримується такого порядку пріоритету для шляху встановлення: -1. `$OPENCODE_INSTALL_DIR` - Користувацький каталог встановлення +1. `$KILO_INSTALL_DIR` - Користувацький каталог встановлення 2. `$XDG_BIN_DIR` - Шлях, сумісний зі специфікацією XDG Base Directory 3. `$HOME/bin` - Стандартний каталог користувацьких бінарників (якщо існує або його можна створити) 4. `$HOME/.opencode/bin` - Резервний варіант за замовчуванням ```bash # Приклади -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.vi.md b/README.vi.md index 0932c50f78..a4eaf22301 100644 --- a/README.vi.md +++ b/README.vi.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop Tập lệnh cài đặt tuân theo thứ tự ưu tiên sau cho đường dẫn cài đặt: -1. `$OPENCODE_INSTALL_DIR` - Thư mục cài đặt tùy chỉnh +1. `$KILO_INSTALL_DIR` - Thư mục cài đặt tùy chỉnh 2. `$XDG_BIN_DIR` - Đường dẫn tuân thủ XDG Base Directory Specification 3. `$HOME/bin` - Thư mục nhị phân tiêu chuẩn của người dùng (nếu tồn tại hoặc có thể tạo) 4. `$HOME/.opencode/bin` - Mặc định dự phòng ```bash # Ví dụ -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.zh.md b/README.zh.md index 46d9f761cb..928378213d 100644 --- a/README.zh.md +++ b/README.zh.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop 安装脚本按照以下优先级决定安装路径: -1. `$OPENCODE_INSTALL_DIR` - 自定义安装目录 +1. `$KILO_INSTALL_DIR` - 自定义安装目录 2. `$XDG_BIN_DIR` - 符合 XDG 基础目录规范的路径 3. `$HOME/bin` - 如果存在或可创建的用户二进制目录 4. `$HOME/.opencode/bin` - 默认备用路径 ```bash # 示例 -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/README.zht.md b/README.zht.md index 7ef51d8fdd..88c1c54565 100644 --- a/README.zht.md +++ b/README.zht.md @@ -86,14 +86,14 @@ scoop bucket add extras; scoop install extras/opencode-desktop 安裝腳本會依據以下優先順序決定安裝路徑: -1. `$OPENCODE_INSTALL_DIR` - 自定義安裝目錄 +1. `$KILO_INSTALL_DIR` - 自定義安裝目錄 2. `$XDG_BIN_DIR` - 符合 XDG 基礎目錄規範的路徑 3. `$HOME/bin` - 標準使用者執行檔目錄 (若存在或可建立) 4. `$HOME/.opencode/bin` - 預設備用路徑 ```bash # 範例 -OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +KILO_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash ``` diff --git a/github/index.ts b/github/index.ts index 6bfa964623..eea2bef71c 100644 --- a/github/index.ts +++ b/github/index.ts @@ -6,7 +6,7 @@ import * as core from "@actions/core" import * as github from "@actions/github" import type { Context as GitHubContext } from "@actions/github/lib/context" import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types" -import { createOpencodeClient } from "@opencode-ai/sdk" +import { createKiloClient } from "@kilocode/sdk" import { spawn } from "node:child_process" import { setTimeout as sleep } from "node:timers/promises" @@ -113,7 +113,7 @@ type IssueQueryResponse = { } } -const { client, server } = createOpencode() +const { client, server } = createKilo() let accessToken: string let octoRest: Octokit let octoGraph: typeof graphql @@ -228,12 +228,12 @@ try { } process.exit(exitCode) -function createOpencode() { +function createKilo() { const host = "127.0.0.1" const port = 4096 const url = `http://${host}:${port}` const proc = spawn(`opencode`, [`serve`, `--hostname=${host}`, `--port=${port}`]) - const client = createOpencodeClient({ baseUrl: url }) + const client = createKiloClient({ baseUrl: url }) return { server: { url, close: () => proc.kill() }, diff --git a/github/package.json b/github/package.json index e1b913abed..b17772bb9f 100644 --- a/github/package.json +++ b/github/package.json @@ -15,6 +15,7 @@ "@actions/github": "6.0.1", "@octokit/graphql": "9.0.1", "@octokit/rest": "catalog:", - "@opencode-ai/sdk": "workspace:*" - } + "@kilocode/sdk": "workspace:*" + }, + "version": "7.2.5" } diff --git a/infra/enterprise.ts b/infra/enterprise.ts index 22b4c6f44e..17eec3ffa1 100644 --- a/infra/enterprise.ts +++ b/infra/enterprise.ts @@ -8,10 +8,10 @@ const teams = new sst.cloudflare.x.SolidStart("Teams", { path: "packages/enterprise", buildCommand: "bun run build:cloudflare", environment: { - OPENCODE_STORAGE_ADAPTER: "r2", - OPENCODE_STORAGE_ACCOUNT_ID: sst.cloudflare.DEFAULT_ACCOUNT_ID, - OPENCODE_STORAGE_ACCESS_KEY_ID: SECRET.R2AccessKey.value, - OPENCODE_STORAGE_SECRET_ACCESS_KEY: SECRET.R2SecretKey.value, - OPENCODE_STORAGE_BUCKET: storage.name, + KILO_STORAGE_ADAPTER: "r2", + KILO_STORAGE_ACCOUNT_ID: sst.cloudflare.DEFAULT_ACCOUNT_ID, + KILO_STORAGE_ACCESS_KEY_ID: SECRET.R2AccessKey.value, + KILO_STORAGE_SECRET_ACCESS_KEY: SECRET.R2SecretKey.value, + KILO_STORAGE_BUCKET: storage.name, }, }) diff --git a/package.json b/package.json index fc73a94d26..cf104473a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "name": "opencode", + "name": "@kilocode/kilo", "description": "AI-powered development tool", "private": true, "type": "module", @@ -15,14 +15,13 @@ "prepare": "husky", "random": "echo 'Random script'", "hello": "echo 'Hello World!'", - "test": "echo 'do not run tests from root' && exit 1" + "test": "echo 'do not run tests from root' && exit 1", + "extension": "bun --cwd packages/kilo-vscode script/launch.ts" }, "workspaces": { "packages": [ "packages/*", - "packages/console/*", - "packages/sdk/js", - "packages/slack" + "packages/sdk/js" ], "catalog": { "@effect/platform-node": "4.0.0-beta.43", @@ -42,14 +41,14 @@ "@pierre/diffs": "1.1.0-beta.18", "@solid-primitives/storage": "4.3.3", "@tailwindcss/vite": "4.1.11", - "diff": "8.0.2", - "dompurify": "3.3.1", + "diff": "8.0.4", + "dompurify": "3.3.3", "drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4", "effect": "4.0.0-beta.43", "ai": "6.0.138", "cross-spawn": "7.0.6", - "hono": "4.10.7", + "hono": "4.12.12", "hono-openapi": "1.1.2", "fuzzysort": "3.1.0", "luxon": "3.6.1", @@ -58,18 +57,18 @@ "remend": "1.3.0", "@playwright/test": "1.51.0", "typescript": "5.8.2", - "@typescript/native-preview": "7.0.0-dev.20251207.1", + "@typescript/native-preview": "7.0.0-dev.20260316.1", "zod": "4.1.8", "remeda": "2.26.0", "shiki": "3.20.0", "solid-list": "0.3.0", "tailwindcss": "4.1.11", "virtua": "0.42.3", - "vite": "7.1.4", + "vite": "7.3.2", "@solidjs/meta": "0.29.4", "@solidjs/router": "0.15.4", "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", - "solid-js": "1.9.10", + "solid-js": "1.9.12", "vite-plugin-solid": "2.11.10" } }, @@ -83,18 +82,21 @@ "prettier": "3.6.2", "semver": "^7.6.0", "sst": "3.18.10", - "turbo": "2.8.13" + "turbo": "2.8.13", + "@changesets/changelog-github": "^0.5.1", + "@changesets/cli": "^2.27.10" }, "dependencies": { - "@aws-sdk/client-s3": "3.933.0", - "@opencode-ai/plugin": "workspace:*", + "@aws-sdk/client-s3": "3.1025.0", + "@kilocode/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", - "@opencode-ai/sdk": "workspace:*", - "typescript": "catalog:" + "@kilocode/sdk": "workspace:*", + "typescript": "catalog:", + "@morphllm/morphsdk": "0.2.141" }, "repository": { "type": "git", - "url": "https://github.com/anomalyco/opencode" + "url": "https://github.com/Kilo-Org/kilocode" }, "license": "MIT", "prettier": { @@ -112,12 +114,26 @@ ], "overrides": { "@types/bun": "catalog:", - "@types/node": "catalog:" + "@types/node": "catalog:", + "effect": "catalog:", + "@effect/platform-node-shared": "4.0.0-beta.35", + "path-to-regexp": ">=8.4.0", + "picomatch": ">=2.3.2", + "defu": "6.1.6", + "lodash": "4.18.1", + "@xmldom/xmldom": ">=0.8.12", + "smol-toml": ">=1.6.1", + "fastify": ">=5.8.3", + "diff": "8.0.4", + "dompurify": "3.3.3", + "happy-dom": ">=20.8.9" }, "patchedDependencies": { "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", "@ai-sdk/provider-utils@4.0.21": "patches/@ai-sdk%2Fprovider-utils@4.0.21.patch", "@ai-sdk/anthropic@3.0.64": "patches/@ai-sdk%2Fanthropic@3.0.64.patch" - } + }, + "version": "7.2.5", + "peerDependencies": {} } diff --git a/packages/app/e2e/backend.ts b/packages/app/e2e/backend.ts index 9febc4b3ff..71dbfe6293 100644 --- a/packages/app/e2e/backend.ts +++ b/packages/app/e2e/backend.ts @@ -70,16 +70,16 @@ export async function startBackend(label: string, input?: { llmUrl?: string }): const opencodeDir = path.join(repoDir, "packages", "opencode") const env = { ...process.env, - OPENCODE_DISABLE_LSP_DOWNLOAD: "true", - OPENCODE_DISABLE_DEFAULT_PLUGINS: "true", - OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: "true", - OPENCODE_TEST_HOME: path.join(sandbox, "home"), + KILO_DISABLE_LSP_DOWNLOAD: "true", + KILO_DISABLE_DEFAULT_PLUGINS: "true", + KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true", + KILO_TEST_HOME: path.join(sandbox, "home"), XDG_DATA_HOME: path.join(sandbox, "share"), XDG_CACHE_HOME: path.join(sandbox, "cache"), XDG_CONFIG_HOME: path.join(sandbox, "config"), XDG_STATE_HOME: path.join(sandbox, "state"), - OPENCODE_CLIENT: "app", - OPENCODE_STRICT_CONFIG_DEPS: "true", + KILO_CLIENT: "app", + KILO_STRICT_CONFIG_DEPS: "true", OPENCODE_E2E_LLM_URL: input?.llmUrl, } satisfies Record const out: string[] = [] diff --git a/packages/app/e2e/prompt/prompt-history.spec.ts b/packages/app/e2e/prompt/prompt-history.spec.ts index 55cb0c9aa3..d880c5e651 100644 --- a/packages/app/e2e/prompt/prompt-history.spec.ts +++ b/packages/app/e2e/prompt/prompt-history.spec.ts @@ -1,4 +1,4 @@ -import type { ToolPart } from "@opencode-ai/sdk/v2/client" +import type { ToolPart } from "@kilocode/sdk/v2/client" import type { Page } from "@playwright/test" import { test, expect } from "../fixtures" import { assistantText } from "../actions" diff --git a/packages/app/e2e/prompt/prompt-shell.spec.ts b/packages/app/e2e/prompt/prompt-shell.spec.ts index 28fa02dcd3..8897f9d6aa 100644 --- a/packages/app/e2e/prompt/prompt-shell.spec.ts +++ b/packages/app/e2e/prompt/prompt-shell.spec.ts @@ -1,4 +1,4 @@ -import type { ToolPart } from "@opencode-ai/sdk/v2/client" +import type { ToolPart } from "@kilocode/sdk/v2/client" import { test, expect } from "../fixtures" import { withSession } from "../actions" import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors" diff --git a/packages/app/e2e/prompt/prompt-slash-share.spec.ts b/packages/app/e2e/prompt/prompt-slash-share.spec.ts index f3eeceee5f..f72a76fc3e 100644 --- a/packages/app/e2e/prompt/prompt-slash-share.spec.ts +++ b/packages/app/e2e/prompt/prompt-slash-share.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from "../fixtures" import { promptSelector } from "../selectors" import { withSession } from "../actions" -const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1" +const shareDisabled = process.env.KILO_DISABLE_SHARE === "true" || process.env.KILO_DISABLE_SHARE === "1" async function seed(sdk: Parameters[0], sessionID: string) { await sdk.session.promptAsync({ @@ -23,7 +23,7 @@ async function seed(sdk: Parameters[0], sessionID: string) { } test("/share and /unshare update session share state", async ({ page, project }) => { - test.skip(shareDisabled, "Share is disabled in this environment (OPENCODE_DISABLE_SHARE).") + test.skip(shareDisabled, "Share is disabled in this environment (KILO_DISABLE_SHARE).") await project.open() await withSession(project.sdk, `e2e slash share ${Date.now()}`, async (session) => { diff --git a/packages/app/e2e/session/session.spec.ts b/packages/app/e2e/session/session.spec.ts index 1b5fb1b600..3c83656f88 100644 --- a/packages/app/e2e/session/session.spec.ts +++ b/packages/app/e2e/session/session.spec.ts @@ -9,7 +9,7 @@ import { } from "../actions" import { sessionItemSelector, inlineInputSelector } from "../selectors" -const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1" +const shareDisabled = process.env.KILO_DISABLE_SHARE === "true" || process.env.KILO_DISABLE_SHARE === "1" type Sdk = Parameters[0] @@ -126,7 +126,7 @@ test("session can be deleted via header menu", async ({ page, project }) => { }) test("session can be shared and unshared via header button", async ({ page, project }) => { - test.skip(shareDisabled, "Share is disabled in this environment (OPENCODE_DISABLE_SHARE).") + test.skip(shareDisabled, "Share is disabled in this environment (KILO_DISABLE_SHARE).") const stamp = Date.now() const title = `e2e share test ${stamp}` diff --git a/packages/app/e2e/utils.ts b/packages/app/e2e/utils.ts index 17a8785664..02cf5228f8 100644 --- a/packages/app/e2e/utils.ts +++ b/packages/app/e2e/utils.ts @@ -1,4 +1,4 @@ -import { createOpencodeClient } from "@opencode-ai/sdk/v2/client" +import { createKiloClient } from "@kilocode/sdk/v2/client" import { base64Encode, checksum } from "@opencode-ai/util/encode" export const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1" @@ -27,7 +27,7 @@ export const modKey = process.platform === "darwin" ? "Meta" : "Control" export const terminalToggleKey = "Control+Backquote" export function createSdk(directory?: string, baseUrl = serverUrl) { - return createOpencodeClient({ baseUrl, directory, throwOnError: true }) + return createKiloClient({ baseUrl, directory, throwOnError: true }) } export async function resolveDirectory(directory: string, baseUrl = serverUrl) { diff --git a/packages/app/package.json b/packages/app/package.json index cb52544eab..f21b736622 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.3.17", + "version": "7.2.5", "description": "", "type": "module", "exports": { @@ -40,7 +40,7 @@ }, "dependencies": { "@kobalte/core": "catalog:", - "@opencode-ai/sdk": "workspace:*", + "@kilocode/sdk": "workspace:*", "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", "@shikijs/transformers": "3.9.2", @@ -72,6 +72,9 @@ "solid-list": "catalog:", "tailwindcss": "catalog:", "virtua": "catalog:", - "zod": "catalog:" - } + "zod": "catalog:", + "@kilocode/kilo-ui": "workspace:*", + "@kilocode/kilo-i18n": "workspace:*" + }, + "peerDependencies": {} } diff --git a/packages/app/playwright.config.ts b/packages/app/playwright.config.ts index e9fb1cfe4e..0a1e56bb4f 100644 --- a/packages/app/playwright.config.ts +++ b/packages/app/playwright.config.ts @@ -31,8 +31,8 @@ export default defineConfig({ reuseExistingServer: reuse, timeout: 120_000, env: { - VITE_OPENCODE_SERVER_HOST: serverHost, - VITE_OPENCODE_SERVER_PORT: serverPort, + VITE_KILO_SERVER_HOST: serverHost, + VITE_KILO_SERVER_PORT: serverPort, }, }, use: { diff --git a/packages/app/script/e2e-local.ts b/packages/app/script/e2e-local.ts index 70442d0d76..5f3b2107d1 100644 --- a/packages/app/script/e2e-local.ts +++ b/packages/app/script/e2e-local.ts @@ -59,11 +59,11 @@ const keepSandbox = process.env.OPENCODE_E2E_KEEP_SANDBOX === "1" const serverEnv = { ...process.env, - OPENCODE_DISABLE_SHARE: process.env.OPENCODE_DISABLE_SHARE ?? "true", - OPENCODE_DISABLE_LSP_DOWNLOAD: "true", - OPENCODE_DISABLE_DEFAULT_PLUGINS: "true", - OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: "true", - OPENCODE_TEST_HOME: path.join(sandbox, "home"), + KILO_DISABLE_SHARE: process.env.KILO_DISABLE_SHARE ?? "true", + KILO_DISABLE_LSP_DOWNLOAD: "true", + KILO_DISABLE_DEFAULT_PLUGINS: "true", + KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true", + KILO_TEST_HOME: path.join(sandbox, "home"), XDG_DATA_HOME: path.join(sandbox, "share"), XDG_CACHE_HOME: path.join(sandbox, "cache"), XDG_CONFIG_HOME: path.join(sandbox, "config"), @@ -72,16 +72,16 @@ const serverEnv = { OPENCODE_E2E_SESSION_TITLE: "E2E Session", OPENCODE_E2E_MESSAGE: "Seeded for UI e2e", OPENCODE_E2E_MODEL: process.env.OPENCODE_E2E_MODEL ?? "opencode/gpt-5-nano", - OPENCODE_CLIENT: "app", - OPENCODE_STRICT_CONFIG_DEPS: "true", + KILO_CLIENT: "app", + KILO_STRICT_CONFIG_DEPS: "true", } satisfies Record const runnerEnv = { ...serverEnv, PLAYWRIGHT_SERVER_HOST: "127.0.0.1", PLAYWRIGHT_SERVER_PORT: String(serverPort), - VITE_OPENCODE_SERVER_HOST: "127.0.0.1", - VITE_OPENCODE_SERVER_PORT: String(serverPort), + VITE_KILO_SERVER_HOST: "127.0.0.1", + VITE_KILO_SERVER_PORT: String(serverPort), PLAYWRIGHT_PORT: String(webPort), } satisfies Record @@ -146,7 +146,7 @@ try { Object.assign(process.env, serverEnv) process.env.AGENT = "1" process.env.OPENCODE = "1" - process.env.OPENCODE_PID = String(process.pid) + process.env.KILO_PID = String(process.pid) const log = await import("../../opencode/src/util/log") const install = await import("../../opencode/src/installation") diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 41225d02aa..376929e83a 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -1,4 +1,4 @@ -import type { ProviderAuthAuthorization, ProviderAuthMethod } from "@opencode-ai/sdk/v2/client" +import type { ProviderAuthAuthorization, ProviderAuthMethod } from "@kilocode/sdk/v2/client" import { Button } from "@opencode-ai/ui/button" import { useDialog } from "@opencode-ai/ui/context/dialog" import { Dialog } from "@opencode-ai/ui/dialog" diff --git a/packages/app/src/components/dialog-custom-provider.tsx b/packages/app/src/components/dialog-custom-provider.tsx index 53b66fb451..6f38651e6d 100644 --- a/packages/app/src/components/dialog-custom-provider.tsx +++ b/packages/app/src/components/dialog-custom-provider.tsx @@ -183,7 +183,7 @@ export function DialogCustomProvider(props: Props) {

{language.t("provider.custom.description.prefix")} - + {language.t("provider.custom.description.link")} {language.t("provider.custom.description.suffix")} diff --git a/packages/app/src/components/dialog-fork.tsx b/packages/app/src/components/dialog-fork.tsx index 9e1b896fa8..9214009e2a 100644 --- a/packages/app/src/components/dialog-fork.tsx +++ b/packages/app/src/components/dialog-fork.tsx @@ -8,7 +8,7 @@ import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { showToast } from "@opencode-ai/ui/toast" import { extractPromptFromParts } from "@/utils/prompt" -import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" +import type { TextPart as SDKTextPart } from "@kilocode/sdk/v2/client" import { base64Encode } from "@opencode-ai/util/encode" import { useLanguage } from "@/context/language" diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 930832fb65..ad3a28415e 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -18,7 +18,7 @@ import { type ParentProps, } from "solid-js" import { Dynamic } from "solid-js/web" -import type { FileNode } from "@opencode-ai/sdk/v2" +import type { FileNode } from "@kilocode/sdk/v2" const MAX_DEPTH = 128 diff --git a/packages/app/src/components/prompt-input/build-request-parts.ts b/packages/app/src/components/prompt-input/build-request-parts.ts index a1076e60ca..9f0a8defbe 100644 --- a/packages/app/src/components/prompt-input/build-request-parts.ts +++ b/packages/app/src/components/prompt-input/build-request-parts.ts @@ -1,5 +1,5 @@ import { getFilename } from "@opencode-ai/util/path" -import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client" +import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@kilocode/sdk/v2/client" import type { FileSelection } from "@/context/file" import { encodeFilePath } from "@/context/file/path" import type { AgentPart, FileAttachmentPart, ImageAttachmentPart, Prompt } from "@/context/prompt" diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index b0166c43a8..4c9bcfafe5 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -63,8 +63,8 @@ beforeAll(async () => { useParams: () => params, })) - mock.module("@opencode-ai/sdk/v2/client", () => ({ - createOpencodeClient: (input: { directory: string }) => { + mock.module("@kilocode/sdk/v2/client", () => ({ + createKiloClient: (input: { directory: string }) => { createdClients.push(input.directory) return clientFor(input.directory) }, diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index 06b6c1e351..a749f0d47d 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -1,4 +1,4 @@ -import type { Message, Session } from "@opencode-ai/sdk/v2/client" +import type { Message, Session } from "@kilocode/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" import { base64Encode } from "@opencode-ai/util/encode" import { Binary } from "@opencode-ai/util/binary" diff --git a/packages/app/src/components/session/session-context-breakdown.test.ts b/packages/app/src/components/session/session-context-breakdown.test.ts index f38aecb55d..36c4384345 100644 --- a/packages/app/src/components/session/session-context-breakdown.test.ts +++ b/packages/app/src/components/session/session-context-breakdown.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { Message, Part } from "@opencode-ai/sdk/v2/client" +import type { Message, Part } from "@kilocode/sdk/v2/client" import { estimateSessionContextBreakdown } from "./session-context-breakdown" const user = (id: string) => { diff --git a/packages/app/src/components/session/session-context-breakdown.ts b/packages/app/src/components/session/session-context-breakdown.ts index e263b2957b..1b11fadcce 100644 --- a/packages/app/src/components/session/session-context-breakdown.ts +++ b/packages/app/src/components/session/session-context-breakdown.ts @@ -1,4 +1,4 @@ -import type { Message, Part } from "@opencode-ai/sdk/v2/client" +import type { Message, Part } from "@kilocode/sdk/v2/client" export type SessionContextBreakdownKey = "system" | "user" | "assistant" | "tool" | "other" diff --git a/packages/app/src/components/session/session-context-metrics.test.ts b/packages/app/src/components/session/session-context-metrics.test.ts index 0e109a71bd..be1bbca258 100644 --- a/packages/app/src/components/session/session-context-metrics.test.ts +++ b/packages/app/src/components/session/session-context-metrics.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { Message } from "@opencode-ai/sdk/v2/client" +import type { Message } from "@kilocode/sdk/v2/client" import { getSessionContextMetrics } from "./session-context-metrics" const assistant = ( diff --git a/packages/app/src/components/session/session-context-metrics.ts b/packages/app/src/components/session/session-context-metrics.ts index 0789b05f17..4bdcff52eb 100644 --- a/packages/app/src/components/session/session-context-metrics.ts +++ b/packages/app/src/components/session/session-context-metrics.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage, Message } from "@opencode-ai/sdk/v2/client" +import type { AssistantMessage, Message } from "@kilocode/sdk/v2/client" type Provider = { id: string diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index 4e7dc8e783..c4cca9cf66 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -10,7 +10,7 @@ import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header" import { File } from "@opencode-ai/ui/file" import { Markdown } from "@opencode-ai/ui/markdown" import { ScrollView } from "@opencode-ai/ui/scroll-view" -import type { Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client" +import type { Message, Part, UserMessage } from "@kilocode/sdk/v2/client" import { useLanguage } from "@/context/language" import { useProviders } from "@/hooks/use-providers" import { useSessionLayout } from "@/pages/session/session-layout" diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index 8a4d498866..7af6961fb2 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -273,7 +273,7 @@ export const SettingsGeneral: Component = () => { description={ <> {language.t("settings.general.row.theme.description")}{" "} - {language.t("common.learnMore")} + {language.t("common.learnMore")} } > diff --git a/packages/app/src/context/file/content-cache.ts b/packages/app/src/context/file/content-cache.ts index 4b72406883..98de196ab6 100644 --- a/packages/app/src/context/file/content-cache.ts +++ b/packages/app/src/context/file/content-cache.ts @@ -1,4 +1,4 @@ -import type { FileContent } from "@opencode-ai/sdk/v2" +import type { FileContent } from "@kilocode/sdk/v2" const MAX_FILE_CONTENT_ENTRIES = 40 const MAX_FILE_CONTENT_BYTES = 20 * 1024 * 1024 diff --git a/packages/app/src/context/file/tree-store.ts b/packages/app/src/context/file/tree-store.ts index a86051d286..dc4e58f7a2 100644 --- a/packages/app/src/context/file/tree-store.ts +++ b/packages/app/src/context/file/tree-store.ts @@ -1,5 +1,5 @@ import { createStore, produce, reconcile } from "solid-js/store" -import type { FileNode } from "@opencode-ai/sdk/v2" +import type { FileNode } from "@kilocode/sdk/v2" type DirectoryState = { expanded: boolean diff --git a/packages/app/src/context/file/types.ts b/packages/app/src/context/file/types.ts index 7ce8a37c25..ee1cc49f31 100644 --- a/packages/app/src/context/file/types.ts +++ b/packages/app/src/context/file/types.ts @@ -1,4 +1,4 @@ -import type { FileContent } from "@opencode-ai/sdk/v2" +import type { FileContent } from "@kilocode/sdk/v2" export type FileSelection = { startLine: number diff --git a/packages/app/src/context/file/watcher.ts b/packages/app/src/context/file/watcher.ts index fbf7199279..57bc68edc9 100644 --- a/packages/app/src/context/file/watcher.ts +++ b/packages/app/src/context/file/watcher.ts @@ -1,4 +1,4 @@ -import type { FileNode } from "@opencode-ai/sdk/v2" +import type { FileNode } from "@kilocode/sdk/v2" type WatcherEvent = { type: string diff --git a/packages/app/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx index 1205a8fa82..fcf435b677 100644 --- a/packages/app/src/context/global-sdk.tsx +++ b/packages/app/src/context/global-sdk.tsx @@ -1,4 +1,4 @@ -import type { Event } from "@opencode-ai/sdk/v2/client" +import type { Event } from "@kilocode/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { makeEventListener } from "@solid-primitives/event-listener" diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 0cf3570a8b..b28554be82 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -1,12 +1,12 @@ import type { Config, - OpencodeClient, + KiloClient, Path, Project, ProviderAuthResponse, ProviderListResponse, Todo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" import { getFilename } from "@opencode-ai/util/path" import { createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js" @@ -47,7 +47,7 @@ function createGlobalSync() { const owner = getOwner() if (!owner) throw new Error("GlobalSync must be created within owner") - const sdkCache = new Map() + const sdkCache = new Map() const booting = new Map>() const sessionLoads = new Map>() const sessionMeta = new Map() diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts index 7edd5a1ce1..551737bd6f 100644 --- a/packages/app/src/context/global-sync/bootstrap.ts +++ b/packages/app/src/context/global-sync/bootstrap.ts @@ -1,6 +1,6 @@ import type { Config, - OpencodeClient, + KiloClient, Path, PermissionRequest, Project, @@ -9,7 +9,7 @@ import type { QuestionRequest, Session, Todo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" import { getFilename } from "@opencode-ai/util/path" import { retry } from "@opencode-ai/util/retry" @@ -82,7 +82,7 @@ function showErrors(input: { } export async function bootstrapGlobal(input: { - globalSDK: OpencodeClient + globalSDK: KiloClient requestFailedTitle: string translate: (key: string, vars?: Record) => string formatMoreCount: (count: number) => string @@ -171,7 +171,7 @@ function warmSessions(input: { ids: string[] store: Store setStore: SetStoreFunction - sdk: OpencodeClient + sdk: KiloClient }) { const known = new Set(input.store.session.map((item) => item.id)) const ids = [...new Set(input.ids)].filter((id) => !!id && !known.has(id)) @@ -189,7 +189,7 @@ function warmSessions(input: { export async function bootstrapDirectory(input: { directory: string - sdk: OpencodeClient + sdk: KiloClient store: Store setStore: SetStoreFunction vcsCache: VcsCache diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 5678491f89..b31e1927e8 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -1,7 +1,7 @@ import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js" import { createStore, type SetStoreFunction, type Store } from "solid-js/store" import { Persist, persisted } from "@/utils/persist" -import type { VcsInfo } from "@opencode-ai/sdk/v2/client" +import type { VcsInfo } from "@kilocode/sdk/v2/client" import { DIR_IDLE_TTL_MS, MAX_DIR_STORES, diff --git a/packages/app/src/context/global-sync/event-reducer.test.ts b/packages/app/src/context/global-sync/event-reducer.test.ts index 892129788e..f66e7ede31 100644 --- a/packages/app/src/context/global-sync/event-reducer.test.ts +++ b/packages/app/src/context/global-sync/event-reducer.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { Message, Part, PermissionRequest, Project, QuestionRequest, Session } from "@opencode-ai/sdk/v2/client" +import type { Message, Part, PermissionRequest, Project, QuestionRequest, Session } from "@kilocode/sdk/v2/client" import { createStore } from "solid-js/store" import type { State } from "./types" import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./event-reducer" diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 4af6365535..b0ebbc6f56 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -10,7 +10,7 @@ import type { Session, SessionStatus, Todo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" import type { State, VcsCache } from "./types" import { trimSessions } from "./session-trim" import { dropSessionCaches } from "./session-cache" diff --git a/packages/app/src/context/global-sync/session-cache.test.ts b/packages/app/src/context/global-sync/session-cache.test.ts index 8e11110e3d..e58a2e61ed 100644 --- a/packages/app/src/context/global-sync/session-cache.test.ts +++ b/packages/app/src/context/global-sync/session-cache.test.ts @@ -7,7 +7,7 @@ import type { QuestionRequest, SessionStatus, Todo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" import { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache" const msg = (id: string, sessionID: string) => diff --git a/packages/app/src/context/global-sync/session-cache.ts b/packages/app/src/context/global-sync/session-cache.ts index 0177ebbe13..2ffce9cfe4 100644 --- a/packages/app/src/context/global-sync/session-cache.ts +++ b/packages/app/src/context/global-sync/session-cache.ts @@ -6,7 +6,7 @@ import type { QuestionRequest, SessionStatus, Todo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" export const SESSION_CACHE_LIMIT = 40 diff --git a/packages/app/src/context/global-sync/session-trim.test.ts b/packages/app/src/context/global-sync/session-trim.test.ts index be12c074b5..da9ef3498f 100644 --- a/packages/app/src/context/global-sync/session-trim.test.ts +++ b/packages/app/src/context/global-sync/session-trim.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" +import type { PermissionRequest, Session } from "@kilocode/sdk/v2/client" import { trimSessions } from "./session-trim" const session = (input: { id: string; parentID?: string; created: number; updated?: number; archived?: number }) => diff --git a/packages/app/src/context/global-sync/session-trim.ts b/packages/app/src/context/global-sync/session-trim.ts index 800ba74a68..8aa3140021 100644 --- a/packages/app/src/context/global-sync/session-trim.ts +++ b/packages/app/src/context/global-sync/session-trim.ts @@ -1,4 +1,4 @@ -import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" +import type { PermissionRequest, Session } from "@kilocode/sdk/v2/client" import { cmp } from "./utils" import { SESSION_RECENT_LIMIT, SESSION_RECENT_WINDOW } from "./types" diff --git a/packages/app/src/context/global-sync/types.ts b/packages/app/src/context/global-sync/types.ts index 1d6e550f8e..6f91fa0941 100644 --- a/packages/app/src/context/global-sync/types.ts +++ b/packages/app/src/context/global-sync/types.ts @@ -16,7 +16,7 @@ import type { SessionStatus, Todo, VcsInfo, -} from "@opencode-ai/sdk/v2/client" +} from "@kilocode/sdk/v2/client" import type { Accessor } from "solid-js" import type { SetStoreFunction, Store } from "solid-js/store" diff --git a/packages/app/src/context/global-sync/utils.test.ts b/packages/app/src/context/global-sync/utils.test.ts index 6d44ac9a89..24f1547c27 100644 --- a/packages/app/src/context/global-sync/utils.test.ts +++ b/packages/app/src/context/global-sync/utils.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { Agent } from "@opencode-ai/sdk/v2/client" +import type { Agent } from "@kilocode/sdk/v2/client" import { normalizeAgentList } from "./utils" const agent = (name = "build") => diff --git a/packages/app/src/context/global-sync/utils.ts b/packages/app/src/context/global-sync/utils.ts index cac58f3174..e6fd6f64ba 100644 --- a/packages/app/src/context/global-sync/utils.ts +++ b/packages/app/src/context/global-sync/utils.ts @@ -1,4 +1,4 @@ -import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client" +import type { Agent, Project, ProviderListResponse } from "@kilocode/sdk/v2/client" export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0) diff --git a/packages/app/src/context/highlights.tsx b/packages/app/src/context/highlights.tsx index 058f7cc4b6..11ee613e64 100644 --- a/packages/app/src/context/highlights.tsx +++ b/packages/app/src/context/highlights.tsx @@ -7,7 +7,7 @@ import { useSettings } from "@/context/settings" import { persisted } from "@/utils/persist" import { DialogReleaseNotes, type Highlight } from "@/components/dialog-release-notes" -const CHANGELOG_URL = "https://opencode.ai/changelog.json" +const CHANGELOG_URL = "https://kilo.ai/changelog.json" type Store = { version?: string diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index bab3d39f38..1a8a1531a6 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -6,7 +6,7 @@ import { useGlobalSync } from "./global-sync" import { useGlobalSDK } from "./global-sdk" import { useServer } from "./server" import { usePlatform } from "./platform" -import { Project } from "@opencode-ai/sdk/v2" +import { Project } from "@kilocode/sdk/v2" import { Persist, persisted, removePersisted } from "@/utils/persist" import { decode64 } from "@/utils/base64" import { same } from "@/utils/same" diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx index 281a1ef33d..ee06e394f1 100644 --- a/packages/app/src/context/notification.tsx +++ b/packages/app/src/context/notification.tsx @@ -10,7 +10,7 @@ import { useSettings } from "@/context/settings" import { Binary } from "@opencode-ai/util/binary" import { base64Encode } from "@opencode-ai/util/encode" import { decode64 } from "@/utils/base64" -import { EventSessionError } from "@opencode-ai/sdk/v2" +import { EventSessionError } from "@kilocode/sdk/v2" import { Persist, persisted } from "@/utils/persist" import { playSoundById } from "@/utils/sound" diff --git a/packages/app/src/context/permission-auto-respond.test.ts b/packages/app/src/context/permission-auto-respond.test.ts index 7556113005..547de1c39a 100644 --- a/packages/app/src/context/permission-auto-respond.test.ts +++ b/packages/app/src/context/permission-auto-respond.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" +import type { PermissionRequest, Session } from "@kilocode/sdk/v2/client" import { base64Encode } from "@opencode-ai/util/encode" import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond" diff --git a/packages/app/src/context/permission.tsx b/packages/app/src/context/permission.tsx index 672f84f82a..6f890da7c9 100644 --- a/packages/app/src/context/permission.tsx +++ b/packages/app/src/context/permission.tsx @@ -1,7 +1,7 @@ import { createEffect, createMemo, onCleanup } from "solid-js" import { createStore, produce } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" -import type { PermissionRequest } from "@opencode-ai/sdk/v2/client" +import type { PermissionRequest } from "@kilocode/sdk/v2/client" import { Persist, persisted } from "@/utils/persist" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "./global-sync" diff --git a/packages/app/src/context/sdk.tsx b/packages/app/src/context/sdk.tsx index bc97ea13ac..2eaf4627c0 100644 --- a/packages/app/src/context/sdk.tsx +++ b/packages/app/src/context/sdk.tsx @@ -1,4 +1,4 @@ -import type { Event } from "@opencode-ai/sdk/v2/client" +import type { Event } from "@kilocode/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { type Accessor, createEffect, createMemo, onCleanup } from "solid-js" diff --git a/packages/app/src/context/sync-optimistic.test.ts b/packages/app/src/context/sync-optimistic.test.ts index 94324f8a06..3c1b8f4644 100644 --- a/packages/app/src/context/sync-optimistic.test.ts +++ b/packages/app/src/context/sync-optimistic.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { Message, Part } from "@opencode-ai/sdk/v2/client" +import type { Message, Part } from "@kilocode/sdk/v2/client" import { applyOptimisticAdd, applyOptimisticRemove, mergeOptimisticPage } from "./sync" type Text = Extract diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index bbf4fc5ec4..d4eb6e6e64 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -11,7 +11,7 @@ import { } from "./global-sync/session-prefetch" import { useGlobalSync } from "./global-sync" import { useSDK } from "./sdk" -import type { Message, Part } from "@opencode-ai/sdk/v2/client" +import type { Message, Part } from "@kilocode/sdk/v2/client" import { SESSION_CACHE_LIMIT, dropSessionCaches, pickSessionCacheEvictions } from "./global-sync/session-cache" const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"]) diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx index b5cbed6e75..84b2b799da 100644 --- a/packages/app/src/entry.tsx +++ b/packages/app/src/entry.tsx @@ -100,7 +100,7 @@ if (!(root instanceof HTMLElement) && import.meta.env.DEV) { const getCurrentUrl = () => { if (location.hostname.includes("opencode.ai")) return "http://localhost:4096" if (import.meta.env.DEV) - return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` + return `http://${import.meta.env.VITE_KILO_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_KILO_SERVER_PORT ?? "4096"}` return location.origin } diff --git a/packages/app/src/env.d.ts b/packages/app/src/env.d.ts index 89721f34f2..8d5d1b15f6 100644 --- a/packages/app/src/env.d.ts +++ b/packages/app/src/env.d.ts @@ -1,8 +1,8 @@ import "solid-js" interface ImportMetaEnv { - readonly VITE_OPENCODE_SERVER_HOST: string - readonly VITE_OPENCODE_SERVER_PORT: string + readonly VITE_KILO_SERVER_HOST: string + readonly VITE_KILO_SERVER_PORT: string } interface ImportMeta { diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 6c3f3bb55e..6b59d4ed94 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -105,7 +105,7 @@ export const dict = { "dialog.model.manage": "إدارة النماذج", "dialog.model.manage.description": "تخصيص النماذج التي تظهر في محدد النماذج.", "dialog.model.manage.provider.toggle": "تبديل جميع نماذج {{provider}}", - "dialog.model.unpaid.freeModels.title": "نماذج مجانية مقدمة من OpenCode", + "dialog.model.unpaid.freeModels.title": "نماذج مجانية مقدمة من Kilo", "dialog.model.unpaid.addMore.title": "إضافة المزيد من النماذج من موفرين مشهورين", "dialog.provider.viewAll": "عرض المزيد من الموفرين", "provider.connect.title": "اتصال {{provider}}", @@ -116,7 +116,7 @@ export const dict = { "provider.connect.status.waiting": "في انتظار التفويض...", "provider.connect.status.failed": "فشل التفويض: {{error}}", "provider.connect.apiKey.description": - "أدخل مفتاح واجهة برمجة تطبيقات {{provider}} الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.", + "أدخل مفتاح واجهة برمجة تطبيقات {{provider}} الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", "provider.connect.apiKey.label": "مفتاح واجهة برمجة تطبيقات {{provider}}", "provider.connect.apiKey.placeholder": "مفتاح API", "provider.connect.apiKey.required": "مفتاح API مطلوب", @@ -130,7 +130,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "قم بزيارة ", "provider.connect.oauth.code.visit.link": "هذا الرابط", "provider.connect.oauth.code.visit.suffix": - " للحصول على رمز التفويض الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.", + " للحصول على رمز التفويض الخاص بك لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", "provider.connect.oauth.code.label": "رمز تفويض {{method}}", "provider.connect.oauth.code.placeholder": "رمز التفويض", "provider.connect.oauth.code.required": "رمز التفويض مطلوب", @@ -138,7 +138,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "قم بزيارة ", "provider.connect.oauth.auto.visit.link": "هذا الرابط", "provider.connect.oauth.auto.visit.suffix": - " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في OpenCode.", + " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.", "provider.connect.oauth.auto.confirmationCode": "رمز التأكيد", "provider.connect.toast.connected.title": "تم توصيل {{provider}}", "provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.", @@ -281,7 +281,7 @@ export const dict = { "dialog.directory.search.placeholder": "البحث في المجلدات", "dialog.directory.empty": "لم يتم العثور على مجلدات", "dialog.server.title": "الخوادم", - "dialog.server.description": "تبديل خادم OpenCode الذي يتصل به هذا التطبيق.", + "dialog.server.description": "تبديل خادم Kilo الذي يتصل به هذا التطبيق.", "dialog.server.search.placeholder": "البحث في الخوادم", "dialog.server.empty": "لا توجد خوادم بعد", "dialog.server.add.title": "إضافة خادم", @@ -395,7 +395,7 @@ export const dict = { "toast.session.unshare.failed.description": "حدث خطأ أثناء إلغاء مشاركة الجلسة", "toast.session.listFailed.title": "فشل تحميل الجلسات لـ {{project}}", "toast.update.title": "تحديث متاح", - "toast.update.description": "نسخة جديدة من OpenCode ({{version}}) متاحة الآن للتثبيت.", + "toast.update.description": "نسخة جديدة من Kilo ({{version}}) متاحة الآن للتثبيت.", "toast.update.action.installRestart": "تثبيت وإعادة تشغيل", "toast.update.action.notYet": "ليس الآن", "error.page.title": "حدث خطأ ما", @@ -405,7 +405,7 @@ export const dict = { "error.page.action.checking": "جارٍ التحقق...", "error.page.action.checkUpdates": "التحقق من وجود تحديثات", "error.page.action.updateTo": "تحديث إلى {{version}}", - "error.page.report.prefix": "يرجى الإبلاغ عن هذا الخطأ لفريق OpenCode", + "error.page.report.prefix": "يرجى الإبلاغ عن هذا الخطأ لفريق Kilo", "error.page.report.discord": "على Discord", "error.page.version": "الإصدار: {{version}}", "error.dev.rootNotFound": @@ -421,7 +421,7 @@ export const dict = { "error.chain.didYouMean": "هل كنت تعني: {{suggestions}}", "error.chain.modelNotFound": "النموذج غير موجود: {{provider}}/{{model}}", "error.chain.checkConfig": "تحقق من أسماء الموفر/النموذج في التكوين (opencode.json)", - "error.chain.mcpFailed": 'فشل خادم MCP "{{name}}". لاحظ أن OpenCode لا يدعم مصادقة MCP بعد.', + "error.chain.mcpFailed": 'فشل خادم MCP "{{name}}". لاحظ أن Kilo لا يدعم مصادقة MCP بعد.', "error.chain.providerAuthFailed": "فشلت مصادقة الموفر ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'فشل تهيئة الموفر "{{provider}}". تحقق من بيانات الاعتماد والتكوين.', "error.chain.configJsonInvalid": "ملف التكوين في {{path}} ليس JSON(C) صالحًا", @@ -537,19 +537,19 @@ export const dict = { "sidebar.workspaces.enable": "تمكين مساحات العمل", "sidebar.workspaces.disable": "تعطيل مساحات العمل", "sidebar.gettingStarted.title": "البدء", - "sidebar.gettingStarted.line1": "يتضمن OpenCode نماذج مجانية حتى تتمكن من البدء فورًا.", + "sidebar.gettingStarted.line1": "يتضمن Kilo نماذج مجانية حتى تتمكن من البدء فورًا.", "sidebar.gettingStarted.line2": "قم بتوصيل أي موفر لاستخدام النماذج، بما في ذلك Claude و GPT و Gemini وما إلى ذلك.", "sidebar.project.recentSessions": "الجلسات الحديثة", "sidebar.project.viewAllSessions": "عرض جميع الجلسات", "sidebar.project.clearNotifications": "مسح الإشعارات", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "سطح المكتب", "settings.section.server": "الخادم", "settings.tab.general": "عام", "settings.tab.shortcuts": "اختصارات", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "تكامل WSL", - "settings.desktop.wsl.description": "تشغيل خادم OpenCode داخل WSL على Windows.", + "settings.desktop.wsl.description": "تشغيل خادم Kilo داخل WSL على Windows.", "settings.general.section.appearance": "المظهر", "settings.general.section.notifications": "إشعارات النظام", "settings.general.section.updates": "التحديثات", @@ -557,13 +557,13 @@ export const dict = { "settings.general.section.feed": "الخلاصة", "settings.general.section.display": "شاشة العرض", "settings.general.row.language.title": "اللغة", - "settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode", + "settings.general.row.language.description": "تغيير لغة العرض لـ Kilo", "settings.general.row.appearance.title": "المظهر", - "settings.general.row.appearance.description": "تخصيص كيفية ظهور OpenCode على جهازك", + "settings.general.row.appearance.description": "تخصيص كيفية ظهور Kilo على جهازك", "settings.general.row.colorScheme.title": "مخطط الألوان", - "settings.general.row.colorScheme.description": "اختر ما إذا كان OpenCode يتبع سمة النظام أو الفاتح أو الداكن", + "settings.general.row.colorScheme.description": "اختر ما إذا كان Kilo يتبع سمة النظام أو الفاتح أو الداكن", "settings.general.row.theme.title": "السمة", - "settings.general.row.theme.description": "تخصيص سمة OpenCode.", + "settings.general.row.theme.description": "تخصيص سمة Kilo.", "settings.general.row.font.title": "خط الكود", "settings.general.row.font.description": "خصّص الخط المستخدم في كتل التعليمات البرمجية والطرفيات", "settings.general.row.uiFont.title": "خط الواجهة", @@ -587,13 +587,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "ملاحظات الإصدار", "settings.general.row.releaseNotes.description": 'عرض نوافذ "ما الجديد" المنبثقة بعد التحديثات', "settings.updates.row.startup.title": "التحقق من التحديثات عند بدء التشغيل", - "settings.updates.row.startup.description": "التحقق تلقائيًا من التحديثات عند تشغيل OpenCode", + "settings.updates.row.startup.description": "التحقق تلقائيًا من التحديثات عند تشغيل Kilo", "settings.updates.row.check.title": "التحقق من التحديثات", "settings.updates.row.check.description": "التحقق يدويًا من التحديثات وتثبيتها إذا كانت متاحة", "settings.updates.action.checkNow": "تحقق الآن", "settings.updates.action.checking": "جارٍ التحقق...", "settings.updates.toast.latest.title": "أنت على آخر إصدار", - "settings.updates.toast.latest.description": "أنت تستخدم أحدث إصدار من OpenCode.", + "settings.updates.toast.latest.description": "أنت تستخدم أحدث إصدار من Kilo.", "sound.option.none": "بلا", "sound.option.alert01": "تنبيه 01", "sound.option.alert02": "تنبيه 02", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 63880462a4..217a017e60 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -105,7 +105,7 @@ export const dict = { "dialog.model.manage": "Gerenciar modelos", "dialog.model.manage.description": "Personalizar quais modelos aparecem no seletor de modelos.", "dialog.model.manage.provider.toggle": "Alternar todos os modelos {{provider}}", - "dialog.model.unpaid.freeModels.title": "Modelos gratuitos fornecidos pelo OpenCode", + "dialog.model.unpaid.freeModels.title": "Modelos gratuitos fornecidos pelo Kilo", "dialog.model.unpaid.addMore.title": "Adicionar mais modelos de provedores populares", "dialog.provider.viewAll": "Ver mais provedores", "provider.connect.title": "Conectar {{provider}}", @@ -116,7 +116,7 @@ export const dict = { "provider.connect.status.waiting": "Aguardando autorização...", "provider.connect.status.failed": "Autorização falhou: {{error}}", "provider.connect.apiKey.description": - "Digite sua chave de API do {{provider}} para conectar sua conta e usar modelos do {{provider}} no OpenCode.", + "Digite sua chave de API do {{provider}} para conectar sua conta e usar modelos do {{provider}} no Kilo.", "provider.connect.apiKey.label": "Chave de API do {{provider}}", "provider.connect.apiKey.placeholder": "Chave de API", "provider.connect.apiKey.required": "A chave de API é obrigatória", @@ -130,7 +130,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Visite ", "provider.connect.oauth.code.visit.link": "este link", "provider.connect.oauth.code.visit.suffix": - " para obter seu código de autorização e conectar sua conta para usar modelos do {{provider}} no OpenCode.", + " para obter seu código de autorização e conectar sua conta para usar modelos do {{provider}} no Kilo.", "provider.connect.oauth.code.label": "Código de autorização {{method}}", "provider.connect.oauth.code.placeholder": "Código de autorização", "provider.connect.oauth.code.required": "O código de autorização é obrigatório", @@ -138,7 +138,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Visite ", "provider.connect.oauth.auto.visit.link": "este link", "provider.connect.oauth.auto.visit.suffix": - " e digite o código abaixo para conectar sua conta e usar modelos do {{provider}} no OpenCode.", + " e digite o código abaixo para conectar sua conta e usar modelos do {{provider}} no Kilo.", "provider.connect.oauth.auto.confirmationCode": "Código de confirmação", "provider.connect.toast.connected.title": "{{provider}} conectado", "provider.connect.toast.connected.description": "Modelos do {{provider}} agora estão disponíveis para uso.", @@ -281,7 +281,7 @@ export const dict = { "dialog.directory.search.placeholder": "Buscar pastas", "dialog.directory.empty": "Nenhuma pasta encontrada", "dialog.server.title": "Servidores", - "dialog.server.description": "Trocar para qual servidor OpenCode este aplicativo se conecta.", + "dialog.server.description": "Trocar para qual servidor Kilo este aplicativo se conecta.", "dialog.server.search.placeholder": "Buscar servidores", "dialog.server.empty": "Nenhum servidor ainda", "dialog.server.add.title": "Adicionar um servidor", @@ -396,7 +396,7 @@ export const dict = { "toast.session.unshare.failed.description": "Ocorreu um erro ao parar de compartilhar a sessão", "toast.session.listFailed.title": "Falha ao carregar sessões para {{project}}", "toast.update.title": "Atualização disponível", - "toast.update.description": "Uma nova versão do OpenCode ({{version}}) está disponível para instalação.", + "toast.update.description": "Uma nova versão do Kilo ({{version}}) está disponível para instalação.", "toast.update.action.installRestart": "Instalar e reiniciar", "toast.update.action.notYet": "Agora não", "error.page.title": "Algo deu errado", @@ -406,7 +406,7 @@ export const dict = { "error.page.action.checking": "Verificando...", "error.page.action.checkUpdates": "Verificar atualizações", "error.page.action.updateTo": "Atualizar para {{version}}", - "error.page.report.prefix": "Por favor, reporte este erro para a equipe do OpenCode", + "error.page.report.prefix": "Por favor, reporte este erro para a equipe do Kilo", "error.page.report.discord": "no Discord", "error.page.version": "Versão: {{version}}", "error.dev.rootNotFound": @@ -422,7 +422,7 @@ export const dict = { "error.chain.didYouMean": "Você quis dizer: {{suggestions}}", "error.chain.modelNotFound": "Modelo não encontrado: {{provider}}/{{model}}", "error.chain.checkConfig": "Verifique os nomes de provedor/modelo na sua configuração (opencode.json)", - "error.chain.mcpFailed": 'Servidor MCP "{{name}}" falhou. Nota: OpenCode ainda não suporta autenticação MCP.', + "error.chain.mcpFailed": 'Servidor MCP "{{name}}" falhou. Nota: Kilo ainda não suporta autenticação MCP.', "error.chain.providerAuthFailed": "Autenticação do provedor falhou ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Falha ao inicializar provedor "{{provider}}". Verifique credenciais e configuração.', @@ -544,19 +544,19 @@ export const dict = { "sidebar.workspaces.enable": "Habilitar espaços de trabalho", "sidebar.workspaces.disable": "Desabilitar espaços de trabalho", "sidebar.gettingStarted.title": "Começando", - "sidebar.gettingStarted.line1": "OpenCode inclui modelos gratuitos para você começar imediatamente.", + "sidebar.gettingStarted.line1": "Kilo inclui modelos gratuitos para você começar imediatamente.", "sidebar.gettingStarted.line2": "Conecte qualquer provedor para usar modelos, incluindo Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Sessões recentes", "sidebar.project.viewAllSessions": "Ver todas as sessões", "sidebar.project.clearNotifications": "Limpar notificações", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Servidor", "settings.tab.general": "Geral", "settings.tab.shortcuts": "Atalhos", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL integration", - "settings.desktop.wsl.description": "Executar o servidor OpenCode dentro do WSL no Windows.", + "settings.desktop.wsl.description": "Executar o servidor Kilo dentro do WSL no Windows.", "settings.general.section.appearance": "Aparência", "settings.general.section.notifications": "Notificações do sistema", "settings.general.section.updates": "Atualizações", @@ -564,13 +564,13 @@ export const dict = { "settings.general.section.feed": "Feed", "settings.general.section.display": "Tela", "settings.general.row.language.title": "Idioma", - "settings.general.row.language.description": "Alterar o idioma de exibição do OpenCode", + "settings.general.row.language.description": "Alterar o idioma de exibição do Kilo", "settings.general.row.appearance.title": "Aparência", - "settings.general.row.appearance.description": "Personalize como o OpenCode aparece no seu dispositivo", + "settings.general.row.appearance.description": "Personalize como o Kilo aparece no seu dispositivo", "settings.general.row.colorScheme.title": "Esquema de cores", - "settings.general.row.colorScheme.description": "Escolha se o OpenCode segue o tema do sistema, claro ou escuro", + "settings.general.row.colorScheme.description": "Escolha se o Kilo segue o tema do sistema, claro ou escuro", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.", + "settings.general.row.theme.description": "Personalize como o Kilo é tematizado.", "settings.general.row.font.title": "Fonte de código", "settings.general.row.font.description": "Personalize a fonte usada em blocos de código e terminais", "settings.general.row.uiFont.title": "Fonte da interface", @@ -595,13 +595,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "Notas da versão", "settings.general.row.releaseNotes.description": 'Mostrar pop-ups de "Novidades" após atualizações', "settings.updates.row.startup.title": "Verificar atualizações ao iniciar", - "settings.updates.row.startup.description": "Verificar atualizações automaticamente quando o OpenCode iniciar", + "settings.updates.row.startup.description": "Verificar atualizações automaticamente quando o Kilo iniciar", "settings.updates.row.check.title": "Verificar atualizações", "settings.updates.row.check.description": "Verificar atualizações manualmente e instalar se houver", "settings.updates.action.checkNow": "Verificar agora", "settings.updates.action.checking": "Verificando...", "settings.updates.toast.latest.title": "Você está atualizado", - "settings.updates.toast.latest.description": "Você está usando a versão mais recente do OpenCode.", + "settings.updates.toast.latest.description": "Você está usando a versão mais recente do Kilo.", "sound.option.none": "Nenhum", "sound.option.alert01": "Alerta 01", "sound.option.alert02": "Alerta 02", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 2b589eb35f..46b40269b0 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -115,7 +115,7 @@ export const dict = { "dialog.model.manage.description": "Prilagodi koji se modeli prikazuju u izborniku modela.", "dialog.model.manage.provider.toggle": "Uključi/isključi sve {{provider}} modele", - "dialog.model.unpaid.freeModels.title": "Besplatni modeli koje obezbjeđuje OpenCode", + "dialog.model.unpaid.freeModels.title": "Besplatni modeli koje obezbjeđuje Kilo", "dialog.model.unpaid.addMore.title": "Dodaj još modela od popularnih provajdera", "dialog.provider.viewAll": "Prikaži više provajdera", @@ -128,7 +128,7 @@ export const dict = { "provider.connect.status.waiting": "Čekanje na autorizaciju...", "provider.connect.status.failed": "Autorizacija nije uspjela: {{error}}", "provider.connect.apiKey.description": - "Unesi svoj {{provider}} API ključ da povežeš račun i koristiš {{provider}} modele u OpenCode-u.", + "Unesi svoj {{provider}} API ključ da povežeš račun i koristiš {{provider}} modele u Kilo-u.", "provider.connect.apiKey.label": "{{provider}} API ključ", "provider.connect.apiKey.placeholder": "API ključ", "provider.connect.apiKey.required": "API ključ je obavezan", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Posjeti ", "provider.connect.oauth.code.visit.link": "ovaj link", "provider.connect.oauth.code.visit.suffix": - " da preuzmeš autorizacijski kod i povežeš račun te koristiš {{provider}} modele u OpenCode-u.", + " da preuzmeš autorizacijski kod i povežeš račun te koristiš {{provider}} modele u Kilo-u.", "provider.connect.oauth.code.label": "{{method}} autorizacijski kod", "provider.connect.oauth.code.placeholder": "Autorizacijski kod", "provider.connect.oauth.code.required": "Autorizacijski kod je obavezan", @@ -150,7 +150,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Posjeti ", "provider.connect.oauth.auto.visit.link": "ovaj link", "provider.connect.oauth.auto.visit.suffix": - " i unesi kod ispod da povežeš račun i koristiš {{provider}} modele u OpenCode-u.", + " i unesi kod ispod da povežeš račun i koristiš {{provider}} modele u Kilo-u.", "provider.connect.oauth.auto.confirmationCode": "Kod za potvrdu", "provider.connect.toast.connected.title": "{{provider}} povezan", "provider.connect.toast.connected.description": "{{provider}} modeli su sada dostupni za korištenje.", @@ -308,7 +308,7 @@ export const dict = { "dialog.directory.empty": "Nema pronađenih foldera", "dialog.server.title": "Serveri", - "dialog.server.description": "Promijeni na koji se OpenCode server ova aplikacija povezuje.", + "dialog.server.description": "Promijeni na koji se Kilo server ova aplikacija povezuje.", "dialog.server.search.placeholder": "Pretraži servere", "dialog.server.empty": "Još nema servera", "dialog.server.add.title": "Dodaj server", @@ -441,7 +441,7 @@ export const dict = { "toast.session.listFailed.title": "Neuspjelo učitavanje sesija za {{project}}", "toast.update.title": "Dostupno ažuriranje", - "toast.update.description": "Nova verzija OpenCode-a ({{version}}) je dostupna za instalaciju.", + "toast.update.description": "Nova verzija Kilo-a ({{version}}) je dostupna za instalaciju.", "toast.update.action.installRestart": "Instaliraj i restartuj", "toast.update.action.notYet": "Ne još", @@ -452,7 +452,7 @@ export const dict = { "error.page.action.checking": "Provjera...", "error.page.action.checkUpdates": "Provjeri ažuriranja", "error.page.action.updateTo": "Ažuriraj na {{version}}", - "error.page.report.prefix": "Molimo prijavi ovu grešku OpenCode timu", + "error.page.report.prefix": "Molimo prijavi ovu grešku Kilo timu", "error.page.report.discord": "na Discordu", "error.page.version": "Verzija: {{version}}", @@ -471,7 +471,7 @@ export const dict = { "error.chain.didYouMean": "Da li si mislio: {{suggestions}}", "error.chain.modelNotFound": "Model nije pronađen: {{provider}}/{{model}}", "error.chain.checkConfig": "Provjeri konfiguraciju (opencode.json) provider/model names", - "error.chain.mcpFailed": 'MCP server "{{name}}" nije uspio. Napomena: OpenCode još ne podržava MCP autentifikaciju.', + "error.chain.mcpFailed": 'MCP server "{{name}}" nije uspio. Napomena: Kilo još ne podržava MCP autentifikaciju.', "error.chain.providerAuthFailed": "Autentifikacija provajdera nije uspjela ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Neuspjelo inicijalizovanje provajdera "{{provider}}". Provjeri kredencijale i konfiguraciju.', @@ -605,13 +605,13 @@ export const dict = { "sidebar.workspaces.enable": "Omogući radne prostore", "sidebar.workspaces.disable": "Onemogući radne prostore", "sidebar.gettingStarted.title": "Početak", - "sidebar.gettingStarted.line1": "OpenCode uključuje besplatne modele, tako da možeš odmah početi.", + "sidebar.gettingStarted.line1": "Kilo uključuje besplatne modele, tako da možeš odmah početi.", "sidebar.gettingStarted.line2": "Poveži bilo kojeg provajdera da koristiš modele, npr. Claude, GPT, Gemini itd.", "sidebar.project.recentSessions": "Nedavne sesije", "sidebar.project.viewAllSessions": "Prikaži sve sesije", "sidebar.project.clearNotifications": "Očisti obavijesti", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", @@ -619,7 +619,7 @@ export const dict = { "settings.tab.shortcuts": "Prečice", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL integracija", - "settings.desktop.wsl.description": "Pokreni OpenCode server unutar WSL-a na Windowsu.", + "settings.desktop.wsl.description": "Pokreni Kilo server unutar WSL-a na Windowsu.", "settings.general.section.appearance": "Izgled", "settings.general.section.notifications": "Sistemske obavijesti", @@ -629,13 +629,13 @@ export const dict = { "settings.general.section.display": "Prikaz", "settings.general.row.language.title": "Jezik", - "settings.general.row.language.description": "Promijeni jezik prikaza u OpenCode-u", + "settings.general.row.language.description": "Promijeni jezik prikaza u Kilo-u", "settings.general.row.appearance.title": "Izgled", - "settings.general.row.appearance.description": "Prilagodi kako OpenCode izgleda na tvom uređaju", + "settings.general.row.appearance.description": "Prilagodi kako Kilo izgleda na tvom uređaju", "settings.general.row.colorScheme.title": "Šema boja", - "settings.general.row.colorScheme.description": "Odaberi da li OpenCode prati sistemsku, svijetlu ili tamnu temu", + "settings.general.row.colorScheme.description": "Odaberi da li Kilo prati sistemsku, svijetlu ili tamnu temu", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "Prilagodi temu OpenCode-a.", + "settings.general.row.theme.description": "Prilagodi temu Kilo-a.", "settings.general.row.font.title": "Font za kod", "settings.general.row.font.description": "Prilagodi font koji se koristi u blokovima koda i terminalima", "settings.general.row.uiFont.title": "UI font", @@ -662,13 +662,13 @@ export const dict = { "settings.general.row.releaseNotes.description": 'Prikaži iskačuće prozore "Šta je novo" nakon ažuriranja', "settings.updates.row.startup.title": "Provjeri ažuriranja pri pokretanju", - "settings.updates.row.startup.description": "Automatski provjerava ažuriranja kada se OpenCode pokrene", + "settings.updates.row.startup.description": "Automatski provjerava ažuriranja kada se Kilo pokrene", "settings.updates.row.check.title": "Provjeri ažuriranja", "settings.updates.row.check.description": "Ručno provjeri ažuriranja i instaliraj ako su dostupna", "settings.updates.action.checkNow": "Provjeri sada", "settings.updates.action.checking": "Provjera...", "settings.updates.toast.latest.title": "Sve je ažurno", - "settings.updates.toast.latest.description": "Koristiš najnoviju verziju OpenCode-a.", + "settings.updates.toast.latest.description": "Koristiš najnoviju verziju Kilo-a.", "sound.option.none": "Nijedan", "sound.option.alert01": "Upozorenje 01", "sound.option.alert02": "Upozorenje 02", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index b096d87b4b..66b6ead579 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -115,7 +115,7 @@ export const dict = { "dialog.model.manage.description": "Tilpas hvilke modeller der vises i modelvælgeren.", "dialog.model.manage.provider.toggle": "Skift alle {{provider}}-modeller", - "dialog.model.unpaid.freeModels.title": "Gratis modeller leveret af OpenCode", + "dialog.model.unpaid.freeModels.title": "Gratis modeller leveret af Kilo", "dialog.model.unpaid.addMore.title": "Tilføj flere modeller fra populære udbydere", "dialog.provider.viewAll": "Vis flere udbydere", @@ -128,7 +128,7 @@ export const dict = { "provider.connect.status.waiting": "Venter på godkendelse...", "provider.connect.status.failed": "Godkendelse mislykkedes: {{error}}", "provider.connect.apiKey.description": - "Indtast din {{provider}} API-nøgle for at forbinde din konto og bruge {{provider}} modeller i OpenCode.", + "Indtast din {{provider}} API-nøgle for at forbinde din konto og bruge {{provider}} modeller i Kilo.", "provider.connect.apiKey.label": "{{provider}} API-nøgle", "provider.connect.apiKey.placeholder": "API-nøgle", "provider.connect.apiKey.required": "API-nøgle er påkrævet", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Besøg ", "provider.connect.oauth.code.visit.link": "dette link", "provider.connect.oauth.code.visit.suffix": - " for at hente din godkendelseskode for at forbinde din konto og bruge {{provider}} modeller i OpenCode.", + " for at hente din godkendelseskode for at forbinde din konto og bruge {{provider}} modeller i Kilo.", "provider.connect.oauth.code.label": "{{method}} godkendelseskode", "provider.connect.oauth.code.placeholder": "Godkendelseskode", "provider.connect.oauth.code.required": "Godkendelseskode er påkrævet", @@ -150,7 +150,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Besøg ", "provider.connect.oauth.auto.visit.link": "dette link", "provider.connect.oauth.auto.visit.suffix": - " og indtast koden nedenfor for at forbinde din konto og bruge {{provider}} modeller i OpenCode.", + " og indtast koden nedenfor for at forbinde din konto og bruge {{provider}} modeller i Kilo.", "provider.connect.oauth.auto.confirmationCode": "Bekræftelseskode", "provider.connect.toast.connected.title": "{{provider}} forbundet", "provider.connect.toast.connected.description": "{{provider}} modeller er nu tilgængelige.", @@ -306,7 +306,7 @@ export const dict = { "dialog.directory.empty": "Ingen mapper fundet", "dialog.server.title": "Servere", - "dialog.server.description": "Skift hvilken OpenCode-server denne app forbinder til.", + "dialog.server.description": "Skift hvilken Kilo-server denne app forbinder til.", "dialog.server.search.placeholder": "Søg servere", "dialog.server.empty": "Ingen servere endnu", "dialog.server.add.title": "Tilføj en server", @@ -438,7 +438,7 @@ export const dict = { "toast.session.listFailed.title": "Kunne ikke indlæse sessioner for {{project}}", "toast.update.title": "Opdatering tilgængelig", - "toast.update.description": "En ny version af OpenCode ({{version}}) er nu tilgængelig til installation.", + "toast.update.description": "En ny version af Kilo ({{version}}) er nu tilgængelig til installation.", "toast.update.action.installRestart": "Installer og genstart", "toast.update.action.notYet": "Ikke endnu", @@ -449,7 +449,7 @@ export const dict = { "error.page.action.checking": "Tjekker...", "error.page.action.checkUpdates": "Tjek for opdateringer", "error.page.action.updateTo": "Opdater til {{version}}", - "error.page.report.prefix": "Rapporter venligst denne fejl til OpenCode-teamet", + "error.page.report.prefix": "Rapporter venligst denne fejl til Kilo-teamet", "error.page.report.discord": "på Discord", "error.page.version": "Version: {{version}}", @@ -468,7 +468,7 @@ export const dict = { "error.chain.didYouMean": "Mente du: {{suggestions}}", "error.chain.modelNotFound": "Model ikke fundet: {{provider}}/{{model}}", "error.chain.checkConfig": "Tjek dine konfigurations (opencode.json) udbyder/modelnavne", - "error.chain.mcpFailed": 'MCP-server "{{name}}" fejlede. Bemærk, OpenCode understøtter ikke MCP-godkendelse endnu.', + "error.chain.mcpFailed": 'MCP-server "{{name}}" fejlede. Bemærk, Kilo understøtter ikke MCP-godkendelse endnu.', "error.chain.providerAuthFailed": "Udbydergodkendelse mislykkedes ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Kunne ikke initialisere udbyder "{{provider}}". Tjek legitimationsoplysninger og konfiguration.', @@ -601,20 +601,20 @@ export const dict = { "sidebar.workspaces.enable": "Aktiver arbejdsområder", "sidebar.workspaces.disable": "Deaktiver arbejdsområder", "sidebar.gettingStarted.title": "Kom i gang", - "sidebar.gettingStarted.line1": "OpenCode inkluderer gratis modeller så du kan starte med det samme.", + "sidebar.gettingStarted.line1": "Kilo inkluderer gratis modeller så du kan starte med det samme.", "sidebar.gettingStarted.line2": "Forbind enhver udbyder for at bruge modeller, inkl. Claude, GPT, Gemini osv.", "sidebar.project.recentSessions": "Seneste sessioner", "sidebar.project.viewAllSessions": "Vis alle sessioner", "sidebar.project.clearNotifications": "Ryd notifikationer", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", "settings.tab.general": "Generelt", "settings.tab.shortcuts": "Genveje", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL integration", - "settings.desktop.wsl.description": "Kør OpenCode-serveren inde i WSL på Windows.", + "settings.desktop.wsl.description": "Kør Kilo-serveren inde i WSL på Windows.", "settings.general.section.appearance": "Udseende", "settings.general.section.notifications": "Systemmeddelelser", @@ -624,13 +624,13 @@ export const dict = { "settings.general.section.display": "Skærm", "settings.general.row.language.title": "Sprog", - "settings.general.row.language.description": "Ændr visningssproget for OpenCode", + "settings.general.row.language.description": "Ændr visningssproget for Kilo", "settings.general.row.appearance.title": "Udseende", - "settings.general.row.appearance.description": "Tilpas hvordan OpenCode ser ud på din enhed", + "settings.general.row.appearance.description": "Tilpas hvordan Kilo ser ud på din enhed", "settings.general.row.colorScheme.title": "Farveskema", - "settings.general.row.colorScheme.description": "Vælg om OpenCode følger systemets, lyst eller mørkt tema", + "settings.general.row.colorScheme.description": "Vælg om Kilo følger systemets, lyst eller mørkt tema", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.", + "settings.general.row.theme.description": "Tilpas hvordan Kilo er temabestemt.", "settings.general.row.font.title": "Kode-skrifttype", "settings.general.row.font.description": "Tilpas skrifttypen, der bruges i kodeblokke og terminaler", "settings.general.row.uiFont.title": "UI-skrifttype", @@ -656,13 +656,13 @@ export const dict = { "settings.general.row.releaseNotes.description": 'Vis "Hvad er nyt"-popups efter opdateringer', "settings.updates.row.startup.title": "Tjek for opdateringer ved opstart", - "settings.updates.row.startup.description": "Tjek automatisk for opdateringer, når OpenCode starter", + "settings.updates.row.startup.description": "Tjek automatisk for opdateringer, når Kilo starter", "settings.updates.row.check.title": "Tjek for opdateringer", "settings.updates.row.check.description": "Tjek manuelt for opdateringer og installer, hvis tilgængelig", "settings.updates.action.checkNow": "Tjek nu", "settings.updates.action.checking": "Tjekker...", "settings.updates.toast.latest.title": "Du er opdateret", - "settings.updates.toast.latest.description": "Du kører den nyeste version af OpenCode.", + "settings.updates.toast.latest.description": "Du kører den nyeste version af Kilo.", "sound.option.none": "Ingen", "sound.option.alert01": "Alarm 01", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 6dc0b04972..694addfaee 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -109,7 +109,7 @@ export const dict = { "dialog.model.manage": "Modelle verwalten", "dialog.model.manage.description": "Anpassen, welche Modelle in der Modellauswahl erscheinen.", "dialog.model.manage.provider.toggle": "Alle {{provider}}-Modelle umschalten", - "dialog.model.unpaid.freeModels.title": "Kostenlose Modelle von OpenCode", + "dialog.model.unpaid.freeModels.title": "Kostenlose Modelle von Kilo", "dialog.model.unpaid.addMore.title": "Weitere Modelle von beliebten Anbietern hinzufügen", "dialog.provider.viewAll": "Mehr Anbieter anzeigen", "provider.connect.title": "{{provider}} verbinden", @@ -120,7 +120,7 @@ export const dict = { "provider.connect.status.waiting": "Warten auf Autorisierung...", "provider.connect.status.failed": "Autorisierung fehlgeschlagen: {{error}}", "provider.connect.apiKey.description": - "Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.", + "Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in Kilo zu nutzen.", "provider.connect.apiKey.label": "{{provider}} API-Schlüssel", "provider.connect.apiKey.placeholder": "API-Schlüssel", "provider.connect.apiKey.required": "API-Schlüssel ist erforderlich", @@ -134,7 +134,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Besuchen Sie ", "provider.connect.oauth.code.visit.link": "diesen Link", "provider.connect.oauth.code.visit.suffix": - ", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.", + ", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in Kilo zu nutzen.", "provider.connect.oauth.code.label": "{{method}} Autorisierungscode", "provider.connect.oauth.code.placeholder": "Autorisierungscode", "provider.connect.oauth.code.required": "Autorisierungscode ist erforderlich", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Besuchen Sie ", "provider.connect.oauth.auto.visit.link": "diesen Link", "provider.connect.oauth.auto.visit.suffix": - " und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.", + " und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in Kilo zu nutzen.", "provider.connect.oauth.auto.confirmationCode": "Bestätigungscode", "provider.connect.toast.connected.title": "{{provider}} verbunden", "provider.connect.toast.connected.description": "{{provider}} Modelle sind jetzt verfügbar.", @@ -287,7 +287,7 @@ export const dict = { "dialog.directory.search.placeholder": "Ordner durchsuchen", "dialog.directory.empty": "Keine Ordner gefunden", "dialog.server.title": "Server", - "dialog.server.description": "Wechseln Sie den OpenCode-Server, mit dem sich diese App verbindet.", + "dialog.server.description": "Wechseln Sie den Kilo-Server, mit dem sich diese App verbindet.", "dialog.server.search.placeholder": "Server durchsuchen", "dialog.server.empty": "Noch keine Server", "dialog.server.add.title": "Server hinzufügen", @@ -403,7 +403,7 @@ export const dict = { "toast.session.unshare.failed.description": "Beim Aufheben des Teilens ist ein Fehler aufgetreten", "toast.session.listFailed.title": "Sitzungen für {{project}} konnten nicht geladen werden", "toast.update.title": "Update verfügbar", - "toast.update.description": "Eine neue Version von OpenCode ({{version}}) ist zur Installation verfügbar.", + "toast.update.description": "Eine neue Version von Kilo ({{version}}) ist zur Installation verfügbar.", "toast.update.action.installRestart": "Installieren und neu starten", "toast.update.action.notYet": "Noch nicht", "error.page.title": "Etwas ist schiefgelaufen", @@ -413,7 +413,7 @@ export const dict = { "error.page.action.checking": "Prüfen...", "error.page.action.checkUpdates": "Nach Updates suchen", "error.page.action.updateTo": "Auf {{version}} aktualisieren", - "error.page.report.prefix": "Bitte melden Sie diesen Fehler dem OpenCode-Team", + "error.page.report.prefix": "Bitte melden Sie diesen Fehler dem Kilo-Team", "error.page.report.discord": "auf Discord", "error.page.version": "Version: {{version}}", "error.dev.rootNotFound": @@ -430,7 +430,7 @@ export const dict = { "error.chain.modelNotFound": "Modell nicht gefunden: {{provider}}/{{model}}", "error.chain.checkConfig": "Überprüfen Sie Ihre Konfiguration (opencode.json) auf Anbieter-/Modellnamen", "error.chain.mcpFailed": - 'MCP-Server "{{name}}" fehlgeschlagen. Hinweis: OpenCode unterstützt noch keine MCP-Authentifizierung.', + 'MCP-Server "{{name}}" fehlgeschlagen. Hinweis: Kilo unterstützt noch keine MCP-Authentifizierung.', "error.chain.providerAuthFailed": "Anbieter-Authentifizierung fehlgeschlagen ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Anbieter "{{provider}}" konnte nicht initialisiert werden. Überprüfen Sie Anmeldeinformationen und Konfiguration.', @@ -552,20 +552,20 @@ export const dict = { "sidebar.workspaces.enable": "Arbeitsbereiche aktivieren", "sidebar.workspaces.disable": "Arbeitsbereiche deaktivieren", "sidebar.gettingStarted.title": "Erste Schritte", - "sidebar.gettingStarted.line1": "OpenCode enthält kostenlose Modelle, damit Sie sofort loslegen können.", + "sidebar.gettingStarted.line1": "Kilo enthält kostenlose Modelle, damit Sie sofort loslegen können.", "sidebar.gettingStarted.line2": "Verbinden Sie einen beliebigen Anbieter, um Modelle wie Claude, GPT, Gemini usw. zu nutzen.", "sidebar.project.recentSessions": "Letzte Sitzungen", "sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen", "sidebar.project.clearNotifications": "Benachrichtigungen löschen", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", "settings.tab.general": "Allgemein", "settings.tab.shortcuts": "Tastenkombinationen", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL-Integration", - "settings.desktop.wsl.description": "OpenCode-Server innerhalb von WSL unter Windows ausführen.", + "settings.desktop.wsl.description": "Kilo-Server innerhalb von WSL unter Windows ausführen.", "settings.general.section.appearance": "Erscheinungsbild", "settings.general.section.notifications": "Systembenachrichtigungen", "settings.general.section.updates": "Updates", @@ -573,14 +573,14 @@ export const dict = { "settings.general.section.feed": "Feed", "settings.general.section.display": "Anzeige", "settings.general.row.language.title": "Sprache", - "settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern", + "settings.general.row.language.description": "Die Anzeigesprache für Kilo ändern", "settings.general.row.appearance.title": "Erscheinungsbild", - "settings.general.row.appearance.description": "Anpassen, wie OpenCode auf Ihrem Gerät aussieht", + "settings.general.row.appearance.description": "Anpassen, wie Kilo auf Ihrem Gerät aussieht", "settings.general.row.colorScheme.title": "Farbschema", "settings.general.row.colorScheme.description": - "Wählen Sie, ob OpenCode dem System-, hellen oder dunklen Thema folgt", + "Wählen Sie, ob Kilo dem System-, hellen oder dunklen Thema folgt", "settings.general.row.theme.title": "Thema", - "settings.general.row.theme.description": "Das Thema von OpenCode anpassen.", + "settings.general.row.theme.description": "Das Thema von Kilo anpassen.", "settings.general.row.font.title": "Code-Schriftart", "settings.general.row.font.description": "Die in Codeblöcken und Terminals verwendete Schriftart anpassen", "settings.general.row.uiFont.title": "UI-Schriftart", @@ -606,13 +606,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "Versionshinweise", "settings.general.row.releaseNotes.description": '"Neuigkeiten"-Pop-ups nach Updates anzeigen', "settings.updates.row.startup.title": "Beim Start nach Updates suchen", - "settings.updates.row.startup.description": "Beim Start von OpenCode automatisch nach Updates suchen", + "settings.updates.row.startup.description": "Beim Start von Kilo automatisch nach Updates suchen", "settings.updates.row.check.title": "Nach Updates suchen", "settings.updates.row.check.description": "Manuell nach Updates suchen und installieren, wenn verfügbar", "settings.updates.action.checkNow": "Jetzt prüfen", "settings.updates.action.checking": "Wird geprüft...", "settings.updates.toast.latest.title": "Du bist auf dem neuesten Stand", - "settings.updates.toast.latest.description": "Du verwendest die aktuelle Version von OpenCode.", + "settings.updates.toast.latest.description": "Du verwendest die aktuelle Version von Kilo.", "sound.option.none": "Keine", "sound.option.alert01": "Alarm 01", "sound.option.alert02": "Alarm 02", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index ace0efeb87..53262ba70a 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -117,7 +117,7 @@ export const dict = { "dialog.model.manage.description": "Customize which models appear in the model selector.", "dialog.model.manage.provider.toggle": "Toggle all {{provider}} models", - "dialog.model.unpaid.freeModels.title": "Free models provided by OpenCode", + "dialog.model.unpaid.freeModels.title": "Free models provided by Kilo", "dialog.model.unpaid.addMore.title": "Add more models from popular providers", "dialog.provider.viewAll": "Show more providers", @@ -130,7 +130,7 @@ export const dict = { "provider.connect.status.waiting": "Waiting for authorization...", "provider.connect.status.failed": "Authorization failed: {{error}}", "provider.connect.apiKey.description": - "Enter your {{provider}} API key to connect your account and use {{provider}} models in OpenCode.", + "Enter your {{provider}} API key to connect your account and use {{provider}} models in Kilo.", "provider.connect.apiKey.label": "{{provider}} API key", "provider.connect.apiKey.placeholder": "API key", "provider.connect.apiKey.required": "API key is required", @@ -144,7 +144,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Visit ", "provider.connect.oauth.code.visit.link": "this link", "provider.connect.oauth.code.visit.suffix": - " to collect your authorization code to connect your account and use {{provider}} models in OpenCode.", + " to collect your authorization code to connect your account and use {{provider}} models in Kilo.", "provider.connect.oauth.code.label": "{{method}} authorization code", "provider.connect.oauth.code.placeholder": "Authorization code", "provider.connect.oauth.code.required": "Authorization code is required", @@ -152,7 +152,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Visit ", "provider.connect.oauth.auto.visit.link": "this link", "provider.connect.oauth.auto.visit.suffix": - " and enter the code below to connect your account and use {{provider}} models in OpenCode.", + " and enter the code below to connect your account and use {{provider}} models in Kilo.", "provider.connect.oauth.auto.confirmationCode": "Confirmation code", "provider.connect.toast.connected.title": "{{provider}} connected", "provider.connect.toast.connected.description": "{{provider}} models are now available to use.", @@ -314,7 +314,7 @@ export const dict = { "app.server.otherServers": "Other servers", "dialog.server.title": "Servers", - "dialog.server.description": "Switch which OpenCode server this app connects to.", + "dialog.server.description": "Switch which Kilo server this app connects to.", "dialog.server.search.placeholder": "Search servers", "dialog.server.empty": "No servers yet", "dialog.server.add.title": "Add server", @@ -455,7 +455,7 @@ export const dict = { "toast.project.reloadFailed.title": "Failed to reload {{project}}", "toast.update.title": "Update available", - "toast.update.description": "A new version of OpenCode ({{version}}) is now available to install.", + "toast.update.description": "A new version of Kilo ({{version}}) is now available to install.", "toast.update.action.installRestart": "Install and restart", "toast.update.action.notYet": "Not yet", @@ -467,7 +467,7 @@ export const dict = { "error.page.action.checkUpdates": "Check for updates", "error.page.action.updateTo": "Update to {{version}}", "error.page.circular": "[Circular]", - "error.page.report.prefix": "Please report this error to the OpenCode team", + "error.page.report.prefix": "Please report this error to the Kilo team", "error.page.report.discord": "on Discord", "error.page.version": "Version: {{version}}", @@ -493,7 +493,7 @@ export const dict = { "error.chain.didYouMean": "Did you mean: {{suggestions}}", "error.chain.modelNotFound": "Model not found: {{provider}}/{{model}}", "error.chain.checkConfig": "Check your config (opencode.json) provider/model names", - "error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, OpenCode does not support MCP authentication yet.', + "error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, Kilo does not support MCP authentication yet.', "error.chain.providerAuthFailed": "Provider authentication failed ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Failed to initialize provider "{{provider}}". Check credentials and configuration.', @@ -674,7 +674,7 @@ export const dict = { "sidebar.workspaces.enable": "Enable workspaces", "sidebar.workspaces.disable": "Disable workspaces", "sidebar.gettingStarted.title": "Getting started", - "sidebar.gettingStarted.line1": "OpenCode includes free models so you can start immediately.", + "sidebar.gettingStarted.line1": "Kilo includes free models so you can start immediately.", "sidebar.gettingStarted.line2": "Connect any provider to use models, inc. Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Recent sessions", "sidebar.project.viewAllSessions": "View all sessions", @@ -706,7 +706,7 @@ export const dict = { "debugBar.mem.tipUnavailable": "Used JS heap vs heap limit. Chromium only.", "debugBar.mem.tip": "Used JS heap vs heap limit. {{used}} of {{limit}}.", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Desktop", "settings.section.server": "Server", @@ -714,7 +714,7 @@ export const dict = { "settings.tab.shortcuts": "Shortcuts", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL integration", - "settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.", + "settings.desktop.wsl.description": "Run the Kilo server inside WSL on Windows.", "settings.general.section.appearance": "Appearance", "settings.general.section.notifications": "System notifications", @@ -724,13 +724,13 @@ export const dict = { "settings.general.section.display": "Display", "settings.general.row.language.title": "Language", - "settings.general.row.language.description": "Change the display language for OpenCode", + "settings.general.row.language.description": "Change the display language for Kilo", "settings.general.row.appearance.title": "Appearance", - "settings.general.row.appearance.description": "Customise how OpenCode looks on your device", + "settings.general.row.appearance.description": "Customise how Kilo looks on your device", "settings.general.row.colorScheme.title": "Color scheme", - "settings.general.row.colorScheme.description": "Choose whether OpenCode follows the system, light, or dark theme", + "settings.general.row.colorScheme.description": "Choose whether Kilo follows the system, light, or dark theme", "settings.general.row.theme.title": "Theme", - "settings.general.row.theme.description": "Customise how OpenCode is themed.", + "settings.general.row.theme.description": "Customise how Kilo is themed.", "settings.general.row.font.title": "Code Font", "settings.general.row.font.description": "Customise the font used in code blocks and terminals", "settings.general.row.uiFont.title": "UI Font", @@ -757,13 +757,13 @@ export const dict = { "settings.general.row.releaseNotes.description": "Show What's New popups after updates", "settings.updates.row.startup.title": "Check for updates on startup", - "settings.updates.row.startup.description": "Automatically check for updates when OpenCode launches", + "settings.updates.row.startup.description": "Automatically check for updates when Kilo launches", "settings.updates.row.check.title": "Check for updates", "settings.updates.row.check.description": "Manually check for updates and install if available", "settings.updates.action.checkNow": "Check now", "settings.updates.action.checking": "Checking...", "settings.updates.toast.latest.title": "You're up to date", - "settings.updates.toast.latest.description": "You're running the latest version of OpenCode.", + "settings.updates.toast.latest.description": "You're running the latest version of Kilo.", "sound.option.none": "None", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index c600232ef6..c00c2ba50c 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -115,7 +115,7 @@ export const dict = { "dialog.model.manage.description": "Personalizar qué modelos aparecen en el selector de modelos.", "dialog.model.manage.provider.toggle": "Alternar todos los modelos de {{provider}}", - "dialog.model.unpaid.freeModels.title": "Modelos gratuitos proporcionados por OpenCode", + "dialog.model.unpaid.freeModels.title": "Modelos gratuitos proporcionados por Kilo", "dialog.model.unpaid.addMore.title": "Añadir más modelos de proveedores populares", "dialog.provider.viewAll": "Ver más proveedores", @@ -128,7 +128,7 @@ export const dict = { "provider.connect.status.waiting": "Esperando autorización...", "provider.connect.status.failed": "Autorización fallida: {{error}}", "provider.connect.apiKey.description": - "Introduce tu clave API de {{provider}} para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.", + "Introduce tu clave API de {{provider}} para conectar tu cuenta y usar modelos de {{provider}} en Kilo.", "provider.connect.apiKey.label": "Clave API de {{provider}}", "provider.connect.apiKey.placeholder": "Clave API", "provider.connect.apiKey.required": "La clave API es obligatoria", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Visita ", "provider.connect.oauth.code.visit.link": "este enlace", "provider.connect.oauth.code.visit.suffix": - " para obtener tu código de autorización para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.", + " para obtener tu código de autorización para conectar tu cuenta y usar modelos de {{provider}} en Kilo.", "provider.connect.oauth.code.label": "Código de autorización {{method}}", "provider.connect.oauth.code.placeholder": "Código de autorización", "provider.connect.oauth.code.required": "El código de autorización es obligatorio", @@ -150,7 +150,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Visita ", "provider.connect.oauth.auto.visit.link": "este enlace", "provider.connect.oauth.auto.visit.suffix": - " e introduce el código a continuación para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.", + " e introduce el código a continuación para conectar tu cuenta y usar modelos de {{provider}} en Kilo.", "provider.connect.oauth.auto.confirmationCode": "Código de confirmación", "provider.connect.toast.connected.title": "{{provider}} conectado", "provider.connect.toast.connected.description": "Los modelos de {{provider}} ahora están disponibles para usar.", @@ -307,7 +307,7 @@ export const dict = { "dialog.directory.empty": "No se encontraron carpetas", "dialog.server.title": "Servidores", - "dialog.server.description": "Cambiar a qué servidor de OpenCode se conecta esta app.", + "dialog.server.description": "Cambiar a qué servidor de Kilo se conecta esta app.", "dialog.server.search.placeholder": "Buscar servidores", "dialog.server.empty": "No hay servidores aún", "dialog.server.add.title": "Añadir un servidor", @@ -441,7 +441,7 @@ export const dict = { "toast.session.listFailed.title": "Fallo al cargar sesiones para {{project}}", "toast.update.title": "Actualización disponible", - "toast.update.description": "Una nueva versión de OpenCode ({{version}}) está disponible para instalar.", + "toast.update.description": "Una nueva versión de Kilo ({{version}}) está disponible para instalar.", "toast.update.action.installRestart": "Instalar y reiniciar", "toast.update.action.notYet": "Todavía no", @@ -452,7 +452,7 @@ export const dict = { "error.page.action.checking": "Comprobando...", "error.page.action.checkUpdates": "Buscar actualizaciones", "error.page.action.updateTo": "Actualizar a {{version}}", - "error.page.report.prefix": "Por favor reporta este error al equipo de OpenCode", + "error.page.report.prefix": "Por favor reporta este error al equipo de Kilo", "error.page.report.discord": "en Discord", "error.page.version": "Versión: {{version}}", @@ -471,7 +471,7 @@ export const dict = { "error.chain.didYouMean": "¿Quisiste decir: {{suggestions}}", "error.chain.modelNotFound": "Modelo no encontrado: {{provider}}/{{model}}", "error.chain.checkConfig": "Comprueba los nombres de proveedor/modelo en tu configuración (opencode.json)", - "error.chain.mcpFailed": 'El servidor MCP "{{name}}" falló. Nota, OpenCode no soporta autenticación MCP todavía.', + "error.chain.mcpFailed": 'El servidor MCP "{{name}}" falló. Nota, Kilo no soporta autenticación MCP todavía.', "error.chain.providerAuthFailed": "Autenticación de proveedor fallida ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Fallo al inicializar proveedor "{{provider}}". Comprueba credenciales y configuración.', @@ -608,13 +608,13 @@ export const dict = { "sidebar.workspaces.enable": "Habilitar espacios de trabajo", "sidebar.workspaces.disable": "Deshabilitar espacios de trabajo", "sidebar.gettingStarted.title": "Empezando", - "sidebar.gettingStarted.line1": "OpenCode incluye modelos gratuitos para que puedas empezar inmediatamente.", + "sidebar.gettingStarted.line1": "Kilo incluye modelos gratuitos para que puedas empezar inmediatamente.", "sidebar.gettingStarted.line2": "Conecta cualquier proveedor para usar modelos, inc. Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Sesiones recientes", "sidebar.project.viewAllSessions": "Ver todas las sesiones", "sidebar.project.clearNotifications": "Borrar notificaciones", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Escritorio", "settings.section.server": "Servidor", @@ -622,7 +622,7 @@ export const dict = { "settings.tab.shortcuts": "Atajos", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "Integración con WSL", - "settings.desktop.wsl.description": "Ejecutar el servidor OpenCode dentro de WSL en Windows.", + "settings.desktop.wsl.description": "Ejecutar el servidor Kilo dentro de WSL en Windows.", "settings.general.section.appearance": "Apariencia", "settings.general.section.notifications": "Notificaciones del sistema", @@ -632,13 +632,13 @@ export const dict = { "settings.general.section.display": "Pantalla", "settings.general.row.language.title": "Idioma", - "settings.general.row.language.description": "Cambiar el idioma de visualización para OpenCode", + "settings.general.row.language.description": "Cambiar el idioma de visualización para Kilo", "settings.general.row.appearance.title": "Apariencia", - "settings.general.row.appearance.description": "Personaliza cómo se ve OpenCode en tu dispositivo", + "settings.general.row.appearance.description": "Personaliza cómo se ve Kilo en tu dispositivo", "settings.general.row.colorScheme.title": "Esquema de color", - "settings.general.row.colorScheme.description": "Elige si OpenCode sigue el tema del sistema, claro u oscuro", + "settings.general.row.colorScheme.description": "Elige si Kilo sigue el tema del sistema, claro u oscuro", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "Personaliza el tema de OpenCode.", + "settings.general.row.theme.description": "Personaliza el tema de Kilo.", "settings.general.row.font.title": "Fuente de código", "settings.general.row.font.description": "Personaliza la fuente usada en bloques de código y terminales", "settings.general.row.uiFont.title": "Fuente de la interfaz", @@ -667,13 +667,13 @@ export const dict = { 'Mostrar ventanas emergentes de "Novedades" después de las actualizaciones', "settings.updates.row.startup.title": "Buscar actualizaciones al iniciar", - "settings.updates.row.startup.description": "Buscar actualizaciones automáticamente cuando se inicia OpenCode", + "settings.updates.row.startup.description": "Buscar actualizaciones automáticamente cuando se inicia Kilo", "settings.updates.row.check.title": "Buscar actualizaciones", "settings.updates.row.check.description": "Buscar actualizaciones manualmente e instalarlas si hay alguna", "settings.updates.action.checkNow": "Buscar ahora", "settings.updates.action.checking": "Buscando...", "settings.updates.toast.latest.title": "Estás al día", - "settings.updates.toast.latest.description": "Estás usando la última versión de OpenCode.", + "settings.updates.toast.latest.description": "Estás usando la última versión de Kilo.", "sound.option.none": "Ninguno", "sound.option.alert01": "Alerta 01", "sound.option.alert02": "Alerta 02", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index a140c1e3a1..2bd6ea940e 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -105,7 +105,7 @@ export const dict = { "dialog.model.manage": "Gérer les modèles", "dialog.model.manage.description": "Personnalisez les modèles qui apparaissent dans le sélecteur.", "dialog.model.manage.provider.toggle": "Basculer tous les modèles {{provider}}", - "dialog.model.unpaid.freeModels.title": "Modèles gratuits fournis par OpenCode", + "dialog.model.unpaid.freeModels.title": "Modèles gratuits fournis par Kilo", "dialog.model.unpaid.addMore.title": "Ajouter plus de modèles de fournisseurs populaires", "dialog.provider.viewAll": "Voir plus de fournisseurs", "provider.connect.title": "Connecter {{provider}}", @@ -116,7 +116,7 @@ export const dict = { "provider.connect.status.waiting": "En attente d'autorisation...", "provider.connect.status.failed": "Échec de l'autorisation : {{error}}", "provider.connect.apiKey.description": - "Entrez votre clé API {{provider}} pour connecter votre compte et utiliser les modèles {{provider}} dans OpenCode.", + "Entrez votre clé API {{provider}} pour connecter votre compte et utiliser les modèles {{provider}} dans Kilo.", "provider.connect.apiKey.label": "Clé API {{provider}}", "provider.connect.apiKey.placeholder": "Clé API", "provider.connect.apiKey.required": "La clé API est requise", @@ -130,7 +130,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Visitez ", "provider.connect.oauth.code.visit.link": "ce lien", "provider.connect.oauth.code.visit.suffix": - " pour récupérer votre code d'autorisation afin de connecter votre compte et utiliser les modèles {{provider}} dans OpenCode.", + " pour récupérer votre code d'autorisation afin de connecter votre compte et utiliser les modèles {{provider}} dans Kilo.", "provider.connect.oauth.code.label": "Code d'autorisation {{method}}", "provider.connect.oauth.code.placeholder": "Code d'autorisation", "provider.connect.oauth.code.required": "Le code d'autorisation est requis", @@ -138,7 +138,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Visitez ", "provider.connect.oauth.auto.visit.link": "ce lien", "provider.connect.oauth.auto.visit.suffix": - " et entrez le code ci-dessous pour connecter votre compte et utiliser les modèles {{provider}} dans OpenCode.", + " et entrez le code ci-dessous pour connecter votre compte et utiliser les modèles {{provider}} dans Kilo.", "provider.connect.oauth.auto.confirmationCode": "Code de confirmation", "provider.connect.toast.connected.title": "{{provider}} connecté", "provider.connect.toast.connected.description": "Les modèles {{provider}} sont maintenant disponibles.", @@ -282,7 +282,7 @@ export const dict = { "dialog.directory.search.placeholder": "Rechercher des dossiers", "dialog.directory.empty": "Aucun dossier trouvé", "dialog.server.title": "Serveurs", - "dialog.server.description": "Changez le serveur OpenCode auquel cette application se connecte.", + "dialog.server.description": "Changez le serveur Kilo auquel cette application se connecte.", "dialog.server.search.placeholder": "Rechercher des serveurs", "dialog.server.empty": "Aucun serveur pour l'instant", "dialog.server.add.title": "Ajouter un serveur", @@ -399,7 +399,7 @@ export const dict = { "toast.session.listFailed.title": "Échec du chargement des sessions pour {{project}}", "toast.update.title": "Mise à jour disponible", "toast.update.description": - "Une nouvelle version d'OpenCode ({{version}}) est maintenant disponible pour installation.", + "Une nouvelle version d'Kilo ({{version}}) est maintenant disponible pour installation.", "toast.update.action.installRestart": "Installer et redémarrer", "toast.update.action.notYet": "Pas encore", "error.page.title": "Quelque chose s'est mal passé", @@ -409,7 +409,7 @@ export const dict = { "error.page.action.checking": "Vérification...", "error.page.action.checkUpdates": "Vérifier les mises à jour", "error.page.action.updateTo": "Mettre à jour vers {{version}}", - "error.page.report.prefix": "Veuillez signaler cette erreur à l'équipe OpenCode", + "error.page.report.prefix": "Veuillez signaler cette erreur à l'équipe Kilo", "error.page.report.discord": "sur Discord", "error.page.version": "Version : {{version}}", "error.dev.rootNotFound": @@ -427,7 +427,7 @@ export const dict = { "error.chain.modelNotFound": "Modèle introuvable : {{provider}}/{{model}}", "error.chain.checkConfig": "Vérifiez votre configuration (opencode.json) pour les noms de fournisseur/modèle", "error.chain.mcpFailed": - "Le serveur MCP \"{{name}}\" a échoué. Notez qu'OpenCode ne supporte pas encore l'authentification MCP.", + "Le serveur MCP \"{{name}}\" a échoué. Notez qu'Kilo ne supporte pas encore l'authentification MCP.", "error.chain.providerAuthFailed": "Échec de l'authentification du fournisseur ({{provider}}) : {{message}}", "error.chain.providerInitFailed": 'Échec de l\'initialisation du fournisseur "{{provider}}". Vérifiez les identifiants et la configuration.', @@ -550,20 +550,20 @@ export const dict = { "sidebar.workspaces.disable": "Désactiver les espaces de travail", "sidebar.gettingStarted.title": "Commencer", "sidebar.gettingStarted.line1": - "OpenCode inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", + "Kilo inclut des modèles gratuits pour que vous puissiez commencer immédiatement.", "sidebar.gettingStarted.line2": "Connectez n'importe quel fournisseur pour utiliser des modèles, y compris Claude, GPT, Gemini etc.", "sidebar.project.recentSessions": "Sessions récentes", "sidebar.project.viewAllSessions": "Voir toutes les sessions", "sidebar.project.clearNotifications": "Effacer les notifications", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Bureau", "settings.section.server": "Serveur", "settings.tab.general": "Général", "settings.tab.shortcuts": "Raccourcis", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "Intégration WSL", - "settings.desktop.wsl.description": "Exécuter le serveur OpenCode dans WSL sur Windows.", + "settings.desktop.wsl.description": "Exécuter le serveur Kilo dans WSL sur Windows.", "settings.general.section.appearance": "Apparence", "settings.general.section.notifications": "Notifications système", "settings.general.section.updates": "Mises à jour", @@ -571,13 +571,13 @@ export const dict = { "settings.general.section.feed": "Flux", "settings.general.section.display": "Affichage", "settings.general.row.language.title": "Langue", - "settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode", + "settings.general.row.language.description": "Changer la langue d'affichage pour Kilo", "settings.general.row.appearance.title": "Apparence", - "settings.general.row.appearance.description": "Personnaliser l'apparence d'OpenCode sur votre appareil", + "settings.general.row.appearance.description": "Personnaliser l'apparence d'Kilo sur votre appareil", "settings.general.row.colorScheme.title": "Schéma de couleurs", - "settings.general.row.colorScheme.description": "Choisissez si OpenCode suit le thème système, clair ou sombre", + "settings.general.row.colorScheme.description": "Choisissez si Kilo suit le thème système, clair ou sombre", "settings.general.row.theme.title": "Thème", - "settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.", + "settings.general.row.theme.description": "Personnaliser le thème d'Kilo.", "settings.general.row.font.title": "Police de code", "settings.general.row.font.description": "Personnaliser la police utilisée dans les blocs de code et les terminaux", "settings.general.row.uiFont.title": "Police de l'interface", @@ -603,13 +603,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "Notes de version", "settings.general.row.releaseNotes.description": 'Afficher des pop-ups "Quoi de neuf" après les mises à jour', "settings.updates.row.startup.title": "Vérifier les mises à jour au démarrage", - "settings.updates.row.startup.description": "Vérifier automatiquement les mises à jour au lancement d'OpenCode", + "settings.updates.row.startup.description": "Vérifier automatiquement les mises à jour au lancement d'Kilo", "settings.updates.row.check.title": "Vérifier les mises à jour", "settings.updates.row.check.description": "Vérifier manuellement les mises à jour et installer si disponible", "settings.updates.action.checkNow": "Vérifier maintenant", "settings.updates.action.checking": "Vérification...", "settings.updates.toast.latest.title": "Vous êtes à jour", - "settings.updates.toast.latest.description": "Vous utilisez la dernière version d'OpenCode.", + "settings.updates.toast.latest.description": "Vous utilisez la dernière version d'Kilo.", "sound.option.none": "Aucun", "sound.option.alert01": "Alerte 01", "sound.option.alert02": "Alerte 02", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 3da1c4b43b..9d1657c12f 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -105,7 +105,7 @@ export const dict = { "dialog.model.manage": "モデルを管理", "dialog.model.manage.description": "モデルセレクターに表示するモデルをカスタマイズします。", "dialog.model.manage.provider.toggle": "すべての{{provider}}モデルを切り替え", - "dialog.model.unpaid.freeModels.title": "OpenCodeが提供する無料モデル", + "dialog.model.unpaid.freeModels.title": "Kiloが提供する無料モデル", "dialog.model.unpaid.addMore.title": "人気のプロバイダーからモデルを追加", "dialog.provider.viewAll": "さらにプロバイダーを表示", "provider.connect.title": "{{provider}}を接続", @@ -116,7 +116,7 @@ export const dict = { "provider.connect.status.waiting": "認証を待機中...", "provider.connect.status.failed": "認証に失敗しました: {{error}}", "provider.connect.apiKey.description": - "{{provider}}のAPIキーを入力してアカウントを接続し、OpenCodeで{{provider}}モデルを使用します。", + "{{provider}}のAPIキーを入力してアカウントを接続し、Kiloで{{provider}}モデルを使用します。", "provider.connect.apiKey.label": "{{provider}} APIキー", "provider.connect.apiKey.placeholder": "APIキー", "provider.connect.apiKey.required": "APIキーが必要です", @@ -129,7 +129,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": " ", "provider.connect.oauth.code.visit.link": "このリンク", "provider.connect.oauth.code.visit.suffix": - " にアクセスして認証コードを取得し、アカウントを接続してOpenCodeで{{provider}}モデルを使用してください。", + " にアクセスして認証コードを取得し、アカウントを接続してKiloで{{provider}}モデルを使用してください。", "provider.connect.oauth.code.label": "{{method}} 認証コード", "provider.connect.oauth.code.placeholder": "認証コード", "provider.connect.oauth.code.required": "認証コードが必要です", @@ -137,7 +137,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": " ", "provider.connect.oauth.auto.visit.link": "このリンク", "provider.connect.oauth.auto.visit.suffix": - " にアクセスし、以下のコードを入力してアカウントを接続し、OpenCodeで{{provider}}モデルを使用してください。", + " にアクセスし、以下のコードを入力してアカウントを接続し、Kiloで{{provider}}モデルを使用してください。", "provider.connect.oauth.auto.confirmationCode": "確認コード", "provider.connect.toast.connected.title": "{{provider}}が接続されました", "provider.connect.toast.connected.description": "{{provider}}モデルが使用可能になりました。", @@ -280,7 +280,7 @@ export const dict = { "dialog.directory.search.placeholder": "フォルダを検索", "dialog.directory.empty": "フォルダが見つかりません", "dialog.server.title": "サーバー", - "dialog.server.description": "このアプリが接続するOpenCodeサーバーを切り替えます。", + "dialog.server.description": "このアプリが接続するKiloサーバーを切り替えます。", "dialog.server.search.placeholder": "サーバーを検索", "dialog.server.empty": "サーバーはまだありません", "dialog.server.add.title": "サーバーを追加", @@ -395,7 +395,7 @@ export const dict = { "toast.session.unshare.failed.description": "セッションの共有解除中にエラーが発生しました", "toast.session.listFailed.title": "{{project}}のセッション読み込みに失敗しました", "toast.update.title": "アップデートが利用可能です", - "toast.update.description": "OpenCodeの新しいバージョン ({{version}}) がインストール可能です。", + "toast.update.description": "Kiloの新しいバージョン ({{version}}) がインストール可能です。", "toast.update.action.installRestart": "インストールして再起動", "toast.update.action.notYet": "今はしない", "error.page.title": "問題が発生しました", @@ -405,7 +405,7 @@ export const dict = { "error.page.action.checking": "確認中...", "error.page.action.checkUpdates": "アップデートを確認", "error.page.action.updateTo": "{{version}}にアップデート", - "error.page.report.prefix": "このエラーをOpenCodeチームに報告してください: ", + "error.page.report.prefix": "このエラーをKiloチームに報告してください: ", "error.page.report.discord": "Discord", "error.page.version": "バージョン: {{version}}", "error.dev.rootNotFound": @@ -421,7 +421,7 @@ export const dict = { "error.chain.didYouMean": "もしかして: {{suggestions}}", "error.chain.modelNotFound": "モデルが見つかりません: {{provider}}/{{model}}", "error.chain.checkConfig": "config (opencode.json) のプロバイダー/モデル名を確認してください", - "error.chain.mcpFailed": 'MCPサーバー "{{name}}" が失敗しました。注意: OpenCodeはまだMCP認証をサポートしていません。', + "error.chain.mcpFailed": 'MCPサーバー "{{name}}" が失敗しました。注意: KiloはまだMCP認証をサポートしていません。', "error.chain.providerAuthFailed": "プロバイダー認証に失敗しました ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'プロバイダー "{{provider}}" の初期化に失敗しました。認証情報と設定を確認してください。', @@ -541,19 +541,19 @@ export const dict = { "sidebar.workspaces.enable": "ワークスペースを有効化", "sidebar.workspaces.disable": "ワークスペースを無効化", "sidebar.gettingStarted.title": "はじめに", - "sidebar.gettingStarted.line1": "OpenCodeには無料モデルが含まれているため、すぐに開始できます。", + "sidebar.gettingStarted.line1": "Kiloには無料モデルが含まれているため、すぐに開始できます。", "sidebar.gettingStarted.line2": "プロバイダーを接続して、Claude、GPT、Geminiなどのモデルを使用できます。", "sidebar.project.recentSessions": "最近のセッション", "sidebar.project.viewAllSessions": "すべてのセッションを表示", "sidebar.project.clearNotifications": "通知をクリア", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "デスクトップ", "settings.section.server": "サーバー", "settings.tab.general": "一般", "settings.tab.shortcuts": "ショートカット", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL連携", - "settings.desktop.wsl.description": "WindowsのWSL環境でOpenCodeサーバーを実行します。", + "settings.desktop.wsl.description": "WindowsのWSL環境でKiloサーバーを実行します。", "settings.general.section.appearance": "外観", "settings.general.section.notifications": "システム通知", "settings.general.section.updates": "アップデート", @@ -561,13 +561,13 @@ export const dict = { "settings.general.section.feed": "フィード", "settings.general.section.display": "ディスプレイ", "settings.general.row.language.title": "言語", - "settings.general.row.language.description": "OpenCodeの表示言語を変更します", + "settings.general.row.language.description": "Kiloの表示言語を変更します", "settings.general.row.appearance.title": "外観", - "settings.general.row.appearance.description": "デバイスでのOpenCodeの表示をカスタマイズします", + "settings.general.row.appearance.description": "デバイスでのKiloの表示をカスタマイズします", "settings.general.row.colorScheme.title": "配色", - "settings.general.row.colorScheme.description": "OpenCodeがシステム、ライト、またはダークテーマに従うかを選択します", + "settings.general.row.colorScheme.description": "Kiloがシステム、ライト、またはダークテーマに従うかを選択します", "settings.general.row.theme.title": "テーマ", - "settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。", + "settings.general.row.theme.description": "Kiloのテーマをカスタマイズします。", "settings.general.row.font.title": "コードフォント", "settings.general.row.font.description": "コードブロックとターミナルで使用するフォントをカスタマイズします", "settings.general.row.uiFont.title": "UIフォント", @@ -592,13 +592,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "リリースノート", "settings.general.row.releaseNotes.description": "アップデート後に「新機能」ポップアップを表示", "settings.updates.row.startup.title": "起動時にアップデートを確認", - "settings.updates.row.startup.description": "OpenCode の起動時に自動でアップデートを確認します", + "settings.updates.row.startup.description": "Kilo の起動時に自動でアップデートを確認します", "settings.updates.row.check.title": "アップデートを確認", "settings.updates.row.check.description": "手動でアップデートを確認し、利用可能ならインストールします", "settings.updates.action.checkNow": "今すぐ確認", "settings.updates.action.checking": "確認中...", "settings.updates.toast.latest.title": "最新です", - "settings.updates.toast.latest.description": "OpenCode は最新バージョンです。", + "settings.updates.toast.latest.description": "Kilo は最新バージョンです。", "sound.option.none": "なし", "sound.option.alert01": "アラート 01", "sound.option.alert02": "アラート 02", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 0f2f7647ab..7814fa3fdf 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -109,7 +109,7 @@ export const dict = { "dialog.model.manage": "모델 관리", "dialog.model.manage.description": "모델 선택기에 표시할 모델 사용자 지정", "dialog.model.manage.provider.toggle": "모든 {{provider}} 모델 토글", - "dialog.model.unpaid.freeModels.title": "OpenCode에서 제공하는 무료 모델", + "dialog.model.unpaid.freeModels.title": "Kilo에서 제공하는 무료 모델", "dialog.model.unpaid.addMore.title": "인기 공급자의 모델 추가", "dialog.provider.viewAll": "더 많은 공급자 보기", "provider.connect.title": "{{provider}} 연결", @@ -120,7 +120,7 @@ export const dict = { "provider.connect.status.waiting": "인증 대기 중...", "provider.connect.status.failed": "인증 실패: {{error}}", "provider.connect.apiKey.description": - "{{provider}} API 키를 입력하여 계정을 연결하고 OpenCode에서 {{provider}} 모델을 사용하세요.", + "{{provider}} API 키를 입력하여 계정을 연결하고 Kilo에서 {{provider}} 모델을 사용하세요.", "provider.connect.apiKey.label": "{{provider}} API 키", "provider.connect.apiKey.placeholder": "API 키", "provider.connect.apiKey.required": "API 키가 필요합니다", @@ -133,7 +133,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "다음 ", "provider.connect.oauth.code.visit.link": "이 링크", "provider.connect.oauth.code.visit.suffix": - "를 방문하여 인증 코드를 받아 계정을 연결하고 OpenCode에서 {{provider}} 모델을 사용하세요.", + "를 방문하여 인증 코드를 받아 계정을 연결하고 Kilo에서 {{provider}} 모델을 사용하세요.", "provider.connect.oauth.code.label": "{{method}} 인증 코드", "provider.connect.oauth.code.placeholder": "인증 코드", "provider.connect.oauth.code.required": "인증 코드가 필요합니다", @@ -141,7 +141,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "다음 ", "provider.connect.oauth.auto.visit.link": "이 링크", "provider.connect.oauth.auto.visit.suffix": - "를 방문하고 아래 코드를 입력하여 계정을 연결하고 OpenCode에서 {{provider}} 모델을 사용하세요.", + "를 방문하고 아래 코드를 입력하여 계정을 연결하고 Kilo에서 {{provider}} 모델을 사용하세요.", "provider.connect.oauth.auto.confirmationCode": "확인 코드", "provider.connect.toast.connected.title": "{{provider}} 연결됨", "provider.connect.toast.connected.description": "이제 {{provider}} 모델을 사용할 수 있습니다.", @@ -284,7 +284,7 @@ export const dict = { "dialog.directory.search.placeholder": "폴더 검색", "dialog.directory.empty": "폴더 없음", "dialog.server.title": "서버", - "dialog.server.description": "이 앱이 연결할 OpenCode 서버를 전환합니다.", + "dialog.server.description": "이 앱이 연결할 Kilo 서버를 전환합니다.", "dialog.server.search.placeholder": "서버 검색", "dialog.server.empty": "서버 없음", "dialog.server.add.title": "서버 추가", @@ -398,7 +398,7 @@ export const dict = { "toast.session.unshare.failed.description": "세션 공유를 해제하는 동안 오류가 발생했습니다", "toast.session.listFailed.title": "{{project}}에 대한 세션을 로드하지 못했습니다", "toast.update.title": "업데이트 가능", - "toast.update.description": "OpenCode의 새 버전({{version}})을 설치할 수 있습니다.", + "toast.update.description": "Kilo의 새 버전({{version}})을 설치할 수 있습니다.", "toast.update.action.installRestart": "설치 및 다시 시작", "toast.update.action.notYet": "나중에", "error.page.title": "문제가 발생했습니다", @@ -408,7 +408,7 @@ export const dict = { "error.page.action.checking": "확인 중...", "error.page.action.checkUpdates": "업데이트 확인", "error.page.action.updateTo": "{{version}} 버전으로 업데이트", - "error.page.report.prefix": "이 오류를 OpenCode 팀에 제보해 주세요: ", + "error.page.report.prefix": "이 오류를 Kilo 팀에 제보해 주세요: ", "error.page.report.discord": "Discord", "error.page.version": "버전: {{version}}", "error.dev.rootNotFound": @@ -424,7 +424,7 @@ export const dict = { "error.chain.didYouMean": "혹시 {{suggestions}}을(를) 의미하셨나요?", "error.chain.modelNotFound": "모델을 찾을 수 없음: {{provider}}/{{model}}", "error.chain.checkConfig": "구성(opencode.json)의 공급자/모델 이름을 확인하세요", - "error.chain.mcpFailed": 'MCP 서버 "{{name}}" 실패. 참고: OpenCode는 아직 MCP 인증을 지원하지 않습니다.', + "error.chain.mcpFailed": 'MCP 서버 "{{name}}" 실패. 참고: Kilo는 아직 MCP 인증을 지원하지 않습니다.', "error.chain.providerAuthFailed": "공급자 인증 실패 ({{provider}}): {{message}}", "error.chain.providerInitFailed": '공급자 "{{provider}}" 초기화 실패. 자격 증명과 구성을 확인하세요.', "error.chain.configJsonInvalid": "{{path}}의 구성 파일이 유효한 JSON(C)가 아닙니다", @@ -542,19 +542,19 @@ export const dict = { "sidebar.workspaces.enable": "작업 공간 활성화", "sidebar.workspaces.disable": "작업 공간 비활성화", "sidebar.gettingStarted.title": "시작하기", - "sidebar.gettingStarted.line1": "OpenCode에는 무료 모델이 포함되어 있어 즉시 시작할 수 있습니다.", + "sidebar.gettingStarted.line1": "Kilo에는 무료 모델이 포함되어 있어 즉시 시작할 수 있습니다.", "sidebar.gettingStarted.line2": "Claude, GPT, Gemini 등을 포함한 모델을 사용하려면 공급자를 연결하세요.", "sidebar.project.recentSessions": "최근 세션", "sidebar.project.viewAllSessions": "모든 세션 보기", "sidebar.project.clearNotifications": "알림 지우기", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "데스크톱", "settings.section.server": "서버", "settings.tab.general": "일반", "settings.tab.shortcuts": "단축키", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL 통합", - "settings.desktop.wsl.description": "Windows의 WSL 내부에서 OpenCode 서버를 실행합니다.", + "settings.desktop.wsl.description": "Windows의 WSL 내부에서 Kilo 서버를 실행합니다.", "settings.general.section.appearance": "모양", "settings.general.section.notifications": "시스템 알림", "settings.general.section.updates": "업데이트", @@ -562,13 +562,13 @@ export const dict = { "settings.general.section.feed": "피드", "settings.general.section.display": "디스플레이", "settings.general.row.language.title": "언어", - "settings.general.row.language.description": "OpenCode 표시 언어 변경", + "settings.general.row.language.description": "Kilo 표시 언어 변경", "settings.general.row.appearance.title": "모양", - "settings.general.row.appearance.description": "기기에서 OpenCode가 보이는 방식 사용자 지정", + "settings.general.row.appearance.description": "기기에서 Kilo가 보이는 방식 사용자 지정", "settings.general.row.colorScheme.title": "색상 테마", - "settings.general.row.colorScheme.description": "OpenCode가 시스템, 라이트 또는 다크 테마를 따를지 선택하세요", + "settings.general.row.colorScheme.description": "Kilo가 시스템, 라이트 또는 다크 테마를 따를지 선택하세요", "settings.general.row.theme.title": "테마", - "settings.general.row.theme.description": "OpenCode 테마 사용자 지정", + "settings.general.row.theme.description": "Kilo 테마 사용자 지정", "settings.general.row.font.title": "코드 글꼴", "settings.general.row.font.description": "코드 블록과 터미널에 사용되는 글꼴을 사용자 지정", "settings.general.row.uiFont.title": "UI 글꼴", @@ -592,13 +592,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "릴리스 노트", "settings.general.row.releaseNotes.description": "업데이트 후 '새 소식' 팝업 표시", "settings.updates.row.startup.title": "시작 시 업데이트 확인", - "settings.updates.row.startup.description": "OpenCode를 실행할 때 업데이트를 자동으로 확인합니다", + "settings.updates.row.startup.description": "Kilo를 실행할 때 업데이트를 자동으로 확인합니다", "settings.updates.row.check.title": "업데이트 확인", "settings.updates.row.check.description": "업데이트를 수동으로 확인하고, 사용 가능하면 설치합니다", "settings.updates.action.checkNow": "지금 확인", "settings.updates.action.checking": "확인 중...", "settings.updates.toast.latest.title": "최신 상태입니다", - "settings.updates.toast.latest.description": "현재 최신 버전의 OpenCode를 사용 중입니다.", + "settings.updates.toast.latest.description": "현재 최신 버전의 Kilo를 사용 중입니다.", "sound.option.none": "없음", "sound.option.alert01": "알림 01", "sound.option.alert02": "알림 02", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index a0a968179c..a77f8b025a 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -118,7 +118,7 @@ export const dict = { "dialog.model.manage.description": "Tilpass hvilke modeller som vises i modellvelgeren.", "dialog.model.manage.provider.toggle": "Veksle alle {{provider}}-modeller", - "dialog.model.unpaid.freeModels.title": "Gratis modeller levert av OpenCode", + "dialog.model.unpaid.freeModels.title": "Gratis modeller levert av Kilo", "dialog.model.unpaid.addMore.title": "Legg til flere modeller fra populære leverandører", "dialog.provider.viewAll": "Vis flere leverandører", @@ -131,7 +131,7 @@ export const dict = { "provider.connect.status.waiting": "Venter på autorisering...", "provider.connect.status.failed": "Autorisering mislyktes: {{error}}", "provider.connect.apiKey.description": - "Skriv inn din {{provider}} API-nøkkel for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.", + "Skriv inn din {{provider}} API-nøkkel for å koble til kontoen din og bruke {{provider}}-modeller i Kilo.", "provider.connect.apiKey.label": "{{provider}} API-nøkkel", "provider.connect.apiKey.placeholder": "API-nøkkel", "provider.connect.apiKey.required": "API-nøkkel er påkrevd", @@ -145,7 +145,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Besøk ", "provider.connect.oauth.code.visit.link": "denne lenken", "provider.connect.oauth.code.visit.suffix": - " for å hente autorisasjonskoden din for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.", + " for å hente autorisasjonskoden din for å koble til kontoen din og bruke {{provider}}-modeller i Kilo.", "provider.connect.oauth.code.label": "{{method}} autorisasjonskode", "provider.connect.oauth.code.placeholder": "Autorisasjonskode", "provider.connect.oauth.code.required": "Autorisasjonskode er påkrevd", @@ -153,7 +153,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Besøk ", "provider.connect.oauth.auto.visit.link": "denne lenken", "provider.connect.oauth.auto.visit.suffix": - " og skriv inn koden nedenfor for å koble til kontoen din og bruke {{provider}}-modeller i OpenCode.", + " og skriv inn koden nedenfor for å koble til kontoen din og bruke {{provider}}-modeller i Kilo.", "provider.connect.oauth.auto.confirmationCode": "Bekreftelseskode", "provider.connect.toast.connected.title": "{{provider}} tilkoblet", "provider.connect.toast.connected.description": "{{provider}}-modeller er nå tilgjengelige.", @@ -310,7 +310,7 @@ export const dict = { "dialog.directory.empty": "Ingen mapper funnet", "dialog.server.title": "Servere", - "dialog.server.description": "Bytt hvilken OpenCode-server denne appen kobler til.", + "dialog.server.description": "Bytt hvilken Kilo-server denne appen kobler til.", "dialog.server.search.placeholder": "Søk etter servere", "dialog.server.empty": "Ingen servere ennå", "dialog.server.add.title": "Legg til en server", @@ -442,7 +442,7 @@ export const dict = { "toast.session.listFailed.title": "Kunne ikke laste sesjoner for {{project}}", "toast.update.title": "Oppdatering tilgjengelig", - "toast.update.description": "En ny versjon av OpenCode ({{version}}) er nå tilgjengelig for installasjon.", + "toast.update.description": "En ny versjon av Kilo ({{version}}) er nå tilgjengelig for installasjon.", "toast.update.action.installRestart": "Installer og start på nytt", "toast.update.action.notYet": "Ikke nå", @@ -453,7 +453,7 @@ export const dict = { "error.page.action.checking": "Sjekker...", "error.page.action.checkUpdates": "Se etter oppdateringer", "error.page.action.updateTo": "Oppdater til {{version}}", - "error.page.report.prefix": "Vennligst rapporter denne feilen til OpenCode-teamet", + "error.page.report.prefix": "Vennligst rapporter denne feilen til Kilo-teamet", "error.page.report.discord": "på Discord", "error.page.version": "Versjon: {{version}}", @@ -472,7 +472,7 @@ export const dict = { "error.chain.didYouMean": "Mente du: {{suggestions}}", "error.chain.modelNotFound": "Modell ikke funnet: {{provider}}/{{model}}", "error.chain.checkConfig": "Sjekk leverandør-/modellnavnene i konfigurasjonen din (opencode.json)", - "error.chain.mcpFailed": 'MCP-server "{{name}}" mislyktes. Merk at OpenCode ikke støtter MCP-autentisering ennå.', + "error.chain.mcpFailed": 'MCP-server "{{name}}" mislyktes. Merk at Kilo ikke støtter MCP-autentisering ennå.', "error.chain.providerAuthFailed": "Leverandørautentisering mislyktes ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Kunne ikke initialisere leverandør "{{provider}}". Sjekk legitimasjon og konfigurasjon.', @@ -608,13 +608,13 @@ export const dict = { "sidebar.workspaces.enable": "Aktiver arbeidsområder", "sidebar.workspaces.disable": "Deaktiver arbeidsområder", "sidebar.gettingStarted.title": "Kom i gang", - "sidebar.gettingStarted.line1": "OpenCode inkluderer gratis modeller så du kan starte umiddelbart.", + "sidebar.gettingStarted.line1": "Kilo inkluderer gratis modeller så du kan starte umiddelbart.", "sidebar.gettingStarted.line2": "Koble til en leverandør for å bruke modeller, inkl. Claude, GPT, Gemini osv.", "sidebar.project.recentSessions": "Nylige sesjoner", "sidebar.project.viewAllSessions": "Vis alle sesjoner", "sidebar.project.clearNotifications": "Fjern varsler", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Skrivebord", "settings.section.server": "Server", @@ -622,7 +622,7 @@ export const dict = { "settings.tab.shortcuts": "Snarveier", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL-integrasjon", - "settings.desktop.wsl.description": "Kjør OpenCode-serveren i WSL på Windows.", + "settings.desktop.wsl.description": "Kjør Kilo-serveren i WSL på Windows.", "settings.general.section.appearance": "Utseende", "settings.general.section.notifications": "Systemvarsler", @@ -632,13 +632,13 @@ export const dict = { "settings.general.section.display": "Skjerm", "settings.general.row.language.title": "Språk", - "settings.general.row.language.description": "Endre visningsspråket for OpenCode", + "settings.general.row.language.description": "Endre visningsspråket for Kilo", "settings.general.row.appearance.title": "Utseende", - "settings.general.row.appearance.description": "Tilpass hvordan OpenCode ser ut på enheten din", + "settings.general.row.appearance.description": "Tilpass hvordan Kilo ser ut på enheten din", "settings.general.row.colorScheme.title": "Fargevalg", - "settings.general.row.colorScheme.description": "Velg om OpenCode skal følge systemets, lyst eller mørkt tema", + "settings.general.row.colorScheme.description": "Velg om Kilo skal følge systemets, lyst eller mørkt tema", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.", + "settings.general.row.theme.description": "Tilpass hvordan Kilo er tematisert.", "settings.general.row.font.title": "Kodefont", "settings.general.row.font.description": "Tilpass skrifttypen som brukes i kodeblokker og terminaler", "settings.general.row.uiFont.title": "UI-skrift", @@ -663,13 +663,13 @@ export const dict = { "settings.general.row.releaseNotes.description": 'Vis "Hva er nytt"-vinduer etter oppdateringer', "settings.updates.row.startup.title": "Se etter oppdateringer ved oppstart", - "settings.updates.row.startup.description": "Se automatisk etter oppdateringer når OpenCode starter", + "settings.updates.row.startup.description": "Se automatisk etter oppdateringer når Kilo starter", "settings.updates.row.check.title": "Se etter oppdateringer", "settings.updates.row.check.description": "Se etter oppdateringer manuelt og installer hvis tilgjengelig", "settings.updates.action.checkNow": "Sjekk nå", "settings.updates.action.checking": "Sjekker...", "settings.updates.toast.latest.title": "Du er oppdatert", - "settings.updates.toast.latest.description": "Du bruker den nyeste versjonen av OpenCode.", + "settings.updates.toast.latest.description": "Du bruker den nyeste versjonen av Kilo.", "sound.option.none": "Ingen", "sound.option.alert01": "Varsel 01", "sound.option.alert02": "Varsel 02", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 88d209f11f..64170ee1d6 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -105,7 +105,7 @@ export const dict = { "dialog.model.manage": "Zarządzaj modelami", "dialog.model.manage.description": "Dostosuj, które modele pojawiają się w wyborze modelu.", "dialog.model.manage.provider.toggle": "Przełącz wszystkie modele {{provider}}", - "dialog.model.unpaid.freeModels.title": "Darmowe modele dostarczane przez OpenCode", + "dialog.model.unpaid.freeModels.title": "Darmowe modele dostarczane przez Kilo", "dialog.model.unpaid.addMore.title": "Dodaj więcej modeli od popularnych dostawców", "dialog.provider.viewAll": "Zobacz więcej dostawców", "provider.connect.title": "Połącz {{provider}}", @@ -116,7 +116,7 @@ export const dict = { "provider.connect.status.waiting": "Oczekiwanie na autoryzację...", "provider.connect.status.failed": "Autoryzacja nie powiodła się: {{error}}", "provider.connect.apiKey.description": - "Wprowadź swój klucz API {{provider}}, aby połączyć konto i używać modeli {{provider}} w OpenCode.", + "Wprowadź swój klucz API {{provider}}, aby połączyć konto i używać modeli {{provider}} w Kilo.", "provider.connect.apiKey.label": "Klucz API {{provider}}", "provider.connect.apiKey.placeholder": "Klucz API", "provider.connect.apiKey.required": "Klucz API jest wymagany", @@ -130,7 +130,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Odwiedź ", "provider.connect.oauth.code.visit.link": "ten link", "provider.connect.oauth.code.visit.suffix": - ", aby odebrać kod autoryzacyjny, połączyć konto i używać modeli {{provider}} w OpenCode.", + ", aby odebrać kod autoryzacyjny, połączyć konto i używać modeli {{provider}} w Kilo.", "provider.connect.oauth.code.label": "Kod autoryzacyjny {{method}}", "provider.connect.oauth.code.placeholder": "Kod autoryzacyjny", "provider.connect.oauth.code.required": "Kod autoryzacyjny jest wymagany", @@ -138,7 +138,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Odwiedź ", "provider.connect.oauth.auto.visit.link": "ten link", "provider.connect.oauth.auto.visit.suffix": - " i wprowadź poniższy kod, aby połączyć konto i używać modeli {{provider}} w OpenCode.", + " i wprowadź poniższy kod, aby połączyć konto i używać modeli {{provider}} w Kilo.", "provider.connect.oauth.auto.confirmationCode": "Kod potwierdzający", "provider.connect.toast.connected.title": "Połączono {{provider}}", "provider.connect.toast.connected.description": "Modele {{provider}} są teraz dostępne do użycia.", @@ -282,7 +282,7 @@ export const dict = { "dialog.directory.search.placeholder": "Szukaj folderów", "dialog.directory.empty": "Nie znaleziono folderów", "dialog.server.title": "Serwery", - "dialog.server.description": "Przełącz serwer OpenCode, z którym łączy się ta aplikacja.", + "dialog.server.description": "Przełącz serwer Kilo, z którym łączy się ta aplikacja.", "dialog.server.search.placeholder": "Szukaj serwerów", "dialog.server.empty": "Brak serwerów", "dialog.server.add.title": "Dodaj serwer", @@ -396,7 +396,7 @@ export const dict = { "toast.session.unshare.failed.description": "Wystąpił błąd podczas zatrzymywania udostępniania sesji", "toast.session.listFailed.title": "Nie udało się załadować sesji dla {{project}}", "toast.update.title": "Dostępna aktualizacja", - "toast.update.description": "Nowa wersja OpenCode ({{version}}) jest teraz dostępna do instalacji.", + "toast.update.description": "Nowa wersja Kilo ({{version}}) jest teraz dostępna do instalacji.", "toast.update.action.installRestart": "Zainstaluj i zrestartuj", "toast.update.action.notYet": "Jeszcze nie", "error.page.title": "Coś poszło nie tak", @@ -406,7 +406,7 @@ export const dict = { "error.page.action.checking": "Sprawdzanie...", "error.page.action.checkUpdates": "Sprawdź aktualizacje", "error.page.action.updateTo": "Zaktualizuj do {{version}}", - "error.page.report.prefix": "Proszę zgłosić ten błąd do zespołu OpenCode", + "error.page.report.prefix": "Proszę zgłosić ten błąd do zespołu Kilo", "error.page.report.discord": "na Discordzie", "error.page.version": "Wersja: {{version}}", "error.dev.rootNotFound": @@ -422,7 +422,7 @@ export const dict = { "error.chain.didYouMean": "Czy miałeś na myśli: {{suggestions}}", "error.chain.modelNotFound": "Model nie znaleziony: {{provider}}/{{model}}", "error.chain.checkConfig": "Sprawdź swoją konfigurację (opencode.json) nazwy dostawców/modeli", - "error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, OpenCode does not support MCP authentication yet.', + "error.chain.mcpFailed": 'MCP server "{{name}}" failed. Note, Kilo does not support MCP authentication yet.', "error.chain.providerAuthFailed": "Uwierzytelnianie dostawcy nie powiodło się ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Nie udało się zainicjować dostawcy "{{provider}}". Sprawdź poświadczenia i konfigurację.', @@ -542,19 +542,19 @@ export const dict = { "sidebar.workspaces.enable": "Włącz przestrzenie robocze", "sidebar.workspaces.disable": "Wyłącz przestrzenie robocze", "sidebar.gettingStarted.title": "Pierwsze kroki", - "sidebar.gettingStarted.line1": "OpenCode zawiera darmowe modele, więc możesz zacząć od razu.", + "sidebar.gettingStarted.line1": "Kilo zawiera darmowe modele, więc możesz zacząć od razu.", "sidebar.gettingStarted.line2": "Połącz dowolnego dostawcę, aby używać modeli, w tym Claude, GPT, Gemini itp.", "sidebar.project.recentSessions": "Ostatnie sesje", "sidebar.project.viewAllSessions": "Zobacz wszystkie sesje", "sidebar.project.clearNotifications": "Wyczyść powiadomienia", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Pulpit", "settings.section.server": "Serwer", "settings.tab.general": "Ogólne", "settings.tab.shortcuts": "Skróty", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL integration", - "settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.", + "settings.desktop.wsl.description": "Run the Kilo server inside WSL on Windows.", "settings.general.section.appearance": "Wygląd", "settings.general.section.notifications": "Powiadomienia systemowe", "settings.general.section.updates": "Aktualizacje", @@ -562,14 +562,14 @@ export const dict = { "settings.general.section.feed": "Kanał", "settings.general.section.display": "Ekran", "settings.general.row.language.title": "Język", - "settings.general.row.language.description": "Zmień język wyświetlania dla OpenCode", + "settings.general.row.language.description": "Zmień język wyświetlania dla Kilo", "settings.general.row.appearance.title": "Wygląd", - "settings.general.row.appearance.description": "Dostosuj wygląd OpenCode na swoim urządzeniu", + "settings.general.row.appearance.description": "Dostosuj wygląd Kilo na swoim urządzeniu", "settings.general.row.colorScheme.title": "Schemat kolorów", "settings.general.row.colorScheme.description": - "Wybierz, czy OpenCode ma używać motywu systemowego, jasnego czy ciemnego", + "Wybierz, czy Kilo ma używać motywu systemowego, jasnego czy ciemnego", "settings.general.row.theme.title": "Motyw", - "settings.general.row.theme.description": "Dostosuj motyw OpenCode.", + "settings.general.row.theme.description": "Dostosuj motyw Kilo.", "settings.general.row.font.title": "Czcionka kodu", "settings.general.row.font.description": "Dostosuj czcionkę używaną w blokach kodu i terminalach", "settings.general.row.uiFont.title": "Czcionka interfejsu", @@ -593,13 +593,13 @@ export const dict = { "settings.general.row.releaseNotes.title": "Informacje o wydaniu", "settings.general.row.releaseNotes.description": 'Pokazuj wyskakujące okna "Co nowego" po aktualizacjach', "settings.updates.row.startup.title": "Sprawdzaj aktualizacje przy uruchomieniu", - "settings.updates.row.startup.description": "Automatycznie sprawdzaj aktualizacje podczas uruchamiania OpenCode", + "settings.updates.row.startup.description": "Automatycznie sprawdzaj aktualizacje podczas uruchamiania Kilo", "settings.updates.row.check.title": "Sprawdź aktualizacje", "settings.updates.row.check.description": "Ręcznie sprawdź aktualizacje i zainstaluj, jeśli są dostępne", "settings.updates.action.checkNow": "Sprawdź teraz", "settings.updates.action.checking": "Sprawdzanie...", "settings.updates.toast.latest.title": "Masz najnowszą wersję", - "settings.updates.toast.latest.description": "Korzystasz z najnowszej wersji OpenCode.", + "settings.updates.toast.latest.description": "Korzystasz z najnowszej wersji Kilo.", "sound.option.none": "Brak", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 688289b7e8..89c9862951 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -115,7 +115,7 @@ export const dict = { "dialog.model.manage.description": "Настройте какие модели появляются в выборе модели", "dialog.model.manage.provider.toggle": "Переключить все модели {{provider}}", - "dialog.model.unpaid.freeModels.title": "Бесплатные модели от OpenCode", + "dialog.model.unpaid.freeModels.title": "Бесплатные модели от Kilo", "dialog.model.unpaid.addMore.title": "Добавьте больше моделей от популярных провайдеров", "dialog.provider.viewAll": "Показать больше провайдеров", @@ -128,7 +128,7 @@ export const dict = { "provider.connect.status.waiting": "Ожидание авторизации...", "provider.connect.status.failed": "Ошибка авторизации: {{error}}", "provider.connect.apiKey.description": - "Введите ваш API ключ {{provider}} для подключения аккаунта и использования моделей {{provider}} в OpenCode.", + "Введите ваш API ключ {{provider}} для подключения аккаунта и использования моделей {{provider}} в Kilo.", "provider.connect.apiKey.label": "{{provider}} API ключ", "provider.connect.apiKey.placeholder": "API ключ", "provider.connect.apiKey.required": "API ключ обязателен", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "Посетите ", "provider.connect.oauth.code.visit.link": "эту ссылку", "provider.connect.oauth.code.visit.suffix": - " чтобы получить код авторизации для подключения аккаунта и использования моделей {{provider}} в OpenCode.", + " чтобы получить код авторизации для подключения аккаунта и использования моделей {{provider}} в Kilo.", "provider.connect.oauth.code.label": "{{method}} код авторизации", "provider.connect.oauth.code.placeholder": "Код авторизации", "provider.connect.oauth.code.required": "Код авторизации обязателен", @@ -150,7 +150,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "Посетите ", "provider.connect.oauth.auto.visit.link": "эту ссылку", "provider.connect.oauth.auto.visit.suffix": - " и введите код ниже для подключения аккаунта и использования моделей {{provider}} в OpenCode.", + " и введите код ниже для подключения аккаунта и использования моделей {{provider}} в Kilo.", "provider.connect.oauth.auto.confirmationCode": "Код подтверждения", "provider.connect.toast.connected.title": "{{provider}} подключён", "provider.connect.toast.connected.description": "Модели {{provider}} теперь доступны.", @@ -307,7 +307,7 @@ export const dict = { "dialog.directory.empty": "Папки не найдены", "dialog.server.title": "Серверы", - "dialog.server.description": "Переключите сервер OpenCode к которому подключается приложение.", + "dialog.server.description": "Переключите сервер Kilo к которому подключается приложение.", "dialog.server.search.placeholder": "Поиск серверов", "dialog.server.empty": "Серверов пока нет", "dialog.server.add.title": "Добавить сервер", @@ -440,7 +440,7 @@ export const dict = { "toast.session.listFailed.title": "Не удалось загрузить сессии для {{project}}", "toast.update.title": "Доступно обновление", - "toast.update.description": "Новая версия OpenCode ({{version}}) доступна для установки.", + "toast.update.description": "Новая версия Kilo ({{version}}) доступна для установки.", "toast.update.action.installRestart": "Установить и перезапустить", "toast.update.action.notYet": "Пока нет", @@ -451,7 +451,7 @@ export const dict = { "error.page.action.checking": "Проверка...", "error.page.action.checkUpdates": "Проверить обновления", "error.page.action.updateTo": "Обновить до {{version}}", - "error.page.report.prefix": "Пожалуйста, сообщите об этой ошибке команде OpenCode", + "error.page.report.prefix": "Пожалуйста, сообщите об этой ошибке команде Kilo", "error.page.report.discord": "в Discord", "error.page.version": "Версия: {{version}}", @@ -471,7 +471,7 @@ export const dict = { "error.chain.modelNotFound": "Модель не найдена: {{provider}}/{{model}}", "error.chain.checkConfig": "Проверьте названия провайдера/модели в конфиге (opencode.json)", "error.chain.mcpFailed": - 'MCP сервер "{{name}}" завершился с ошибкой. Обратите внимание, что OpenCode пока не поддерживает MCP авторизацию.', + 'MCP сервер "{{name}}" завершился с ошибкой. Обратите внимание, что Kilo пока не поддерживает MCP авторизацию.', "error.chain.providerAuthFailed": "Ошибка аутентификации провайдера ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'Не удалось инициализировать провайдера "{{provider}}". Проверьте учётные данные и конфигурацию.', @@ -605,21 +605,21 @@ export const dict = { "sidebar.workspaces.enable": "Включить рабочие пространства", "sidebar.workspaces.disable": "Отключить рабочие пространства", "sidebar.gettingStarted.title": "Начало работы", - "sidebar.gettingStarted.line1": "OpenCode включает бесплатные модели, чтобы вы могли начать сразу.", + "sidebar.gettingStarted.line1": "Kilo включает бесплатные модели, чтобы вы могли начать сразу.", "sidebar.gettingStarted.line2": "Подключите любого провайдера для использования моделей, включая Claude, GPT, Gemini и др.", "sidebar.project.recentSessions": "Недавние сессии", "sidebar.project.viewAllSessions": "Посмотреть все сессии", "sidebar.project.clearNotifications": "Очистить уведомления", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "Приложение", "settings.section.server": "Сервер", "settings.tab.general": "Основные", "settings.tab.shortcuts": "Горячие клавиши", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "Интеграция с WSL", - "settings.desktop.wsl.description": "Запускать сервер OpenCode внутри WSL на Windows.", + "settings.desktop.wsl.description": "Запускать сервер Kilo внутри WSL на Windows.", "settings.general.section.appearance": "Внешний вид", "settings.general.section.notifications": "Системные уведомления", @@ -629,13 +629,13 @@ export const dict = { "settings.general.section.display": "Дисплей", "settings.general.row.language.title": "Язык", - "settings.general.row.language.description": "Изменить язык отображения OpenCode", + "settings.general.row.language.description": "Изменить язык отображения Kilo", "settings.general.row.appearance.title": "Внешний вид", - "settings.general.row.appearance.description": "Настройте как OpenCode выглядит на вашем устройстве", + "settings.general.row.appearance.description": "Настройте как Kilo выглядит на вашем устройстве", "settings.general.row.colorScheme.title": "Цветовая схема", - "settings.general.row.colorScheme.description": "Выберите, следует ли OpenCode системной, светлой или тёмной теме", + "settings.general.row.colorScheme.description": "Выберите, следует ли Kilo системной, светлой или тёмной теме", "settings.general.row.theme.title": "Тема", - "settings.general.row.theme.description": "Настройте оформление OpenCode.", + "settings.general.row.theme.description": "Настройте оформление Kilo.", "settings.general.row.font.title": "Шрифт кода", "settings.general.row.font.description": "Настройте шрифт, используемый в блоках кода и терминалах", "settings.general.row.uiFont.title": "Шрифт интерфейса", @@ -663,13 +663,13 @@ export const dict = { "settings.general.row.releaseNotes.description": 'Показывать всплывающие окна "Что нового" после обновлений', "settings.updates.row.startup.title": "Проверять обновления при запуске", - "settings.updates.row.startup.description": "Автоматически проверять обновления при запуске OpenCode", + "settings.updates.row.startup.description": "Автоматически проверять обновления при запуске Kilo", "settings.updates.row.check.title": "Проверить обновления", "settings.updates.row.check.description": "Проверить обновления вручную и установить, если доступны", "settings.updates.action.checkNow": "Проверить сейчас", "settings.updates.action.checking": "Проверка...", "settings.updates.toast.latest.title": "У вас последняя версия", - "settings.updates.toast.latest.description": "Вы используете последнюю версию OpenCode.", + "settings.updates.toast.latest.description": "Вы используете последнюю версию Kilo.", "sound.option.none": "Нет", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 5decf3adb5..4a6bffdcbb 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -115,7 +115,7 @@ export const dict = { "dialog.model.manage.description": "ปรับแต่งโมเดลที่จะปรากฏในตัวเลือกโมเดล", "dialog.model.manage.provider.toggle": "สลับโมเดลทั้งหมดของ {{provider}}", - "dialog.model.unpaid.freeModels.title": "โมเดลฟรีที่จัดหาให้โดย OpenCode", + "dialog.model.unpaid.freeModels.title": "โมเดลฟรีที่จัดหาให้โดย Kilo", "dialog.model.unpaid.addMore.title": "เพิ่มโมเดลเพิ่มเติมจากผู้ให้บริการยอดนิยม", "dialog.provider.viewAll": "แสดงผู้ให้บริการเพิ่มเติม", @@ -128,7 +128,7 @@ export const dict = { "provider.connect.status.waiting": "รอการอนุญาต...", "provider.connect.status.failed": "การอนุญาตล้มเหลว: {{error}}", "provider.connect.apiKey.description": - "ป้อนคีย์ API ของ {{provider}} เพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน OpenCode", + "ป้อนคีย์ API ของ {{provider}} เพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", "provider.connect.apiKey.label": "คีย์ API ของ {{provider}}", "provider.connect.apiKey.placeholder": "คีย์ API", "provider.connect.apiKey.required": "ต้องใช้คีย์ API", @@ -142,7 +142,7 @@ export const dict = { "provider.connect.oauth.code.visit.prefix": "เยี่ยมชม ", "provider.connect.oauth.code.visit.link": "ลิงก์นี้", "provider.connect.oauth.code.visit.suffix": - " เพื่อรวบรวมรหัสการอนุญาตของคุณเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน OpenCode", + " เพื่อรวบรวมรหัสการอนุญาตของคุณเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", "provider.connect.oauth.code.label": "รหัสการอนุญาต {{method}}", "provider.connect.oauth.code.placeholder": "รหัสการอนุญาต", "provider.connect.oauth.code.required": "ต้องใช้รหัสการอนุญาต", @@ -150,7 +150,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "เยี่ยมชม ", "provider.connect.oauth.auto.visit.link": "ลิงก์นี้", "provider.connect.oauth.auto.visit.suffix": - " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน OpenCode", + " และป้อนรหัสด้านล่างเพื่อเชื่อมต่อบัญชีและใช้โมเดล {{provider}} ใน Kilo", "provider.connect.oauth.auto.confirmationCode": "รหัสยืนยัน", "provider.connect.toast.connected.title": "{{provider}} ที่เชื่อมต่อแล้ว", "provider.connect.toast.connected.description": "โมเดล {{provider}} พร้อมใช้งานแล้ว", @@ -307,7 +307,7 @@ export const dict = { "dialog.directory.empty": "ไม่พบโฟลเดอร์", "dialog.server.title": "เซิร์ฟเวอร์", - "dialog.server.description": "สลับเซิร์ฟเวอร์ OpenCode ที่แอปนี้เชื่อมต่อด้วย", + "dialog.server.description": "สลับเซิร์ฟเวอร์ Kilo ที่แอปนี้เชื่อมต่อด้วย", "dialog.server.search.placeholder": "ค้นหาเซิร์ฟเวอร์", "dialog.server.empty": "ยังไม่มีเซิร์ฟเวอร์", "dialog.server.add.title": "เพิ่มเซิร์ฟเวอร์", @@ -439,7 +439,7 @@ export const dict = { "toast.session.listFailed.title": "ไม่สามารถโหลดเซสชันสำหรับ {{project}}", "toast.update.title": "มีการอัปเดต", - "toast.update.description": "เวอร์ชันใหม่ของ OpenCode ({{version}}) พร้อมใช้งานสำหรับติดตั้ง", + "toast.update.description": "เวอร์ชันใหม่ของ Kilo ({{version}}) พร้อมใช้งานสำหรับติดตั้ง", "toast.update.action.installRestart": "ติดตั้งและรีสตาร์ท", "toast.update.action.notYet": "ยังไม่", @@ -450,7 +450,7 @@ export const dict = { "error.page.action.checking": "กำลังตรวจสอบ...", "error.page.action.checkUpdates": "ตรวจสอบการอัปเดต", "error.page.action.updateTo": "อัปเดตเป็น {{version}}", - "error.page.report.prefix": "โปรดรายงานข้อผิดพลาดนี้ให้ทีม OpenCode", + "error.page.report.prefix": "โปรดรายงานข้อผิดพลาดนี้ให้ทีม Kilo", "error.page.report.discord": "บน Discord", "error.page.version": "เวอร์ชัน: {{version}}", @@ -468,7 +468,7 @@ export const dict = { "error.chain.didYouMean": "คุณหมายถึง: {{suggestions}}", "error.chain.modelNotFound": "ไม่พบโมเดล: {{provider}}/{{model}}", "error.chain.checkConfig": "ตรวจสอบการกำหนดค่าของคุณ (opencode.json) ชื่อผู้ให้บริการ/โมเดล", - "error.chain.mcpFailed": 'เซิร์ฟเวอร์ MCP "{{name}}" ล้มเหลว โปรดทราบว่า OpenCode ยังไม่รองรับการตรวจสอบสิทธิ์ MCP', + "error.chain.mcpFailed": 'เซิร์ฟเวอร์ MCP "{{name}}" ล้มเหลว โปรดทราบว่า Kilo ยังไม่รองรับการตรวจสอบสิทธิ์ MCP', "error.chain.providerAuthFailed": "การตรวจสอบสิทธิ์ผู้ให้บริการล้มเหลว ({{provider}}): {{message}}", "error.chain.providerInitFailed": 'ไม่สามารถเริ่มต้นผู้ให้บริการ "{{provider}}" ตรวจสอบข้อมูลรับรองและการกำหนดค่า', "error.chain.configJsonInvalid": "ไฟล์กำหนดค่าที่ {{path}} ไม่ใช่ JSON(C) ที่ถูกต้อง", @@ -599,13 +599,13 @@ export const dict = { "sidebar.workspaces.enable": "เปิดใช้งานพื้นที่ทำงาน", "sidebar.workspaces.disable": "ปิดใช้งานพื้นที่ทำงาน", "sidebar.gettingStarted.title": "เริ่มต้นใช้งาน", - "sidebar.gettingStarted.line1": "OpenCode รวมถึงโมเดลฟรีเพื่อให้คุณเริ่มต้นได้ทันที", + "sidebar.gettingStarted.line1": "Kilo รวมถึงโมเดลฟรีเพื่อให้คุณเริ่มต้นได้ทันที", "sidebar.gettingStarted.line2": "เชื่อมต่อผู้ให้บริการใด ๆ เพื่อใช้โมเดล รวมถึง Claude, GPT, Gemini ฯลฯ", "sidebar.project.recentSessions": "เซสชันล่าสุด", "sidebar.project.viewAllSessions": "ดูเซสชันทั้งหมด", "sidebar.project.clearNotifications": "ล้างการแจ้งเตือน", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "เดสก์ท็อป", "settings.section.server": "เซิร์ฟเวอร์", @@ -613,7 +613,7 @@ export const dict = { "settings.tab.shortcuts": "ทางลัด", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "การรวม WSL", - "settings.desktop.wsl.description": "เรียกใช้เซิร์ฟเวอร์ OpenCode ภายใน WSL บน Windows", + "settings.desktop.wsl.description": "เรียกใช้เซิร์ฟเวอร์ Kilo ภายใน WSL บน Windows", "settings.general.section.appearance": "รูปลักษณ์", "settings.general.section.notifications": "การแจ้งเตือนระบบ", @@ -623,13 +623,13 @@ export const dict = { "settings.general.section.display": "การแสดงผล", "settings.general.row.language.title": "ภาษา", - "settings.general.row.language.description": "เปลี่ยนภาษาที่แสดงสำหรับ OpenCode", + "settings.general.row.language.description": "เปลี่ยนภาษาที่แสดงสำหรับ Kilo", "settings.general.row.appearance.title": "รูปลักษณ์", - "settings.general.row.appearance.description": "ปรับแต่งวิธีการที่ OpenCode มีลักษณะบนอุปกรณ์ของคุณ", + "settings.general.row.appearance.description": "ปรับแต่งวิธีการที่ Kilo มีลักษณะบนอุปกรณ์ของคุณ", "settings.general.row.colorScheme.title": "โทนสี", - "settings.general.row.colorScheme.description": "เลือกว่าจะให้ OpenCode ใช้ธีมตามระบบ สว่าง หรือมืด", + "settings.general.row.colorScheme.description": "เลือกว่าจะให้ Kilo ใช้ธีมตามระบบ สว่าง หรือมืด", "settings.general.row.theme.title": "ธีม", - "settings.general.row.theme.description": "ปรับแต่งวิธีการที่ OpenCode มีธีม", + "settings.general.row.theme.description": "ปรับแต่งวิธีการที่ Kilo มีธีม", "settings.general.row.font.title": "ฟอนต์โค้ด", "settings.general.row.font.description": "ปรับแต่งฟอนต์ที่ใช้ในบล็อกโค้ดและเทอร์มินัล", "settings.general.row.uiFont.title": "ฟอนต์ UI", @@ -653,13 +653,13 @@ export const dict = { "settings.general.row.releaseNotes.description": "แสดงป๊อปอัพ What's New หลังจากอัปเดต", "settings.updates.row.startup.title": "ตรวจสอบการอัปเดตเมื่อเริ่มต้น", - "settings.updates.row.startup.description": "ตรวจสอบการอัปเดตโดยอัตโนมัติเมื่อ OpenCode เปิดใช้งาน", + "settings.updates.row.startup.description": "ตรวจสอบการอัปเดตโดยอัตโนมัติเมื่อ Kilo เปิดใช้งาน", "settings.updates.row.check.title": "ตรวจสอบการอัปเดต", "settings.updates.row.check.description": "ตรวจสอบการอัปเดตด้วยตนเองและติดตั้งหากมี", "settings.updates.action.checkNow": "ตรวจสอบทันที", "settings.updates.action.checking": "กำลังตรวจสอบ...", "settings.updates.toast.latest.title": "คุณเป็นเวอร์ชันล่าสุดแล้ว", - "settings.updates.toast.latest.description": "คุณกำลังใช้งาน OpenCode เวอร์ชันล่าสุด", + "settings.updates.toast.latest.description": "คุณกำลังใช้งาน Kilo เวอร์ชันล่าสุด", "sound.option.none": "ไม่มี", "sound.option.alert01": "เสียงเตือน 01", diff --git a/packages/app/src/i18n/tr.ts b/packages/app/src/i18n/tr.ts index 6a3ade0d0b..f6abb762f5 100644 --- a/packages/app/src/i18n/tr.ts +++ b/packages/app/src/i18n/tr.ts @@ -119,7 +119,7 @@ export const dict = { "dialog.model.manage.description": "Model seçicide hangi modellerin görüneceğini özelleştirin.", "dialog.model.manage.provider.toggle": "Tüm {{provider}} modellerini aç/kapat", - "dialog.model.unpaid.freeModels.title": "OpenCode tarafından sunulan ücretsiz modeller", + "dialog.model.unpaid.freeModels.title": "Kilo tarafından sunulan ücretsiz modeller", "dialog.model.unpaid.addMore.title": "Popüler sağlayıcılardan daha fazla model ekleyin", "dialog.provider.viewAll": "Daha fazla sağlayıcı göster", @@ -132,7 +132,7 @@ export const dict = { "provider.connect.status.waiting": "Yetkilendirme bekleniyor...", "provider.connect.status.failed": "Yetkilendirme başarısız: {{error}}", "provider.connect.apiKey.description": - "{{provider}} hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için {{provider}} API anahtarınızı girin.", + "{{provider}} hesabınızı bağlamak ve Kilo'da {{provider}} modellerini kullanmak için {{provider}} API anahtarınızı girin.", "provider.connect.apiKey.label": "{{provider}} API anahtarı", "provider.connect.apiKey.placeholder": "API anahtarı", "provider.connect.apiKey.required": "API anahtarı gerekli", @@ -144,7 +144,7 @@ export const dict = { "provider.connect.opencodeZen.visit.link": "opencode.ai/zen", "provider.connect.opencodeZen.visit.suffix": " adresini ziyaret ederek API anahtarınızı alın.", "provider.connect.oauth.code.visit.prefix": - "Hesabınızı bağlamak ve OpenCode'da {{provider}} modellerini kullanmak için ", + "Hesabınızı bağlamak ve Kilo'da {{provider}} modellerini kullanmak için ", "provider.connect.oauth.code.visit.link": "bu bağlantıya", "provider.connect.oauth.code.visit.suffix": " tıklayarak yetkilendirme kodunuzu alın.", "provider.connect.oauth.code.label": "{{method}} yetkilendirme kodu", @@ -154,7 +154,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "", "provider.connect.oauth.auto.visit.link": "Bu bağlantıya", "provider.connect.oauth.auto.visit.suffix": - " tıklayarak aşağıdaki kodu girin ve hesabınızı bağlayarak OpenCode'da {{provider}} modellerini kullanın.", + " tıklayarak aşağıdaki kodu girin ve hesabınızı bağlayarak Kilo'da {{provider}} modellerini kullanın.", "provider.connect.oauth.auto.confirmationCode": "Onay kodu", "provider.connect.toast.connected.title": "{{provider}} bağlandı", "provider.connect.toast.connected.description": "{{provider}} modelleri artık kullanımda.", @@ -312,7 +312,7 @@ export const dict = { "dialog.directory.empty": "Klasör bulunamadı", "dialog.server.title": "Sunucular", - "dialog.server.description": "Bu uygulamanın hangi OpenCode sunucusuna bağlanacağını değiştirin.", + "dialog.server.description": "Bu uygulamanın hangi Kilo sunucusuna bağlanacağını değiştirin.", "dialog.server.search.placeholder": "Sunucu ara", "dialog.server.empty": "Henüz sunucu yok", "dialog.server.add.title": "Sunucu ekle", @@ -444,7 +444,7 @@ export const dict = { "toast.session.listFailed.title": "{{project}} için oturumlar yüklenemedi", "toast.update.title": "Güncelleme mevcut", - "toast.update.description": "OpenCode'un yeni bir sürümü ({{version}}) yüklemeye hazır.", + "toast.update.description": "Kilo'un yeni bir sürümü ({{version}}) yüklemeye hazır.", "toast.update.action.installRestart": "Yükle ve yeniden başlat", "toast.update.action.notYet": "Şimdi değil", @@ -455,7 +455,7 @@ export const dict = { "error.page.action.checking": "Kontrol ediliyor...", "error.page.action.checkUpdates": "Güncellemeleri kontrol et", "error.page.action.updateTo": "{{version}} sürümüne güncelle", - "error.page.report.prefix": "Lütfen bu hatayı OpenCode ekibine bildirin", + "error.page.report.prefix": "Lütfen bu hatayı Kilo ekibine bildirin", "error.page.report.discord": "Discord üzerinden", "error.page.version": "Sürüm: {{version}}", @@ -475,7 +475,7 @@ export const dict = { "error.chain.modelNotFound": "Model bulunamadı: {{provider}}/{{model}}", "error.chain.checkConfig": "Yapılandırma dosyanızı (opencode.json) sağlayıcı/model adlarını kontrol edin", "error.chain.mcpFailed": - 'MCP sunucusu "{{name}}" başarısız oldu. Not: OpenCode henüz MCP kimlik doğrulamasını desteklemiyor.', + 'MCP sunucusu "{{name}}" başarısız oldu. Not: Kilo henüz MCP kimlik doğrulamasını desteklemiyor.', "error.chain.providerAuthFailed": "Sağlayıcı kimlik doğrulaması başarısız ({{provider}}): {{message}}", "error.chain.providerInitFailed": '"{{provider}}" sağlayıcısı başlatılamadı. Kimlik bilgilerini ve yapılandırmayı kontrol edin.', @@ -611,13 +611,13 @@ export const dict = { "sidebar.workspaces.enable": "Çalışma alanlarını etkinleştir", "sidebar.workspaces.disable": "Çalışma alanlarını devre dışı bırak", "sidebar.gettingStarted.title": "Başlarken", - "sidebar.gettingStarted.line1": "OpenCode ücretsiz modeller içerir, böylece hemen başlayabilirsiniz.", + "sidebar.gettingStarted.line1": "Kilo ücretsiz modeller içerir, böylece hemen başlayabilirsiniz.", "sidebar.gettingStarted.line2": "Claude, GPT, Gemini vb. modelleri kullanmak için herhangi bir sağlayıcı bağlayın.", "sidebar.project.recentSessions": "Son oturumlar", "sidebar.project.viewAllSessions": "Tüm oturumları görüntüle", "sidebar.project.clearNotifications": "Bildirimleri temizle", - "app.name.desktop": "OpenCode Masaüstü", + "app.name.desktop": "Kilo Masaüstü", "settings.section.desktop": "Masaüstü", "settings.section.server": "Sunucu", @@ -625,7 +625,7 @@ export const dict = { "settings.tab.shortcuts": "Kısayollar", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL entegrasyonu", - "settings.desktop.wsl.description": "OpenCode sunucusunu Windows'ta WSL içinde çalıştırın.", + "settings.desktop.wsl.description": "Kilo sunucusunu Windows'ta WSL içinde çalıştırın.", "settings.general.section.appearance": "Görünüm", "settings.general.section.notifications": "Sistem bildirimleri", @@ -635,14 +635,14 @@ export const dict = { "settings.general.section.display": "Ekran", "settings.general.row.language.title": "Dil", - "settings.general.row.language.description": "OpenCode'un görünüm dilini değiştirin", + "settings.general.row.language.description": "Kilo'un görünüm dilini değiştirin", "settings.general.row.appearance.title": "Görünüm", - "settings.general.row.appearance.description": "OpenCode'un cihazınızdaki görünümünü özelleştirin", + "settings.general.row.appearance.description": "Kilo'un cihazınızdaki görünümünü özelleştirin", "settings.general.row.colorScheme.title": "Renk şeması", "settings.general.row.colorScheme.description": - "OpenCode'un sistem, açık veya koyu temayı takip etip etmeyeceğini seçin", + "Kilo'un sistem, açık veya koyu temayı takip etip etmeyeceğini seçin", "settings.general.row.theme.title": "Tema", - "settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.", + "settings.general.row.theme.description": "Kilo'un temasını özelleştirin.", "settings.general.row.font.title": "Kod Yazı Tipi", "settings.general.row.font.description": "Kod bloklarında ve terminallerde kullanılan yazı tipini özelleştirin", "settings.general.row.uiFont.title": "Arayüz Yazı Tipi", @@ -671,13 +671,13 @@ export const dict = { "settings.general.row.releaseNotes.description": "Güncellemelerden sonra Yenilikler bildirimlerini göster", "settings.updates.row.startup.title": "Başlangıçta güncellemeleri kontrol et", - "settings.updates.row.startup.description": "OpenCode başladığında otomatik güncelleme kontrolü yap", + "settings.updates.row.startup.description": "Kilo başladığında otomatik güncelleme kontrolü yap", "settings.updates.row.check.title": "Güncellemeleri kontrol et", "settings.updates.row.check.description": "Elle güncelleme kontrolü yap ve varsa yükle", "settings.updates.action.checkNow": "Şimdi kontrol et", "settings.updates.action.checking": "Kontrol ediliyor...", "settings.updates.toast.latest.title": "Güncelsiniz", - "settings.updates.toast.latest.description": "OpenCode'un en son sürümünü kullanıyorsunuz.", + "settings.updates.toast.latest.description": "Kilo'un en son sürümünü kullanıyorsunuz.", "sound.option.none": "Yok", "sound.option.alert01": "Uyarı 01", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 28231733ea..840c5ac9e1 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -141,7 +141,7 @@ export const dict = { "dialog.model.manage": "管理模型", "dialog.model.manage.description": "自定义模型选择器中显示的模型。", "dialog.model.manage.provider.toggle": "切换所有 {{provider}} 模型", - "dialog.model.unpaid.freeModels.title": "OpenCode 提供的免费模型", + "dialog.model.unpaid.freeModels.title": "Kilo 提供的免费模型", "dialog.model.unpaid.addMore.title": "从热门提供商添加更多模型", "dialog.provider.viewAll": "查看更多提供商", @@ -154,7 +154,7 @@ export const dict = { "provider.connect.status.waiting": "等待授权...", "provider.connect.status.failed": "授权失败:{{error}}", "provider.connect.apiKey.description": - "输入你的 {{provider}} API 密钥以连接帐户,并在 OpenCode 中使用 {{provider}} 模型。", + "输入你的 {{provider}} API 密钥以连接帐户,并在 Kilo 中使用 {{provider}} 模型。", "provider.connect.apiKey.label": "{{provider}} API 密钥", "provider.connect.apiKey.placeholder": "API 密钥", "provider.connect.apiKey.required": "API 密钥为必填项", @@ -165,14 +165,14 @@ export const dict = { "provider.connect.opencodeZen.visit.suffix": " 获取你的 API 密钥。", "provider.connect.oauth.code.visit.prefix": "访问 ", "provider.connect.oauth.code.visit.link": "此链接", - "provider.connect.oauth.code.visit.suffix": " 获取授权码,以连接你的帐户并在 OpenCode 中使用 {{provider}} 模型。", + "provider.connect.oauth.code.visit.suffix": " 获取授权码,以连接你的帐户并在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.code.label": "{{method}} 授权码", "provider.connect.oauth.code.placeholder": "授权码", "provider.connect.oauth.code.required": "授权码为必填项", "provider.connect.oauth.code.invalid": "授权码无效", "provider.connect.oauth.auto.visit.prefix": "访问 ", "provider.connect.oauth.auto.visit.link": "此链接", - "provider.connect.oauth.auto.visit.suffix": " 并输入以下代码,以连接你的帐户并在 OpenCode 中使用 {{provider}} 模型。", + "provider.connect.oauth.auto.visit.suffix": " 并输入以下代码,以连接你的帐户并在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.auto.confirmationCode": "确认码", "provider.connect.toast.connected.title": "{{provider}} 已连接", "provider.connect.toast.connected.description": "现在可以使用 {{provider}} 模型了。", @@ -327,7 +327,7 @@ export const dict = { "dialog.directory.empty": "未找到文件夹", "dialog.server.title": "服务器", - "dialog.server.description": "切换此应用连接的 OpenCode 服务器。", + "dialog.server.description": "切换此应用连接的 Kilo 服务器。", "dialog.server.search.placeholder": "搜索服务器", "dialog.server.empty": "暂无服务器", "dialog.server.add.title": "添加服务器", @@ -444,7 +444,7 @@ export const dict = { "toast.session.unshare.failed.description": "取消分享会话时发生错误", "toast.session.listFailed.title": "无法加载 {{project}} 的会话", "toast.update.title": "有可用更新", - "toast.update.description": "OpenCode 有新版本 ({{version}}) 可安装。", + "toast.update.description": "Kilo 有新版本 ({{version}}) 可安装。", "toast.update.action.installRestart": "安装并重启", "toast.update.action.notYet": "稍后", @@ -455,7 +455,7 @@ export const dict = { "error.page.action.checking": "检查中...", "error.page.action.checkUpdates": "检查更新", "error.page.action.updateTo": "更新到 {{version}}", - "error.page.report.prefix": "请将此错误报告给 OpenCode 团队", + "error.page.report.prefix": "请将此错误报告给 Kilo 团队", "error.page.report.discord": "在 Discord 上", "error.page.version": "版本:{{version}}", "error.dev.rootNotFound": "未找到根元素。你是不是忘了把它添加到 index.html?或者 id 属性拼写错了?", @@ -472,7 +472,7 @@ export const dict = { "error.chain.didYouMean": "你是不是想输入:{{suggestions}}", "error.chain.modelNotFound": "未找到模型:{{provider}}/{{model}}", "error.chain.checkConfig": "请检查你的配置 (opencode.json) 中的 provider/model 名称", - "error.chain.mcpFailed": 'MCP 服务器 "{{name}}" 启动失败。注意: OpenCode 暂不支持 MCP 认证。', + "error.chain.mcpFailed": 'MCP 服务器 "{{name}}" 启动失败。注意: Kilo 暂不支持 MCP 认证。', "error.chain.providerAuthFailed": "提供商认证失败({{provider}}):{{message}}", "error.chain.providerInitFailed": '无法初始化提供商 "{{provider}}"。请检查凭据和配置。', "error.chain.configJsonInvalid": "配置文件 {{path}} 不是有效的 JSON(C)", @@ -598,13 +598,13 @@ export const dict = { "sidebar.workspaces.enable": "启用工作区", "sidebar.workspaces.disable": "禁用工作区", "sidebar.gettingStarted.title": "入门", - "sidebar.gettingStarted.line1": "OpenCode 提供免费模型,你可以立即开始使用。", + "sidebar.gettingStarted.line1": "Kilo 提供免费模型,你可以立即开始使用。", "sidebar.gettingStarted.line2": "连接任意提供商即可使用更多模型,如 Claude、GPT、Gemini 等。", "sidebar.project.recentSessions": "最近会话", "sidebar.project.viewAllSessions": "查看全部会话", "sidebar.project.clearNotifications": "清除通知", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "桌面", "settings.section.server": "服务器", @@ -614,7 +614,7 @@ export const dict = { "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL 集成", - "settings.desktop.wsl.description": "在 Windows 的 WSL 环境中运行 OpenCode 服务器。", + "settings.desktop.wsl.description": "在 Windows 的 WSL 环境中运行 Kilo 服务器。", "settings.general.section.appearance": "外观", "settings.general.section.notifications": "系统通知", @@ -623,13 +623,13 @@ export const dict = { "settings.general.section.feed": "动态", "settings.general.section.display": "显示", "settings.general.row.language.title": "语言", - "settings.general.row.language.description": "更改 OpenCode 的显示语言", + "settings.general.row.language.description": "更改 Kilo 的显示语言", "settings.general.row.appearance.title": "外观", - "settings.general.row.appearance.description": "自定义 OpenCode 在你的设备上的外观", + "settings.general.row.appearance.description": "自定义 Kilo 在你的设备上的外观", "settings.general.row.colorScheme.title": "配色方案", - "settings.general.row.colorScheme.description": "选择 OpenCode 跟随系统、浅色或深色主题", + "settings.general.row.colorScheme.description": "选择 Kilo 跟随系统、浅色或深色主题", "settings.general.row.theme.title": "主题", - "settings.general.row.theme.description": "自定义 OpenCode 的主题。", + "settings.general.row.theme.description": "自定义 Kilo 的主题。", "settings.general.row.font.title": "代码字体", "settings.general.row.font.description": "自定义代码块和终端使用的字体", "settings.general.row.uiFont.title": "界面字体", @@ -651,13 +651,13 @@ export const dict = { "settings.general.row.releaseNotes.description": "更新后显示“新功能”弹窗", "settings.updates.row.startup.title": "启动时检查更新", - "settings.updates.row.startup.description": "在 OpenCode 启动时自动检查更新", + "settings.updates.row.startup.description": "在 Kilo 启动时自动检查更新", "settings.updates.row.check.title": "检查更新", "settings.updates.row.check.description": "手动检查更新并在有更新时安装", "settings.updates.action.checkNow": "立即检查", "settings.updates.action.checking": "正在检查...", "settings.updates.toast.latest.title": "已是最新版本", - "settings.updates.toast.latest.description": "你正在使用最新版本的 OpenCode。", + "settings.updates.toast.latest.description": "你正在使用最新版本的 Kilo。", "sound.option.none": "无", "sound.option.alert01": "警报 01", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 4abdf5db57..dcd680c9b9 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -119,7 +119,7 @@ export const dict = { "dialog.model.manage.description": "自訂模型選擇器中顯示的模型。", "dialog.model.manage.provider.toggle": "切換所有 {{provider}} 模型", - "dialog.model.unpaid.freeModels.title": "OpenCode 提供的免費模型", + "dialog.model.unpaid.freeModels.title": "Kilo 提供的免費模型", "dialog.model.unpaid.addMore.title": "從熱門提供者新增更多模型", "dialog.provider.viewAll": "查看更多提供者", @@ -132,7 +132,7 @@ export const dict = { "provider.connect.status.waiting": "等待授權...", "provider.connect.status.failed": "授權失敗: {{error}}", "provider.connect.apiKey.description": - "輸入你的 {{provider}} API 金鑰以連線帳戶,並在 OpenCode 中使用 {{provider}} 模型。", + "輸入你的 {{provider}} API 金鑰以連線帳戶,並在 Kilo 中使用 {{provider}} 模型。", "provider.connect.apiKey.label": "{{provider}} API 金鑰", "provider.connect.apiKey.placeholder": "API 金鑰", "provider.connect.apiKey.required": "API 金鑰為必填", @@ -143,7 +143,7 @@ export const dict = { "provider.connect.opencodeZen.visit.suffix": " 取得你的 API 金鑰。", "provider.connect.oauth.code.visit.prefix": "造訪 ", "provider.connect.oauth.code.visit.link": "此連結", - "provider.connect.oauth.code.visit.suffix": " 取得授權碼,以連線你的帳戶並在 OpenCode 中使用 {{provider}} 模型。", + "provider.connect.oauth.code.visit.suffix": " 取得授權碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.code.label": "{{method}} 授權碼", "provider.connect.oauth.code.placeholder": "授權碼", "provider.connect.oauth.code.required": "授權碼為必填", @@ -151,7 +151,7 @@ export const dict = { "provider.connect.oauth.auto.visit.prefix": "造訪 ", "provider.connect.oauth.auto.visit.link": "此連結", "provider.connect.oauth.auto.visit.suffix": - " 並輸入以下程式碼,以連線你的帳戶並在 OpenCode 中使用 {{provider}} 模型。", + " 並輸入以下程式碼,以連線你的帳戶並在 Kilo 中使用 {{provider}} 模型。", "provider.connect.oauth.auto.confirmationCode": "確認碼", "provider.connect.toast.connected.title": "{{provider}} 已連線", "provider.connect.toast.connected.description": "現在可以使用 {{provider}} 模型了。", @@ -307,7 +307,7 @@ export const dict = { "dialog.directory.empty": "找不到資料夾", "dialog.server.title": "伺服器", - "dialog.server.description": "切換此應用程式連線的 OpenCode 伺服器。", + "dialog.server.description": "切換此應用程式連線的 Kilo 伺服器。", "dialog.server.search.placeholder": "搜尋伺服器", "dialog.server.empty": "暫無伺服器", "dialog.server.add.title": "新增伺服器", @@ -437,7 +437,7 @@ export const dict = { "toast.session.listFailed.title": "無法載入 {{project}} 的工作階段", "toast.update.title": "有可用更新", - "toast.update.description": "OpenCode 有新版本 ({{version}}) 可安裝。", + "toast.update.description": "Kilo 有新版本 ({{version}}) 可安裝。", "toast.update.action.installRestart": "安裝並重新啟動", "toast.update.action.notYet": "稍後", @@ -448,7 +448,7 @@ export const dict = { "error.page.action.checking": "檢查中...", "error.page.action.checkUpdates": "檢查更新", "error.page.action.updateTo": "更新到 {{version}}", - "error.page.report.prefix": "請將此錯誤回報給 OpenCode 團隊", + "error.page.report.prefix": "請將此錯誤回報給 Kilo 團隊", "error.page.report.discord": "在 Discord 上", "error.page.version": "版本: {{version}}", @@ -466,7 +466,7 @@ export const dict = { "error.chain.didYouMean": "你是不是想輸入: {{suggestions}}", "error.chain.modelNotFound": "找不到模型: {{provider}}/{{model}}", "error.chain.checkConfig": "請檢查你的設定 (opencode.json) 中的 provider/model 名稱", - "error.chain.mcpFailed": 'MCP 伺服器 "{{name}}" 啟動失敗。注意: OpenCode 暫不支援 MCP 認證。', + "error.chain.mcpFailed": 'MCP 伺服器 "{{name}}" 啟動失敗。注意: Kilo 暫不支援 MCP 認證。', "error.chain.providerAuthFailed": "提供者認證失敗 ({{provider}}): {{message}}", "error.chain.providerInitFailed": '無法初始化提供者 "{{provider}}"。請檢查憑證和設定。', "error.chain.configJsonInvalid": "設定檔 {{path}} 不是有效的 JSON(C)", @@ -595,20 +595,20 @@ export const dict = { "sidebar.workspaces.enable": "啟用工作區", "sidebar.workspaces.disable": "停用工作區", "sidebar.gettingStarted.title": "開始使用", - "sidebar.gettingStarted.line1": "OpenCode 提供免費模型,你可以立即開始使用。", + "sidebar.gettingStarted.line1": "Kilo 提供免費模型,你可以立即開始使用。", "sidebar.gettingStarted.line2": "連線任意提供者即可使用更多模型,如 Claude、GPT、Gemini 等。", "sidebar.project.recentSessions": "最近工作階段", "sidebar.project.viewAllSessions": "查看全部工作階段", "sidebar.project.clearNotifications": "清除通知", - "app.name.desktop": "OpenCode Desktop", + "app.name.desktop": "Kilo Desktop", "settings.section.desktop": "桌面", "settings.section.server": "伺服器", "settings.tab.general": "一般", "settings.tab.shortcuts": "快速鍵", "settings.desktop.section.wsl": "WSL", "settings.desktop.wsl.title": "WSL 整合", - "settings.desktop.wsl.description": "在 Windows 上的 WSL 中執行 OpenCode 伺服器。", + "settings.desktop.wsl.description": "在 Windows 上的 WSL 中執行 Kilo 伺服器。", "settings.general.section.appearance": "外觀", "settings.general.section.notifications": "系統通知", @@ -618,13 +618,13 @@ export const dict = { "settings.general.section.display": "顯示", "settings.general.row.language.title": "語言", - "settings.general.row.language.description": "變更 OpenCode 的顯示語言", + "settings.general.row.language.description": "變更 Kilo 的顯示語言", "settings.general.row.appearance.title": "外觀", - "settings.general.row.appearance.description": "自訂 OpenCode 在你的裝置上的外觀", + "settings.general.row.appearance.description": "自訂 Kilo 在你的裝置上的外觀", "settings.general.row.colorScheme.title": "配色方案", - "settings.general.row.colorScheme.description": "選擇 OpenCode 要跟隨系統、淺色或深色主題", + "settings.general.row.colorScheme.description": "選擇 Kilo 要跟隨系統、淺色或深色主題", "settings.general.row.theme.title": "主題", - "settings.general.row.theme.description": "自訂 OpenCode 的主題。", + "settings.general.row.theme.description": "自訂 Kilo 的主題。", "settings.general.row.font.title": "程式碼字型", "settings.general.row.font.description": "自訂程式碼區塊和終端機使用的字型", "settings.general.row.uiFont.title": "介面字型", @@ -648,13 +648,13 @@ export const dict = { "settings.general.row.releaseNotes.description": "更新後顯示「新功能」彈出視窗", "settings.updates.row.startup.title": "啟動時檢查更新", - "settings.updates.row.startup.description": "在 OpenCode 啟動時自動檢查更新", + "settings.updates.row.startup.description": "在 Kilo 啟動時自動檢查更新", "settings.updates.row.check.title": "檢查更新", "settings.updates.row.check.description": "手動檢查更新並在有更新時安裝", "settings.updates.action.checkNow": "立即檢查", "settings.updates.action.checking": "檢查中...", "settings.updates.toast.latest.title": "已是最新版本", - "settings.updates.toast.latest.description": "你正在使用最新版本的 OpenCode。", + "settings.updates.toast.latest.description": "你正在使用最新版本的 Kilo。", "sound.option.none": "無", "sound.option.alert01": "警報 01", diff --git a/packages/app/src/pages/error.tsx b/packages/app/src/pages/error.tsx index 1cdc061166..e34514926c 100644 --- a/packages/app/src/pages/error.tsx +++ b/packages/app/src/pages/error.tsx @@ -309,7 +309,7 @@ export const ErrorPage: Component = (props) => {