From eb553f53ac9689ab2056fceea0c7b0504f642101 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 15 Feb 2026 00:41:16 -0600 Subject: [PATCH 001/255] fix: ensure sqlite migration logs to stderr instead of stdout (#13691) --- packages/opencode/src/index.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 0c4fb5d19514..39e77782f599 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -82,14 +82,14 @@ const cli = yargs(hideBin(process.argv)) const marker = path.join(Global.Path.data, "opencode.db") if (!(await Bun.file(marker).exists())) { - console.log("Performing one time database migration, may take a few minutes...") - const tty = process.stdout.isTTY + const tty = process.stderr.isTTY + process.stderr.write("Performing one time database migration, may take a few minutes..." + EOL) const width = 36 const orange = "\x1b[38;5;214m" const muted = "\x1b[0;2m" const reset = "\x1b[0m" let last = -1 - if (tty) process.stdout.write("\x1b[?25l") + if (tty) process.stderr.write("\x1b[?25l") try { await JsonMigration.run(Database.Client().$client, { progress: (event) => { @@ -99,22 +99,22 @@ const cli = yargs(hideBin(process.argv)) if (tty) { const fill = Math.round((percent / 100) * width) const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}` - process.stdout.write( + process.stderr.write( `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.label.padEnd(12)} ${event.current}/${event.total}${reset}`, ) - if (event.current === event.total) process.stdout.write("\n") + if (event.current === event.total) process.stderr.write("\n") } else { - console.log(`sqlite-migration:${percent}`) + process.stderr.write(`sqlite-migration:${percent}${EOL}`) } }, }) } finally { - if (tty) process.stdout.write("\x1b[?25h") + if (tty) process.stderr.write("\x1b[?25h") else { - console.log(`sqlite-migration:done`) + process.stderr.write(`sqlite-migration:done${EOL}`) } } - console.log("Database migration complete.") + process.stderr.write("Database migration complete." + EOL) } }) .usage("\n" + UI.logo()) @@ -190,7 +190,7 @@ try { if (formatted) UI.error(formatted) if (formatted === undefined) { UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL) - console.error(e instanceof Error ? e.message : String(e)) + process.stderr.write((e instanceof Error ? e.message : String(e)) + EOL) } process.exitCode = 1 } finally { From 985c2a3d15c13512b9bb456882b97ebe863cae5f Mon Sep 17 00:00:00 2001 From: Brandon Julio Thenaro Date: Sun, 15 Feb 2026 20:44:21 +0700 Subject: [PATCH 002/255] feat: Add GeistMono Nerd Font to available mono font options (#13720) --- packages/app/src/components/settings-general.tsx | 1 + packages/app/src/context/settings.tsx | 1 + packages/app/src/i18n/ar.ts | 1 + packages/app/src/i18n/br.ts | 1 + packages/app/src/i18n/bs.ts | 1 + packages/app/src/i18n/da.ts | 1 + packages/app/src/i18n/de.ts | 1 + packages/app/src/i18n/en.ts | 1 + packages/app/src/i18n/es.ts | 1 + packages/app/src/i18n/fr.ts | 1 + packages/app/src/i18n/ja.ts | 1 + packages/app/src/i18n/ko.ts | 1 + packages/app/src/i18n/no.ts | 1 + packages/app/src/i18n/pl.ts | 1 + packages/app/src/i18n/ru.ts | 1 + packages/app/src/i18n/th.ts | 1 + packages/app/src/i18n/zh.ts | 1 + packages/app/src/i18n/zht.ts | 1 + packages/ui/src/components/font.tsx | 7 +++++++ 19 files changed, 25 insertions(+) diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index 439f542bb1cb..d5a0b813b6c2 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -128,6 +128,7 @@ export const SettingsGeneral: Component = () => { { value: "roboto-mono", label: "font.option.robotoMono" }, { value: "source-code-pro", label: "font.option.sourceCodePro" }, { value: "ubuntu-mono", label: "font.option.ubuntuMono" }, + { value: "geist-mono", label: "font.option.geistMono" }, ] as const const fontOptionsList = [...fontOptions] diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx index d72d4ceb1ec9..fbcd0a851845 100644 --- a/packages/app/src/context/settings.tsx +++ b/packages/app/src/context/settings.tsx @@ -85,6 +85,7 @@ const monoFonts: Record = { "roboto-mono": `"Roboto Mono Nerd Font", "RobotoMono Nerd Font", "RobotoMono Nerd Font Mono", "IBM Plex Mono", "IBM Plex Mono Fallback", ${monoFallback}`, "source-code-pro": `"Source Code Pro Nerd Font", "SauceCodePro Nerd Font", "SauceCodePro Nerd Font Mono", "IBM Plex Mono", "IBM Plex Mono Fallback", ${monoFallback}`, "ubuntu-mono": `"Ubuntu Mono Nerd Font", "UbuntuMono Nerd Font", "UbuntuMono Nerd Font Mono", "IBM Plex Mono", "IBM Plex Mono Fallback", ${monoFallback}`, + "geist-mono": `"GeistMono Nerd Font", "GeistMono Nerd Font Mono", "IBM Plex Mono", "IBM Plex Mono Fallback", ${monoFallback}`, } export function monoFontFamily(font: string | undefined) { diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 81cc92bf6de2..3d347c8423cb 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -557,6 +557,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "تنبيه 01", "sound.option.alert02": "تنبيه 02", "sound.option.alert03": "تنبيه 03", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 9ed3a9fc6fc1..730c01fdfffb 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -563,6 +563,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alerta 01", "sound.option.alert02": "Alerta 02", "sound.option.alert03": "Alerta 03", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 206aae3729da..d53c261126b7 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -631,6 +631,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Upozorenje 01", "sound.option.alert02": "Upozorenje 02", "sound.option.alert03": "Upozorenje 03", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 6bf67168fb08..9faa14d3da4b 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -627,6 +627,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alarm 01", "sound.option.alert02": "Alarm 02", "sound.option.alert03": "Alarm 03", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 4b6b43a57c00..d350af6cf55f 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -572,6 +572,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alarm 01", "sound.option.alert02": "Alarm 02", "sound.option.alert03": "Alarm 03", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index fd70f389ecfc..cb42b016f1fb 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -632,6 +632,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", "sound.option.alert03": "Alert 03", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 135a63fef742..c4ec378dcdd4 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -635,6 +635,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alerta 01", "sound.option.alert02": "Alerta 02", "sound.option.alert03": "Alerta 03", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 1ab0c72d53a5..7069fbd98fe1 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -571,6 +571,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alerte 01", "sound.option.alert02": "Alerte 02", "sound.option.alert03": "Alerte 03", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 6f092a60f683..e7e24a9bd68f 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -561,6 +561,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "アラート 01", "sound.option.alert02": "アラート 02", "sound.option.alert03": "アラート 03", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 4d814d43d0a3..650b7e662a36 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -562,6 +562,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "알림 01", "sound.option.alert02": "알림 02", "sound.option.alert03": "알림 03", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 63bc66acfcd2..afc162ab1765 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -634,6 +634,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Varsel 01", "sound.option.alert02": "Varsel 02", "sound.option.alert03": "Varsel 03", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 2a3ea7bfb183..d8572148a896 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -562,6 +562,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", "sound.option.alert03": "Alert 03", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 93e5b274253c..86d201cebcab 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -632,6 +632,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "Alert 01", "sound.option.alert02": "Alert 02", "sound.option.alert03": "Alert 03", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 3b3486b5c700..83020bf8c07b 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -626,6 +626,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "เสียงเตือน 01", "sound.option.alert02": "เสียงเตือน 02", "sound.option.alert03": "เสียงเตือน 03", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 6489b7025425..d0bf86cbba6d 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -623,6 +623,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "警报 01", "sound.option.alert02": "警报 02", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index a01b76c0521b..349c90b0e111 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -621,6 +621,7 @@ export const dict = { "font.option.robotoMono": "Roboto Mono", "font.option.sourceCodePro": "Source Code Pro", "font.option.ubuntuMono": "Ubuntu Mono", + "font.option.geistMono": "Geist Mono", "sound.option.alert01": "警報 01", "sound.option.alert02": "警報 02", "sound.option.alert03": "警報 03", diff --git a/packages/ui/src/components/font.tsx b/packages/ui/src/components/font.tsx index fa4b6d8c0158..bcb8863c89a0 100644 --- a/packages/ui/src/components/font.tsx +++ b/packages/ui/src/components/font.tsx @@ -26,6 +26,8 @@ import ubuntuMono from "../assets/fonts/ubuntu-mono-nerd-font.woff2" import ubuntuMonoBold from "../assets/fonts/ubuntu-mono-nerd-font-bold.woff2" import iosevka from "../assets/fonts/iosevka-nerd-font.woff2" import iosevkaBold from "../assets/fonts/iosevka-nerd-font-bold.woff2" +import geistMono from "../assets/fonts/GeistMonoNerdFontMono-Regular.woff2" +import geistMonoBold from "../assets/fonts/GeistMonoNerdFontMono-Bold.woff2" type MonoFont = { family: string @@ -89,6 +91,11 @@ export const MONO_NERD_FONTS = [ regular: iosevka, bold: iosevkaBold, }, + { + family: "GeistMono Nerd Font", + regular: geistMono, + bold: geistMonoBold, + }, ] satisfies MonoFont[] const monoNerdCss = MONO_NERD_FONTS.map( From 3aaa34be1efe2e202312fe1312605c4cdac2e115 Mon Sep 17 00:00:00 2001 From: zerone0x Date: Sun, 15 Feb 2026 21:45:34 +0800 Subject: [PATCH 003/255] fix(desktop): focus window after update/relaunch (#13701) --- packages/desktop/src-tauri/src/windows.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/desktop/src-tauri/src/windows.rs b/packages/desktop/src-tauri/src/windows.rs index 2ddcb0506d8f..056720055b55 100644 --- a/packages/desktop/src-tauri/src/windows.rs +++ b/packages/desktop/src-tauri/src/windows.rs @@ -22,6 +22,8 @@ impl MainWindow { pub fn create(app: &AppHandle) -> Result { if let Some(window) = app.get_webview_window(Self::LABEL) { + let _ = window.set_focus(); + let _ = window.unminimize(); return Ok(Self(window)); } @@ -50,6 +52,9 @@ impl MainWindow { let window = window_builder.build()?; + // Ensure window is focused after creation (e.g., after update/relaunch) + let _ = window.set_focus(); + setup_window_state_listener(app, &window); #[cfg(windows)] From 37611217282b81458bcd5a74850bd96787721b06 Mon Sep 17 00:00:00 2001 From: Denys Date: Sun, 15 Feb 2026 14:46:19 +0100 Subject: [PATCH 004/255] docs: add Ukrainian README translation (#13697) --- README.ar.md | 3 +- README.br.md | 3 +- README.bs.md | 3 +- README.da.md | 3 +- README.de.md | 3 +- README.es.md | 3 +- README.fr.md | 3 +- README.it.md | 3 +- README.ja.md | 3 +- README.ko.md | 3 +- README.md | 3 +- README.no.md | 3 +- README.pl.md | 3 +- README.ru.md | 3 +- README.th.md | 3 +- README.tr.md | 3 +- README.uk.md | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.zh.md | 3 +- README.zht.md | 3 +- 19 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 README.uk.md diff --git a/README.ar.md b/README.ar.md index edac204a2851..f24e598d5eb9 100644 --- a/README.ar.md +++ b/README.ar.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.br.md b/README.br.md index c185603efbbd..4802c4996f63 100644 --- a/README.br.md +++ b/README.br.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.bs.md b/README.bs.md index d64a69c0d765..9ad6852018c0 100644 --- a/README.bs.md +++ b/README.bs.md @@ -32,7 +32,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.da.md b/README.da.md index 7f3d5aa5ddec..4b1302dbc3c2 100644 --- a/README.da.md +++ b/README.da.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.de.md b/README.de.md index 2aa78657ca9e..16116dc72f23 100644 --- a/README.de.md +++ b/README.de.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.es.md b/README.es.md index 2b80427ab247..5c18ff4aca7c 100644 --- a/README.es.md +++ b/README.es.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.fr.md b/README.fr.md index bc3fe9e75798..0382164bedc5 100644 --- a/README.fr.md +++ b/README.fr.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.it.md b/README.it.md index 6da7d51fc684..c966ccec4916 100644 --- a/README.it.md +++ b/README.it.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.ja.md b/README.ja.md index 7a0bbb08f38c..11109e7eb408 100644 --- a/README.ja.md +++ b/README.ja.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.ko.md b/README.ko.md index 1c931c31f3c0..23fea76b1ebd 100644 --- a/README.ko.md +++ b/README.ko.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.md b/README.md index bd01fc94e8f1..99b4b2c50ff9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.no.md b/README.no.md index 092316bae1b9..9b9e90dc3850 100644 --- a/README.no.md +++ b/README.no.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.pl.md b/README.pl.md index a225d82539ed..fced98dfc3a1 100644 --- a/README.pl.md +++ b/README.pl.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.ru.md b/README.ru.md index c13f039d1689..a7c590c16b7c 100644 --- a/README.ru.md +++ b/README.ru.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.th.md b/README.th.md index ba2db8a850a8..0999167f239c 100644 --- a/README.th.md +++ b/README.th.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.tr.md b/README.tr.md index 635a5782fef1..67f84e4ddbce 100644 --- a/README.tr.md +++ b/README.tr.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.uk.md b/README.uk.md new file mode 100644 index 000000000000..77e859a45d73 --- /dev/null +++ b/README.uk.md @@ -0,0 +1,139 @@ +

+ + + + + OpenCode logo + + +

+

AI-агент для програмування з відкритим кодом.

+

+ Discord + npm + Build status +

+ +

+ English | + 简体中文 | + 繁體中文 | + 한국어 | + Deutsch | + Español | + Français | + Italiano | + Dansk | + 日本語 | + Polski | + Русский | + Bosanski | + العربية | + Norsk | + Português (Brasil) | + ไทย | + Türkçe | + Українська +

+ +[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) + +--- + +### Встановлення + +```bash +# YOLO +curl -fsSL https://opencode.ai/install | bash + +# Менеджери пакетів +npm i -g opencode-ai@latest # або bun/pnpm/yarn +scoop install opencode # Windows +choco install opencode # Windows +brew install anomalyco/tap/opencode # macOS і Linux (рекомендовано, завжди актуально) +brew install opencode # macOS і Linux (офіційна формула Homebrew, оновлюється рідше) +sudo pacman -S opencode # Arch Linux (Stable) +paru -S opencode-bin # Arch Linux (Latest from AUR) +mise use -g opencode # Будь-яка ОС +nix run nixpkgs#opencode # або github:anomalyco/opencode для найновішої dev-гілки +``` + +> [!TIP] +> Перед встановленням видаліть версії старші за 0.1.x. + +### Десктопний застосунок (BETA) + +OpenCode також доступний як десктопний застосунок. Завантажуйте напряму зі [сторінки релізів](https://github.com/anomalyco/opencode/releases) або [opencode.ai/download](https://opencode.ai/download). + +| Платформа | Завантаження | +| --------------------- | ------------------------------------- | +| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` | +| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` | +| Windows | `opencode-desktop-windows-x64.exe` | +| Linux | `.deb`, `.rpm` або AppImage | + +```bash +# macOS (Homebrew) +brew install --cask opencode-desktop +# Windows (Scoop) +scoop bucket add extras; scoop install extras/opencode-desktop +``` + +#### Каталог встановлення + +Скрипт встановлення дотримується такого порядку пріоритету для шляху встановлення: + +1. `$OPENCODE_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 +XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash +``` + +### Агенти + +OpenCode містить два вбудовані агенти, між якими можна перемикатися клавішею `Tab`. + +- **build** - Агент за замовчуванням із повним доступом для завдань розробки +- **plan** - Агент лише для читання для аналізу та дослідження коду + - За замовчуванням забороняє редагування файлів + - Запитує дозвіл перед запуском bash-команд + - Ідеально підходить для дослідження незнайомих кодових баз або планування змін + +Також доступний допоміжний агент **general** для складного пошуку та багатокрокових завдань. +Він використовується всередині системи й може бути викликаний у повідомленнях через `@general`. + +Дізнайтеся більше про [agents](https://opencode.ai/docs/agents). + +### Документація + +Щоб дізнатися більше про налаштування OpenCode, [**перейдіть до нашої документації**](https://opencode.ai/docs). + +### Внесок + +Якщо ви хочете зробити внесок в OpenCode, будь ласка, прочитайте нашу [документацію для контриб'юторів](./CONTRIBUTING.md) перед надсиланням pull request. + +### Проєкти на базі OpenCode + +Якщо ви працюєте над проєктом, пов'язаним з OpenCode, і використовуєте "opencode" у назві, наприклад "opencode-dashboard" або "opencode-mobile", додайте примітку до свого README. +Уточніть, що цей проєкт не створений командою OpenCode і жодним чином не афілійований із нами. + +### FAQ + +#### Чим це відрізняється від Claude Code? + +За можливостями це дуже схоже на Claude Code. Ось ключові відмінності: + +- 100% open source +- Немає прив'язки до конкретного провайдера. Ми рекомендуємо моделі, які надаємо через [OpenCode Zen](https://opencode.ai/zen), але OpenCode також працює з Claude, OpenAI, Google і навіть локальними моделями. З розвитком моделей різниця між ними зменшуватиметься, а ціни падатимуть, тому незалежність від провайдера має значення. +- Підтримка LSP з коробки +- Фокус на TUI. OpenCode створено користувачами neovim та авторами [terminal.shop](https://terminal.shop); ми й надалі розширюватимемо межі можливого в терміналі. +- Клієнт-серверна архітектура. Наприклад, це дає змогу запускати OpenCode на вашому комп'ютері й керувати ним віддалено з мобільного застосунку, тобто TUI-фронтенд - лише один із можливих клієнтів. + +--- + +**Приєднуйтеся до нашої спільноти** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode) diff --git a/README.zh.md b/README.zh.md index b2f288f5ba5c..113d476b2ed3 100644 --- a/README.zh.md +++ b/README.zh.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) diff --git a/README.zht.md b/README.zht.md index be4ef053c012..b5181044438d 100644 --- a/README.zht.md +++ b/README.zht.md @@ -31,7 +31,8 @@ Norsk | Português (Brasil) | ไทย | - Türkçe + Türkçe | + Українська

[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) From 878ddc6a0a9eff4fe990dfc241a8eb1c72f0659d Mon Sep 17 00:00:00 2001 From: Filip <34747899+neriousy@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:46:56 +0100 Subject: [PATCH 005/255] fix(app): keybind [shift+tab] (#13695) --- packages/app/src/context/command.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/app/src/context/command.tsx b/packages/app/src/context/command.tsx index 237d7188467a..03437c973597 100644 --- a/packages/app/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -316,8 +316,10 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex const isPalette = palette().has(sig) const option = keymap().get(sig) const modified = event.ctrlKey || event.metaKey || event.altKey + const isTab = event.key === "Tab" - if (isEditableTarget(event.target) && !isPalette && !isAllowedEditableKeybind(option?.id) && !modified) return + if (isEditableTarget(event.target) && !isPalette && !isAllowedEditableKeybind(option?.id) && !modified && !isTab) + return if (isPalette) { event.preventDefault() From 3c85cf4fac596928713685068c6c92f356b848f3 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Sun, 15 Feb 2026 19:17:19 +0530 Subject: [PATCH 006/255] fix(app): only navigate prompt history at input boundaries (#13690) --- packages/app/src/components/prompt-input.tsx | 22 ++----------------- .../components/prompt-input/history.test.ts | 15 ++++++++++--- .../src/components/prompt-input/history.ts | 10 +++++---- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 8e8c3c895b47..984888c35d87 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -911,31 +911,13 @@ export const PromptInput: Component = (props) => { if (!collapsed) return const cursorPosition = getCursorPosition(editorRef) - const textLength = promptLength(prompt.current()) const textContent = prompt .current() .map((part) => ("content" in part ? part.content : "")) .join("") const direction = event.key === "ArrowUp" ? "up" : "down" - if (!canNavigateHistoryAtCursor(direction, textContent, cursorPosition)) return - const isEmpty = textContent.trim() === "" || textLength <= 1 - const hasNewlines = textContent.includes("\n") - const inHistory = store.historyIndex >= 0 - const atStart = cursorPosition <= (isEmpty ? 1 : 0) - const atEnd = cursorPosition >= (isEmpty ? textLength - 1 : textLength) - const allowUp = isEmpty || atStart || (!hasNewlines && !inHistory) || (inHistory && atEnd) - const allowDown = isEmpty || atEnd || (!hasNewlines && !inHistory) || (inHistory && atStart) - - if (direction === "up") { - if (!allowUp) return - if (navigateHistory("up")) { - event.preventDefault() - } - return - } - - if (!allowDown) return - if (navigateHistory("down")) { + if (!canNavigateHistoryAtCursor(direction, textContent, cursorPosition, store.historyIndex >= 0)) return + if (navigateHistory(direction)) { event.preventDefault() } return diff --git a/packages/app/src/components/prompt-input/history.test.ts b/packages/app/src/components/prompt-input/history.test.ts index a37fdad6777a..b7a4f896b886 100644 --- a/packages/app/src/components/prompt-input/history.test.ts +++ b/packages/app/src/components/prompt-input/history.test.ts @@ -73,7 +73,7 @@ describe("prompt-input history", () => { expect(original[1].selection?.startLine).toBe(1) }) - test("canNavigateHistoryAtCursor only allows multiline boundaries", () => { + test("canNavigateHistoryAtCursor only allows prompt boundaries", () => { const value = "a\nb\nc" expect(canNavigateHistoryAtCursor("up", value, 0)).toBe(true) @@ -85,7 +85,16 @@ describe("prompt-input history", () => { expect(canNavigateHistoryAtCursor("up", value, 5)).toBe(false) expect(canNavigateHistoryAtCursor("down", value, 5)).toBe(true) - expect(canNavigateHistoryAtCursor("up", "abc", 1)).toBe(true) - expect(canNavigateHistoryAtCursor("down", "abc", 1)).toBe(true) + expect(canNavigateHistoryAtCursor("up", "abc", 0)).toBe(true) + expect(canNavigateHistoryAtCursor("down", "abc", 3)).toBe(true) + expect(canNavigateHistoryAtCursor("up", "abc", 1)).toBe(false) + expect(canNavigateHistoryAtCursor("down", "abc", 1)).toBe(false) + + expect(canNavigateHistoryAtCursor("up", "abc", 0, true)).toBe(true) + expect(canNavigateHistoryAtCursor("up", "abc", 3, true)).toBe(true) + expect(canNavigateHistoryAtCursor("down", "abc", 0, true)).toBe(true) + expect(canNavigateHistoryAtCursor("down", "abc", 3, true)).toBe(true) + expect(canNavigateHistoryAtCursor("up", "abc", 1, true)).toBe(false) + expect(canNavigateHistoryAtCursor("down", "abc", 1, true)).toBe(false) }) }) diff --git a/packages/app/src/components/prompt-input/history.ts b/packages/app/src/components/prompt-input/history.ts index f26f80848718..c279a3ed563b 100644 --- a/packages/app/src/components/prompt-input/history.ts +++ b/packages/app/src/components/prompt-input/history.ts @@ -4,11 +4,13 @@ const DEFAULT_PROMPT: Prompt = [{ type: "text", content: "", start: 0, end: 0 }] export const MAX_HISTORY = 100 -export function canNavigateHistoryAtCursor(direction: "up" | "down", text: string, cursor: number) { - if (!text.includes("\n")) return true +export function canNavigateHistoryAtCursor(direction: "up" | "down", text: string, cursor: number, inHistory = false) { const position = Math.max(0, Math.min(cursor, text.length)) - if (direction === "up") return !text.slice(0, position).includes("\n") - return !text.slice(position).includes("\n") + const atStart = position === 0 + const atEnd = position === text.length + if (inHistory) return atStart || atEnd + if (direction === "up") return position === 0 + return position === text.length } export function clonePromptParts(prompt: Prompt): Prompt { From cf50a289db056657171b73fb5e1f907b0baedd59 Mon Sep 17 00:00:00 2001 From: Shane Bishop <71288697+shanebishop1@users.noreply.github.com> Date: Sun, 15 Feb 2026 07:48:40 -0800 Subject: [PATCH 007/255] fix(desktop): issue viewing new files opened from the file tree (#13689) --- packages/app/src/pages/session.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 5ce6202eef9b..41646d2f6ce1 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -272,6 +272,7 @@ export default function Page() { if (!path) return file.load(path) openReviewPanel() + tabs().setActive(next) } createEffect(() => { From 3a3aa300bb846ae60391ba96c5f1f4aa9a9a5d74 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk <34632190+alexyaroshuk@users.noreply.github.com> Date: Mon, 16 Feb 2026 00:40:09 +0800 Subject: [PATCH 008/255] feat(app): localize "free usage exceeded" error & "Add credits" clickable link (#13652) --- packages/ui/src/components/session-turn.css | 6 ++++++ packages/ui/src/components/session-turn.tsx | 19 ++++++++++++++++++- packages/ui/src/i18n/ar.ts | 2 ++ packages/ui/src/i18n/br.ts | 2 ++ packages/ui/src/i18n/bs.ts | 2 ++ packages/ui/src/i18n/da.ts | 2 ++ packages/ui/src/i18n/de.ts | 2 ++ packages/ui/src/i18n/en.ts | 2 ++ packages/ui/src/i18n/es.ts | 2 ++ packages/ui/src/i18n/fr.ts | 2 ++ packages/ui/src/i18n/ja.ts | 2 ++ packages/ui/src/i18n/ko.ts | 2 ++ packages/ui/src/i18n/no.ts | 2 ++ packages/ui/src/i18n/pl.ts | 2 ++ packages/ui/src/i18n/ru.ts | 2 ++ packages/ui/src/i18n/th.ts | 2 ++ packages/ui/src/i18n/zh.ts | 2 ++ packages/ui/src/i18n/zht.ts | 2 ++ 18 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 9887ce2fc6a2..414e8a359009 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -560,6 +560,12 @@ overflow-y: auto; } + .retry-error-link, + .error-card-link { + color: var(--text-strong); + text-decoration: underline; + } + [data-slot="session-turn-collapsible-content-inner"] { width: 100%; min-width: 0; diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 9ffa671e6985..c03622105916 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -436,6 +436,11 @@ export function SessionTurn( if (s.type !== "retry") return return s }) + const isRetryFreeUsageLimitError = createMemo(() => { + const r = retry() + if (!r) return false + return r.message.includes("Free usage exceeded") + }) const response = createMemo(() => lastTextPart()?.text) const responsePartId = createMemo(() => lastTextPart()?.id) @@ -691,10 +696,22 @@ export function SessionTurn( {(() => { const r = retry() if (!r) return "" - const msg = unwrap(r.message) + const msg = isRetryFreeUsageLimitError() + ? i18n.t("ui.sessionTurn.error.freeUsageExceeded") + : unwrap(r.message) return msg.length > 60 ? msg.slice(0, 60) + "..." : msg })()} + + + {i18n.t("ui.sessionTurn.error.addCredits")} + + · {i18n.t("ui.sessionTurn.retry.retrying")} {store.retrySeconds > 0 diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts index 9a6c8dcbd050..4a1525d468c0 100644 --- a/packages/ui/src/i18n/ar.ts +++ b/packages/ui/src/i18n/ar.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "إعادة المحاولة", "ui.sessionTurn.retry.inSeconds": "خلال {{seconds}} ثواني", + "ui.sessionTurn.error.freeUsageExceeded": "تم تجاوز حد الاستخدام المجاني", + "ui.sessionTurn.error.addCredits": "إضافة رصيد", "ui.sessionTurn.status.delegating": "تفويض العمل", "ui.sessionTurn.status.planning": "تخطيط الخطوات التالية", diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts index 148b0ae17419..160d07aee217 100644 --- a/packages/ui/src/i18n/br.ts +++ b/packages/ui/src/i18n/br.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "tentando novamente", "ui.sessionTurn.retry.inSeconds": "em {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Limite de uso gratuito excedido", + "ui.sessionTurn.error.addCredits": "Adicionar créditos", "ui.sessionTurn.status.delegating": "Delegando trabalho", "ui.sessionTurn.status.planning": "Planejando próximos passos", diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts index 7614af087f90..9a049c14bc75 100644 --- a/packages/ui/src/i18n/bs.ts +++ b/packages/ui/src/i18n/bs.ts @@ -32,6 +32,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "ponovni pokušaj", "ui.sessionTurn.retry.inSeconds": "za {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Besplatna upotreba premašena", + "ui.sessionTurn.error.addCredits": "Dodaj kredite", "ui.sessionTurn.status.delegating": "Delegiranje posla", "ui.sessionTurn.status.planning": "Planiranje sljedećih koraka", diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts index 2f49a94344cf..de0e854be9e7 100644 --- a/packages/ui/src/i18n/da.ts +++ b/packages/ui/src/i18n/da.ts @@ -27,6 +27,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "prøver igen", "ui.sessionTurn.retry.inSeconds": "om {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Gratis forbrug overskredet", + "ui.sessionTurn.error.addCredits": "Tilføj kreditter", "ui.sessionTurn.status.delegating": "Delegerer arbejde", "ui.sessionTurn.status.planning": "Planlægger næste trin", diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts index 44090b7bdb8c..977065db4c88 100644 --- a/packages/ui/src/i18n/de.ts +++ b/packages/ui/src/i18n/de.ts @@ -31,6 +31,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "erneuter Versuch", "ui.sessionTurn.retry.inSeconds": "in {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Kostenloses Nutzungslimit überschritten", + "ui.sessionTurn.error.addCredits": "Guthaben aufladen", "ui.sessionTurn.status.delegating": "Arbeit delegieren", "ui.sessionTurn.status.planning": "Nächste Schritte planen", diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts index 9b6ab0bd6d9e..59f08e48d399 100644 --- a/packages/ui/src/i18n/en.ts +++ b/packages/ui/src/i18n/en.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "retrying", "ui.sessionTurn.retry.inSeconds": "in {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Free usage exceeded", + "ui.sessionTurn.error.addCredits": "Add credits", "ui.sessionTurn.status.delegating": "Delegating work", "ui.sessionTurn.status.planning": "Planning next steps", diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts index c2f8ac3b9d5c..6706515ecb38 100644 --- a/packages/ui/src/i18n/es.ts +++ b/packages/ui/src/i18n/es.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "reintentando", "ui.sessionTurn.retry.inSeconds": "en {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Límite de uso gratuito excedido", + "ui.sessionTurn.error.addCredits": "Añadir créditos", "ui.sessionTurn.status.delegating": "Delegando trabajo", "ui.sessionTurn.status.planning": "Planificando siguientes pasos", diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts index 679d56fa76ff..68a687e840f4 100644 --- a/packages/ui/src/i18n/fr.ts +++ b/packages/ui/src/i18n/fr.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "nouvelle tentative", "ui.sessionTurn.retry.inSeconds": "dans {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Limite d'utilisation gratuite dépassée", + "ui.sessionTurn.error.addCredits": "Ajouter des crédits", "ui.sessionTurn.status.delegating": "Délégation du travail", "ui.sessionTurn.status.planning": "Planification des prochaines étapes", diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts index bf85807d0052..6fff28cff435 100644 --- a/packages/ui/src/i18n/ja.ts +++ b/packages/ui/src/i18n/ja.ts @@ -27,6 +27,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "再試行中", "ui.sessionTurn.retry.inSeconds": "{{seconds}}秒後", + "ui.sessionTurn.error.freeUsageExceeded": "無料使用制限に達しました", + "ui.sessionTurn.error.addCredits": "クレジットを追加", "ui.sessionTurn.status.delegating": "作業を委任中", "ui.sessionTurn.status.planning": "次のステップを計画中", diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts index aba793a11b8d..6fac1590d794 100644 --- a/packages/ui/src/i18n/ko.ts +++ b/packages/ui/src/i18n/ko.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "재시도 중", "ui.sessionTurn.retry.inSeconds": "{{seconds}}초 후", + "ui.sessionTurn.error.freeUsageExceeded": "무료 사용량 초과", + "ui.sessionTurn.error.addCredits": "크레딧 추가", "ui.sessionTurn.status.delegating": "작업 위임 중", "ui.sessionTurn.status.planning": "다음 단계 계획 중", diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts index 7982b3ac75ed..160f26a5468a 100644 --- a/packages/ui/src/i18n/no.ts +++ b/packages/ui/src/i18n/no.ts @@ -31,6 +31,8 @@ export const dict: Record = { "ui.sessionTurn.retry.retrying": "Prøver igjen", "ui.sessionTurn.retry.inSeconds": "om {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Gratis bruk overskredet", + "ui.sessionTurn.error.addCredits": "Legg til kreditt", "ui.sessionTurn.status.delegating": "Delegerer arbeid", "ui.sessionTurn.status.planning": "Planlegger neste trinn", diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts index 2489ac7f2ee4..4882ba034849 100644 --- a/packages/ui/src/i18n/pl.ts +++ b/packages/ui/src/i18n/pl.ts @@ -27,6 +27,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "ponawianie", "ui.sessionTurn.retry.inSeconds": "za {{seconds}}s", + "ui.sessionTurn.error.freeUsageExceeded": "Przekroczono limit darmowego użytkowania", + "ui.sessionTurn.error.addCredits": "Dodaj kredyty", "ui.sessionTurn.status.delegating": "Delegowanie pracy", "ui.sessionTurn.status.planning": "Planowanie kolejnych kroków", diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts index 8e6bb678f249..93a9883d26a7 100644 --- a/packages/ui/src/i18n/ru.ts +++ b/packages/ui/src/i18n/ru.ts @@ -27,6 +27,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "повтор", "ui.sessionTurn.retry.inSeconds": "через {{seconds}}с", + "ui.sessionTurn.error.freeUsageExceeded": "Лимит бесплатного использования превышен", + "ui.sessionTurn.error.addCredits": "Добавить кредиты", "ui.sessionTurn.status.delegating": "Делегирование работы", "ui.sessionTurn.status.planning": "Планирование следующих шагов", diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts index b036eca2e8ae..1a5438a2ae85 100644 --- a/packages/ui/src/i18n/th.ts +++ b/packages/ui/src/i18n/th.ts @@ -28,6 +28,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "กำลังลองใหม่", "ui.sessionTurn.retry.inSeconds": "ใน {{seconds}}วิ", + "ui.sessionTurn.error.freeUsageExceeded": "เกินขีดจำกัดการใช้งานฟรี", + "ui.sessionTurn.error.addCredits": "เพิ่มเครดิต", "ui.sessionTurn.status.delegating": "มอบหมายงาน", "ui.sessionTurn.status.planning": "วางแผนขั้นตอนถัดไป", diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts index dcb8062a3365..dbebfb3f9f01 100644 --- a/packages/ui/src/i18n/zh.ts +++ b/packages/ui/src/i18n/zh.ts @@ -32,6 +32,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "重试中", "ui.sessionTurn.retry.inSeconds": "{{seconds}} 秒后", + "ui.sessionTurn.error.freeUsageExceeded": "免费使用额度已用完", + "ui.sessionTurn.error.addCredits": "添加积分", "ui.sessionTurn.status.delegating": "正在委派工作", "ui.sessionTurn.status.planning": "正在规划下一步", diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts index 271a6ded3253..5cec9c399ef2 100644 --- a/packages/ui/src/i18n/zht.ts +++ b/packages/ui/src/i18n/zht.ts @@ -32,6 +32,8 @@ export const dict = { "ui.sessionTurn.retry.retrying": "重試中", "ui.sessionTurn.retry.inSeconds": "{{seconds}} 秒後", + "ui.sessionTurn.error.freeUsageExceeded": "免費使用額度已用完", + "ui.sessionTurn.error.addCredits": "新增點數", "ui.sessionTurn.status.delegating": "正在委派工作", "ui.sessionTurn.status.planning": "正在規劃下一步", From 62a24c2ddaf56c4234898269b1951ab11483f57a Mon Sep 17 00:00:00 2001 From: opencode Date: Sun, 15 Feb 2026 18:49:52 +0000 Subject: [PATCH 009/255] release: v1.2.5 --- bun.lock | 30 +++++++++++++------------- 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/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 +- 18 files changed, 37 insertions(+), 37 deletions(-) diff --git a/bun.lock b/bun.lock index 59106e14afcb..a29f00687886 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -73,7 +73,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -107,7 +107,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -134,7 +134,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -158,7 +158,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -182,7 +182,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -215,7 +215,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -244,7 +244,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -260,7 +260,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.2.4", + "version": "1.2.5", "bin": { "opencode": "./bin/opencode", }, @@ -369,7 +369,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -389,7 +389,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.2.4", + "version": "1.2.5", "devDependencies": { "@hey-api/openapi-ts": "0.90.10", "@tsconfig/node22": "catalog:", @@ -400,7 +400,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -413,7 +413,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -455,7 +455,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "zod": "catalog:", }, @@ -466,7 +466,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.2.4", + "version": "1.2.5", "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 31afda656629..0272e78bbf27 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.2.4", + "version": "1.2.5", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index a6b2f5685d0b..0075a949d29f 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index f81304c92064..34626c1e9362 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.2.4", + "version": "1.2.5", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index e3864bfad68e..5e8c5841c90a 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.2.4", + "version": "1.2.5", "$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 261d36bae5e9..99a5ab7d936a 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.2.4", + "version": "1.2.5", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 2901e299c0a3..e89078b8935e 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index ac7060dd1081..874f2ed21854 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.2.4", + "version": "1.2.5", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 9c10eb9826a8..00af2842cdd2 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.2.4" +version = "1.2.5" 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.2.4/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.4/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.4/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/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.2.4/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/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.2.4/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 3a431e9bdfdc..d2c4e51e90a2 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.2.4", + "version": "1.2.5", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index a5b3415550ea..99be25372fb7 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.2.4", + "version": "1.2.5", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 437fc0917090..82618195c9b5 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.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 74c3fdb1ad6a..6b3494e0c60d 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.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 5a5a0e83599f..45eb1d841d65 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 684836335cca..56343983778e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.2.4", + "version": "1.2.5", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index 74393ceceed8..7a76bfdec2db 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.2.4", + "version": "1.2.5", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index e2acd6cf39ed..ff2a7d2686fe 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.2.4", + "version": "1.2.5", "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 e8e41a4f2946..bea2f5712ddd 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.2.4", + "version": "1.2.5", "publisher": "sst-dev", "repository": { "type": "git", From 9b23130ac47442a216d84eace4032369620e548a Mon Sep 17 00:00:00 2001 From: Salam Elbilig Date: Sun, 15 Feb 2026 13:21:57 -0800 Subject: [PATCH 010/255] feat(opencode): add `cljfmt` formatter support for Clojure files (#13426) --- packages/opencode/src/format/formatter.ts | 9 +++++ packages/web/src/content/docs/formatters.mdx | 35 ++++++++++---------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index 9e97fae9dfc8..418446958029 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -364,3 +364,12 @@ export const ormolu: Info = { return Bun.which("ormolu") !== null }, } + +export const cljfmt: Info = { + name: "cljfmt", + command: ["cljfmt", "fix", "--quiet", "$FILE"], + extensions: [".clj", ".cljs", ".cljc", ".edn"], + async enabled() { + return Bun.which("cljfmt") !== null + }, +} diff --git a/packages/web/src/content/docs/formatters.mdx b/packages/web/src/content/docs/formatters.mdx index 54f36e0cd0e8..0cb947b08f01 100644 --- a/packages/web/src/content/docs/formatters.mdx +++ b/packages/web/src/content/docs/formatters.mdx @@ -13,30 +13,31 @@ OpenCode comes with several built-in formatters for popular languages and framew | Formatter | Extensions | Requirements | | -------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| gofmt | .go | `gofmt` command available | -| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available | -| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in `package.json` | +| air | .R | `air` command available | | biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json(c)` config file | -| zig | .zig, .zon | `zig` command available | +| cargofmt | .rs | `cargo fmt` command available | | clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file | +| cljfmt | .clj, .cljs, .cljc, .edn | `cljfmt` command available | +| dart | .dart | `dart` command available | +| gleam | .gleam | `gleam` command available | +| gofmt | .go | `gofmt` command available | +| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | | ktlint | .kt, .kts | `ktlint` command available | +| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available | +| nixfmt | .nix | `nixfmt` command available | +| ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | +| ormolu | .hs | `ormolu` command available | +| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `oxfmt` dependency in `package.json` and an [experimental env variable flag](/docs/cli/#experimental) | +| pint | .php | `laravel/pint` dependency in `composer.json` | +| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in `package.json` | +| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available | | ruff | .py, .pyi | `ruff` command available with config | | rustfmt | .rs | `rustfmt` command available | -| cargofmt | .rs | `cargo fmt` command available | -| uv | .py, .pyi | `uv` command available | -| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available | +| shfmt | .sh, .bash | `shfmt` command available | | standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available | -| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | -| air | .R | `air` command available | -| dart | .dart | `dart` command available | -| ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | | terraform | .tf, .tfvars | `terraform` command available | -| gleam | .gleam | `gleam` command available | -| nixfmt | .nix | `nixfmt` command available | -| shfmt | .sh, .bash | `shfmt` command available | -| pint | .php | `laravel/pint` dependency in `composer.json` | -| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `oxfmt` dependency in `package.json` and an [experimental env variable flag](/docs/cli/#experimental) | -| ormolu | .hs | `ormolu` command available | +| uv | .py, .pyi | `uv` command available | +| zig | .zig, .zon | `zig` command available | So if your project has `prettier` in your `package.json`, OpenCode will automatically use it. From d9363da9eebc0481e9829f5b96cb07adcb4caaa8 Mon Sep 17 00:00:00 2001 From: Pan Kaixin Date: Mon, 16 Feb 2026 06:30:47 +0800 Subject: [PATCH 011/255] fix(website): correct zh-CN translation of proprietary terms in zen.mdx (#13734) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- packages/web/src/content/docs/zh-cn/zen.mdx | 56 ++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/web/src/content/docs/zh-cn/zen.mdx b/packages/web/src/content/docs/zh-cn/zen.mdx index 7c00b5cdf48a..e808652a3a79 100644 --- a/packages/web/src/content/docs/zh-cn/zen.mdx +++ b/packages/web/src/content/docs/zh-cn/zen.mdx @@ -116,39 +116,39 @@ https://opencode.ai/zen/v1/models | 模型 | 输入 | 输出 | 缓存读取 | 缓存写入 | | ---------------------------------- | ---------- | ---------- | ---------- | ---------- | -| 大泡菜 | 免费 | 免费 | 免费 | - | -| MiniMax M2.1 免费 | 免费 | 免费 | 免费 | - | -| 迷你最大M2.1 | 0.30 美元 | 1.20 美元 | 0.10 美元 | - | -| GLM 4.7 免费 | 免费 | 免费 | 免费 | - | +| Big Pickle | 免费 | 免费 | 免费 | - | +| MiniMax M2.1 Free | 免费 | 免费 | 免费 | - | +| MiniMax M2.1 | 0.30 美元 | 1.20 美元 | 0.10 美元 | - | +| GLM 4.7 Free | 免费 | 免费 | 免费 | - | | GLM 4.7 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | | GLM 4.6 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | -| Kimi K2.5 免费 | 免费 | 免费 | 免费 | - | -| 作为K2.5 | 0.60 美元 | $3.00 | 0.08 美元 | - | -| Kimi K2 思考 | 0.40 美元 | 2.50 美元 | - | - | -| 作为K2 | 0.40 美元 | 2.50 美元 | - | - | -| Qwen3 编码器 480B | 0.45 美元 | 1.50 美元 | - | - | -| Claude Sonnet 4.5(≤ 200K Tokens) | $3.00 | 15.00 美元 | 0.30 美元 | 3.75 美元 | -| 克劳德十四行诗 4.5(> 200K 代币) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | -| Claude Sonnet 4(≤ 200K Tokens) | $3.00 | 15.00 美元 | 0.30 美元 | 3.75 美元 | +| Kimi K2.5 Free | 免费 | 免费 | 免费 | - | +| Kimi K2.5 | 0.60 美元 | 3.00 美元 | 0.08 美元 | - | +| Kimi K2 Thinking | 0.40 美元 | 2.50 美元 | - | - | +| Kimi K2 | 0.40 美元 | 2.50 美元 | - | - | +| Qwen3 Coder 480B | 0.45 美元 | 1.50 美元 | - | - | +| Claude Sonnet 4.5(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | +| Claude Sonnet 4.5(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | +| Claude Sonnet 4(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | | Claude Sonnet 4(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | -| Claude 俳句 4.5 | 1.00 美元 | 5.00 美元 | 0.10 美元 | 1.25 美元 | -| Claude 俳句 3.5 | 0.80 美元 | 4.00 美元 | 0.08 美元 | 1.00 美元 | -| 克劳德作品4.6(≤ 200K 代币) | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | +| Claude Haiku 4.5 | 1.00 美元 | 5.00 美元 | 0.10 美元 | 1.25 美元 | +| Claude Haiku 3.5 | 0.80 美元 | 4.00 美元 | 0.08 美元 | 1.00 美元 | +| Claude Opus 4.6(≤ 200K Tokens) | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | | Claude Opus 4.6(> 200K Tokens) | 10.00 美元 | 37.50 美元 | 1.00 美元 | 12.50 美元 | -| Claude 工作 4.5 | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | -| Claude 工作 4.1 | 15.00 美元 | 75.00 美元 | 1.50 美元 | 18.75 美元 | -| Gemini 3 Pro(≤20万代币) | 2.00 美元 | 12.00 美元 | 0.20 美元 | - | -| Gemini 3 Pro(>20万代币) | 4.00 美元 | 18.00 美元 | 0.40 美元 | - | -| 双子座 3 闪光 | 0.50 美元 | $3.00 | 0.05 美元 | - | +| Claude Opus 4.5 | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | +| Claude Opus 4.1 | 15.00 美元 | 75.00 美元 | 1.50 美元 | 18.75 美元 | +| Gemini 3 Pro(≤20万 Tokens) | 2.00 美元 | 12.00 美元 | 0.20 美元 | - | +| Gemini 3 Pro(>20万 Tokens) | 4.00 美元 | 18.00 美元 | 0.40 美元 | - | +| Gemini 3 Flash | 0.50 美元 | 3.00 美元 | 0.05 美元 | - | | GPT 5.2 | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | -| GPT 5.2 法典 | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | +| GPT 5.2 Codex | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | | GPT 5.1 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 法典 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 法典最大 | 1.25 美元 | 10.00 美元 | 0.125 美元 | - | -| GPT 5.1 迷你版 | 0.25 美元 | 2.00 美元 | 0.025 美元 | - | +| GPT 5.1 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | +| GPT 5.1 Codex Max | 1.25 美元 | 10.00 美元 | 0.125 美元 | - | +| GPT 5.1 Codex Mini | 0.25 美元 | 2.00 美元 | 0.025 美元 | - | | GPT 5 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5 法典 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5 奈米 | 免费 | 免费 | 免费 | - | +| GPT 5 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | +| GPT 5 Nano | 免费 | 免费 | 免费 | - | 您可能会在您的使用历史记录中注意到*Claude Haiku 3.5*。这是一个[低成本模型](/docs/config/#models),用于生成会话标题。 @@ -216,8 +216,8 @@ Zen 也非常适合团队使用。您可以邀请您可以邀请队友,分配 您可以邀请团队成员到您的工作区并分配角色: -- **管理员**:管理模型、成员、API 密钥和设备 -- **成员**:仅管理自己的API 金? +- **管理员**:管理模型、成员、API 密钥和计费/账单 +- **成员**:仅管理自己的 API 密钥 管理员还可以为每个成员设置每月支出限额,以控制成本。 From 21e07780023dc34b57b1b79cf9715b537971d673 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sun, 15 Feb 2026 22:31:40 +0000 Subject: [PATCH 012/255] chore: generate --- packages/web/src/content/docs/zh-cn/zen.mdx | 34 ++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/web/src/content/docs/zh-cn/zen.mdx b/packages/web/src/content/docs/zh-cn/zen.mdx index e808652a3a79..d03836dc55a3 100644 --- a/packages/web/src/content/docs/zh-cn/zen.mdx +++ b/packages/web/src/content/docs/zh-cn/zen.mdx @@ -116,38 +116,38 @@ https://opencode.ai/zen/v1/models | 模型 | 输入 | 输出 | 缓存读取 | 缓存写入 | | ---------------------------------- | ---------- | ---------- | ---------- | ---------- | -| Big Pickle | 免费 | 免费 | 免费 | - | +| Big Pickle | 免费 | 免费 | 免费 | - | | MiniMax M2.1 Free | 免费 | 免费 | 免费 | - | | MiniMax M2.1 | 0.30 美元 | 1.20 美元 | 0.10 美元 | - | | GLM 4.7 Free | 免费 | 免费 | 免费 | - | | GLM 4.7 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | | GLM 4.6 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | | Kimi K2.5 Free | 免费 | 免费 | 免费 | - | -| Kimi K2.5 | 0.60 美元 | 3.00 美元 | 0.08 美元 | - | -| Kimi K2 Thinking | 0.40 美元 | 2.50 美元 | - | - | -| Kimi K2 | 0.40 美元 | 2.50 美元 | - | - | -| Qwen3 Coder 480B | 0.45 美元 | 1.50 美元 | - | - | +| Kimi K2.5 | 0.60 美元 | 3.00 美元 | 0.08 美元 | - | +| Kimi K2 Thinking | 0.40 美元 | 2.50 美元 | - | - | +| Kimi K2 | 0.40 美元 | 2.50 美元 | - | - | +| Qwen3 Coder 480B | 0.45 美元 | 1.50 美元 | - | - | | Claude Sonnet 4.5(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | -| Claude Sonnet 4.5(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | +| Claude Sonnet 4.5(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | | Claude Sonnet 4(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | | Claude Sonnet 4(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | -| Claude Haiku 4.5 | 1.00 美元 | 5.00 美元 | 0.10 美元 | 1.25 美元 | -| Claude Haiku 3.5 | 0.80 美元 | 4.00 美元 | 0.08 美元 | 1.00 美元 | -| Claude Opus 4.6(≤ 200K Tokens) | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | +| Claude Haiku 4.5 | 1.00 美元 | 5.00 美元 | 0.10 美元 | 1.25 美元 | +| Claude Haiku 3.5 | 0.80 美元 | 4.00 美元 | 0.08 美元 | 1.00 美元 | +| Claude Opus 4.6(≤ 200K Tokens) | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | | Claude Opus 4.6(> 200K Tokens) | 10.00 美元 | 37.50 美元 | 1.00 美元 | 12.50 美元 | | Claude Opus 4.5 | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | | Claude Opus 4.1 | 15.00 美元 | 75.00 美元 | 1.50 美元 | 18.75 美元 | -| Gemini 3 Pro(≤20万 Tokens) | 2.00 美元 | 12.00 美元 | 0.20 美元 | - | -| Gemini 3 Pro(>20万 Tokens) | 4.00 美元 | 18.00 美元 | 0.40 美元 | - | -| Gemini 3 Flash | 0.50 美元 | 3.00 美元 | 0.05 美元 | - | +| Gemini 3 Pro(≤20万 Tokens) | 2.00 美元 | 12.00 美元 | 0.20 美元 | - | +| Gemini 3 Pro(>20万 Tokens) | 4.00 美元 | 18.00 美元 | 0.40 美元 | - | +| Gemini 3 Flash | 0.50 美元 | 3.00 美元 | 0.05 美元 | - | | GPT 5.2 | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | -| GPT 5.2 Codex | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | +| GPT 5.2 Codex | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | | GPT 5.1 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 Codex Max | 1.25 美元 | 10.00 美元 | 0.125 美元 | - | -| GPT 5.1 Codex Mini | 0.25 美元 | 2.00 美元 | 0.025 美元 | - | +| GPT 5.1 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | +| GPT 5.1 Codex Max | 1.25 美元 | 10.00 美元 | 0.125 美元 | - | +| GPT 5.1 Codex Mini | 0.25 美元 | 2.00 美元 | 0.025 美元 | - | | GPT 5 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | +| GPT 5 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | | GPT 5 Nano | 免费 | 免费 | 免费 | - | 您可能会在您的使用历史记录中注意到*Claude Haiku 3.5*。这是一个[低成本模型](/docs/config/#models),用于生成会话标题。 From 920255e8c69270942206b60f94e26b545af18050 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Mon, 16 Feb 2026 12:14:24 +0800 Subject: [PATCH 013/255] desktop: use process-wrap instead of manual job object (#13431) --- packages/desktop/src-tauri/Cargo.lock | 91 ++++++++++- packages/desktop/src-tauri/Cargo.toml | 11 +- packages/desktop/src-tauri/src/cli.rs | 162 ++++++++++++++++--- packages/desktop/src-tauri/src/job_object.rs | 145 ----------------- packages/desktop/src-tauri/src/lib.rs | 15 +- packages/desktop/src-tauri/src/logging.rs | 11 +- packages/desktop/src-tauri/src/server.rs | 2 +- 7 files changed, 229 insertions(+), 208 deletions(-) delete mode 100644 packages/desktop/src-tauri/src/job_object.rs diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index a2bb2532af7e..c8575a7593bd 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -2343,9 +2343,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -2663,6 +2663,18 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -3093,6 +3105,7 @@ dependencies = [ "listeners", "objc2 0.6.3", "objc2-web-kit", + "process-wrap", "reqwest 0.12.24", "semver", "serde", @@ -3123,7 +3136,6 @@ dependencies = [ "tracing-subscriber", "uuid", "webkit2gtk", - "windows 0.61.3", ] [[package]] @@ -3638,6 +3650,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "process-wrap" +version = "9.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd9713fe2c91c3c85ac388b31b89de339365d2c995146e630b5e0da9d06526a" +dependencies = [ + "futures", + "indexmap 2.12.1", + "nix 0.31.1", + "tokio", + "tracing", + "windows 0.62.2", +] + [[package]] name = "psl-types" version = "2.0.11" @@ -6460,11 +6486,23 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -6476,6 +6514,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -6519,7 +6566,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6566,6 +6624,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -6741,6 +6809,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-version" version = "0.1.7" diff --git a/packages/desktop/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml index 67efd8d8c9b5..a5539645d606 100644 --- a/packages/desktop/src-tauri/Cargo.toml +++ b/packages/desktop/src-tauri/Cargo.toml @@ -34,7 +34,7 @@ tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = "1.48.0" +tokio = { version = "1.48.0", features = ["process"] } listeners = "0.3" tauri-plugin-os = "2" futures = "0.3.31" @@ -52,6 +52,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-appender = "0.2" chrono = "0.4" tokio-stream = { version = "0.1.18", features = ["sync"] } +process-wrap = { version = "9.0.3", features = ["tokio1"] } [target.'cfg(target_os = "linux")'.dependencies] gtk = "0.18.2" @@ -62,14 +63,6 @@ objc2 = "0.6" objc2-web-kit = "0.3" -[target.'cfg(windows)'.dependencies] -windows = { version = "0.61", features = [ - "Win32_Foundation", - "Win32_System_JobObjects", - "Win32_System_Threading", - "Win32_Security" -] } - [patch.crates-io] specta = { git = "https://github.com/specta-rs/specta", rev = "591a5f3ddc78348abf4cbb541d599d65306d92b9" } specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "591a5f3ddc78348abf4cbb541d599d65306d92b9" } diff --git a/packages/desktop/src-tauri/src/cli.rs b/packages/desktop/src-tauri/src/cli.rs index dade1a28186e..0f5cd2ff1cfa 100644 --- a/packages/desktop/src-tauri/src/cli.rs +++ b/packages/desktop/src-tauri/src/cli.rs @@ -1,12 +1,19 @@ use futures::{FutureExt, Stream, StreamExt, future}; +use process_wrap::tokio::CommandWrap; +#[cfg(unix)] +use process_wrap::tokio::ProcessGroup; +#[cfg(windows)] +use process_wrap::tokio::{JobObject, KillOnDrop}; +#[cfg(unix)] +use std::os::unix::process::ExitStatusExt; +use std::{process::Stdio, time::Duration}; use tauri::{AppHandle, Manager, path::BaseDirectory}; -use tauri_plugin_shell::{ - ShellExt, - process::{CommandChild, CommandEvent, TerminatedPayload}, -}; use tauri_plugin_store::StoreExt; use tauri_specta::Event; -use tokio::sync::oneshot; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; +use tokio::sync::{mpsc, oneshot}; +use tokio_stream::wrappers::ReceiverStream; use tracing::Instrument; use crate::constants::{SETTINGS_STORE, WSL_ENABLED_KEY}; @@ -25,6 +32,33 @@ pub struct Config { pub server: Option, } +#[derive(Clone, Debug)] +pub enum CommandEvent { + Stdout(Vec), + Stderr(Vec), + Error(String), + Terminated(TerminatedPayload), +} + +#[derive(Clone, Copy, Debug)] +pub struct TerminatedPayload { + pub code: Option, + pub signal: Option, +} + +#[derive(Clone, Debug)] +pub struct CommandChild { + kill: mpsc::Sender<()>, +} + +impl CommandChild { + pub fn kill(&self) -> std::io::Result<()> { + self.kill + .try_send(()) + .map_err(|e| std::io::Error::other(e.to_string())) + } +} + pub async fn get_config(app: &AppHandle) -> Option { let (events, _) = spawn_command(app, "debug config", &[]).ok()?; @@ -190,7 +224,7 @@ pub fn spawn_command( app: &tauri::AppHandle, args: &str, extra_env: &[(&str, String)], -) -> Result<(impl Stream + 'static, CommandChild), tauri_plugin_shell::Error> { +) -> Result<(impl Stream + 'static, CommandChild), std::io::Error> { let state_dir = app .path() .resolve("", BaseDirectory::AppLocalData) @@ -217,7 +251,7 @@ pub fn spawn_command( .map(|(key, value)| (key.to_string(), value.clone())), ); - let cmd = if cfg!(windows) { + let mut cmd = if cfg!(windows) { if is_wsl_enabled(app) { tracing::info!("WSL is enabled, spawning CLI server in WSL"); let version = app.package_info().version.to_string(); @@ -249,18 +283,16 @@ pub fn spawn_command( script.push(format!("{} exec \"$BIN\" {}", env_prefix.join(" "), args)); - app.shell() - .command("wsl") - .args(["-e", "bash", "-lc", &script.join("\n")]) + let mut cmd = Command::new("wsl"); + cmd.args(["-e", "bash", "-lc", &script.join("\n")]); + cmd } else { - let mut cmd = app - .shell() - .sidecar("opencode-cli") - .unwrap() - .args(args.split_whitespace()); + let sidecar = get_sidecar_path(app); + let mut cmd = Command::new(sidecar); + cmd.args(args.split_whitespace()); for (key, value) in envs { - cmd = cmd.env(key, value); + cmd.env(key, value); } cmd @@ -269,26 +301,111 @@ pub fn spawn_command( let sidecar = get_sidecar_path(app); let shell = get_user_shell(); - let cmd = if shell.ends_with("/nu") { + let line = if shell.ends_with("/nu") { format!("^\"{}\" {}", sidecar.display(), args) } else { format!("\"{}\" {}", sidecar.display(), args) }; - let mut cmd = app.shell().command(&shell).args(["-il", "-c", &cmd]); + let mut cmd = Command::new(shell); + cmd.args(["-il", "-c", &line]); for (key, value) in envs { - cmd = cmd.env(key, value); + cmd.env(key, value); } cmd }; - let (rx, child) = cmd.spawn()?; - let event_stream = tokio_stream::wrappers::ReceiverStream::new(rx); + cmd.stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut wrap = CommandWrap::from(cmd); + + #[cfg(unix)] + { + wrap.wrap(ProcessGroup::leader()); + } + + #[cfg(windows)] + { + wrap.wrap(JobObject).wrap(KillOnDrop); + } + + let mut child = wrap.spawn()?; + let stdout = child.stdout().take(); + let stderr = child.stderr().take(); + let (tx, rx) = mpsc::channel(256); + let (kill_tx, mut kill_rx) = mpsc::channel(1); + + if let Some(stdout) = stdout { + let tx = tx.clone(); + tokio::spawn(async move { + let mut lines = BufReader::new(stdout).lines(); + while let Ok(Some(line)) = lines.next_line().await { + let _ = tx.send(CommandEvent::Stdout(line.into_bytes())).await; + } + }); + } + + if let Some(stderr) = stderr { + let tx = tx.clone(); + tokio::spawn(async move { + let mut lines = BufReader::new(stderr).lines(); + while let Ok(Some(line)) = lines.next_line().await { + let _ = tx.send(CommandEvent::Stderr(line.into_bytes())).await; + } + }); + } + + tokio::spawn(async move { + let status = loop { + match child.try_wait() { + Ok(Some(status)) => break Ok(status), + Ok(None) => {} + Err(err) => break Err(err), + } + + tokio::select! { + _ = kill_rx.recv() => { + let _ = child.start_kill(); + } + _ = tokio::time::sleep(Duration::from_millis(100)) => {} + } + }; + + match status { + Ok(status) => { + let payload = TerminatedPayload { + code: status.code(), + signal: signal_from_status(status), + }; + let _ = tx.send(CommandEvent::Terminated(payload)).await; + } + Err(err) => { + let _ = tx.send(CommandEvent::Error(err.to_string())).await; + } + } + }); + + let event_stream = ReceiverStream::new(rx); let event_stream = sqlite_migration::logs_middleware(app.clone(), event_stream); - Ok((event_stream, child)) + Ok((event_stream, CommandChild { kill: kill_tx })) +} + +fn signal_from_status(status: std::process::ExitStatus) -> Option { + #[cfg(unix)] + { + return status.signal(); + } + + #[cfg(not(unix))] + { + let _ = status; + None + } } pub fn serve( @@ -340,7 +457,6 @@ pub fn serve( let _ = tx.send(payload); } } - _ => {} } future::ready(()) diff --git a/packages/desktop/src-tauri/src/job_object.rs b/packages/desktop/src-tauri/src/job_object.rs deleted file mode 100644 index 8d774b14cd9c..000000000000 --- a/packages/desktop/src-tauri/src/job_object.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! Windows Job Object for reliable child process cleanup. -//! -//! This module provides a wrapper around Windows Job Objects with the -//! `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` flag set. When the job object handle -//! is closed (including when the parent process exits or crashes), Windows -//! automatically terminates all processes assigned to the job. -//! -//! This is more reliable than manual cleanup because it works even if: -//! - The parent process crashes -//! - The parent is killed via Task Manager -//! - The RunEvent::Exit handler fails to run - -use std::io::{Error, Result}; -#[cfg(windows)] -use std::sync::Mutex; -use windows::Win32::Foundation::{CloseHandle, HANDLE}; -use windows::Win32::System::JobObjects::{ - AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation, - SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, -}; -use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_TERMINATE}; - -/// A Windows Job Object configured to kill all assigned processes when closed. -/// -/// When this struct is dropped or when the owning process exits (even abnormally), -/// Windows will automatically terminate all processes that have been assigned to it. -pub struct JobObject(HANDLE); - -// SAFETY: HANDLE is just a pointer-sized value, and Windows job objects -// can be safely accessed from multiple threads. -unsafe impl Send for JobObject {} -unsafe impl Sync for JobObject {} - -impl JobObject { - /// Creates a new anonymous job object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` set. - /// - /// When the last handle to this job is closed (including on process exit), - /// Windows will terminate all processes assigned to the job. - pub fn new() -> Result { - unsafe { - // Create an anonymous job object - let job = CreateJobObjectW(None, None).map_err(|e| Error::other(e.message()))?; - - // Configure the job to kill all processes when the handle is closed - let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default(); - info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - - SetInformationJobObject( - job, - JobObjectExtendedLimitInformation, - &info as *const _ as *const std::ffi::c_void, - std::mem::size_of::() as u32, - ) - .map_err(|e| Error::other(e.message()))?; - - Ok(Self(job)) - } - } - - /// Assigns a process to this job object by its process ID. - /// - /// Once assigned, the process will be terminated when this job object is dropped - /// or when the owning process exits. - /// - /// # Arguments - /// * `pid` - The process ID of the process to assign - pub fn assign_pid(&self, pid: u32) -> Result<()> { - unsafe { - // Open a handle to the process with the minimum required permissions - // PROCESS_SET_QUOTA and PROCESS_TERMINATE are required by AssignProcessToJobObject - let process = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, false, pid) - .map_err(|e| Error::other(e.message()))?; - - // Assign the process to the job - let result = AssignProcessToJobObject(self.0, process); - - // Close our handle to the process - the job object maintains its own reference - let _ = CloseHandle(process); - - result.map_err(|e| Error::other(e.message())) - } - } -} - -impl Drop for JobObject { - fn drop(&mut self) { - unsafe { - // When this handle is closed and it's the last handle to the job, - // Windows will terminate all processes in the job due to KILL_ON_JOB_CLOSE - let _ = CloseHandle(self.0); - } - } -} - -/// Holds the Windows Job Object that ensures child processes are killed when the app exits. -/// On Windows, when the job object handle is closed (including on crash), all assigned -/// processes are automatically terminated by the OS. -#[cfg(windows)] -pub struct JobObjectState { - job: Mutex>, - error: Mutex>, -} - -#[cfg(windows)] -impl JobObjectState { - pub fn new() -> Self { - match JobObject::new() { - Ok(job) => Self { - job: Mutex::new(Some(job)), - error: Mutex::new(None), - }, - Err(e) => { - tracing::error!("Failed to create job object: {e}"); - Self { - job: Mutex::new(None), - error: Mutex::new(Some(format!("Failed to create job object: {e}"))), - } - } - } - } - - pub fn assign_pid(&self, pid: u32) { - if let Some(job) = self.job.lock().unwrap().as_ref() { - if let Err(e) = job.assign_pid(pid) { - tracing::error!(pid, "Failed to assign process to job object: {e}"); - *self.error.lock().unwrap() = - Some(format!("Failed to assign process to job object: {e}")); - } else { - tracing::info!(pid, "Assigned process to job object for automatic cleanup"); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_job_object_creation() { - let job = JobObject::new(); - assert!(job.is_ok(), "Failed to create job object: {:?}", job.err()); - } -} diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index aa605a9239fd..4a1c8dc4a889 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -1,7 +1,5 @@ mod cli; mod constants; -#[cfg(windows)] -mod job_object; #[cfg(target_os = "linux")] pub mod linux_display; mod logging; @@ -10,12 +8,11 @@ mod server; mod window_customizer; mod windows; +use crate::cli::CommandChild; use futures::{ FutureExt, TryFutureExt, future::{self, Shared}, }; -#[cfg(windows)] -use job_object::*; use std::{ env, net::TcpListener, @@ -27,7 +24,6 @@ use std::{ use tauri::{AppHandle, Listener, Manager, RunEvent, State, ipc::Channel}; #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] use tauri_plugin_deep_link::DeepLinkExt; -use tauri_plugin_shell::process::CommandChild; use tauri_specta::Event; use tokio::{ sync::{oneshot, watch}, @@ -631,12 +627,6 @@ async fn initialize(app: AppHandle) { tracing::info!("CLI health check OK"); - #[cfg(windows)] - { - let job_state = app.state::(); - job_state.assign_pid(child.pid()); - } - app.state::().set_child(Some(child)); Ok(ServerReadyData { url, password }) @@ -710,9 +700,6 @@ fn setup_app(app: &tauri::AppHandle, init_rx: watch::Receiver) { #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] app.deep_link().register_all().ok(); - #[cfg(windows)] - app.manage(JobObjectState::new()); - app.manage(InitState { current: init_rx }); } diff --git a/packages/desktop/src-tauri/src/logging.rs b/packages/desktop/src-tauri/src/logging.rs index f794f9c1bc48..b985b1f9d0bc 100644 --- a/packages/desktop/src-tauri/src/logging.rs +++ b/packages/desktop/src-tauri/src/logging.rs @@ -36,11 +36,7 @@ pub fn init(log_dir: &Path) -> WorkerGuard { tracing_subscriber::registry() .with(filter) .with(fmt::layer().with_writer(std::io::stderr)) - .with( - fmt::layer() - .with_writer(non_blocking) - .with_ansi(false), - ) + .with(fmt::layer().with_writer(non_blocking).with_ansi(false)) .init(); guard @@ -55,10 +51,7 @@ pub fn tail() -> String { return String::new(); }; - let lines: Vec = BufReader::new(file) - .lines() - .map_while(Result::ok) - .collect(); + let lines: Vec = BufReader::new(file).lines().map_while(Result::ok).collect(); let start = lines.len().saturating_sub(TAIL_LINES); lines[start..].join("\n") diff --git a/packages/desktop/src-tauri/src/server.rs b/packages/desktop/src-tauri/src/server.rs index 6dcf0e5860ae..8253482ef06f 100644 --- a/packages/desktop/src-tauri/src/server.rs +++ b/packages/desktop/src-tauri/src/server.rs @@ -2,12 +2,12 @@ use std::time::{Duration, Instant}; use tauri::AppHandle; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult}; -use tauri_plugin_shell::process::CommandChild; use tauri_plugin_store::StoreExt; use tokio::task::JoinHandle; use crate::{ cli, + cli::CommandChild, constants::{DEFAULT_SERVER_URL_KEY, SETTINGS_STORE, WSL_ENABLED_KEY}, }; From afd0716cbdca5191b6c45dbc8325c6f9e658715f Mon Sep 17 00:00:00 2001 From: dpuyosa Date: Mon, 16 Feb 2026 05:24:24 +0100 Subject: [PATCH 014/255] feat(opencode): Add Venice support in temperature, topP, topK and smallOption (#13553) --- packages/opencode/src/provider/transform.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 853d03c1d8b9..759dab440d40 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -298,8 +298,8 @@ export namespace ProviderTransform { if (id.includes("glm-4.7")) return 1.0 if (id.includes("minimax-m2")) return 1.0 if (id.includes("kimi-k2")) { - // kimi-k2-thinking & kimi-k2.5 && kimi-k2p5 - if (id.includes("thinking") || id.includes("k2.") || id.includes("k2p")) { + // kimi-k2-thinking & kimi-k2.5 && kimi-k2p5 && kimi-k2-5 + if (["thinking", "k2.", "k2p", "k2-5"].some((s) => id.includes(s))) { return 1.0 } return 0.6 @@ -310,7 +310,7 @@ export namespace ProviderTransform { export function topP(model: Provider.Model) { const id = model.id.toLowerCase() if (id.includes("qwen")) return 1 - if (id.includes("minimax-m2") || id.includes("kimi-k2.5") || id.includes("kimi-k2p5") || id.includes("gemini")) { + if (["minimax-m2", "gemini", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) { return 0.95 } return undefined @@ -319,7 +319,7 @@ export namespace ProviderTransform { export function topK(model: Provider.Model) { const id = model.id.toLowerCase() if (id.includes("minimax-m2")) { - if (id.includes("m2.1")) return 40 + if (["m2.", "m25", "m21"].some((s) => id.includes(s))) return 40 return 20 } if (id.includes("gemini")) return 64 @@ -802,6 +802,11 @@ export namespace ProviderTransform { } return { reasoningEffort: "minimal" } } + + if (model.providerID === "venice") { + return { veniceParameters: { disableThinking: true } } + } + return {} } From 60807846a92be5ab75367d8ca14b6b1bc697aebe Mon Sep 17 00:00:00 2001 From: bnema Date: Mon, 16 Feb 2026 06:24:28 +0100 Subject: [PATCH 015/255] fix(desktop): normalize Linux Wayland/X11 backend and decoration policy (#13143) Co-authored-by: Brendan Allan --- packages/desktop/src-tauri/src/lib.rs | 2 + .../desktop/src-tauri/src/linux_windowing.rs | 475 ++++++++++++++++++ packages/desktop/src-tauri/src/main.rs | 54 +- packages/desktop/src-tauri/src/windows.rs | 26 +- 4 files changed, 519 insertions(+), 38 deletions(-) create mode 100644 packages/desktop/src-tauri/src/linux_windowing.rs diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 4a1c8dc4a889..c6a7d13e68ff 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -2,6 +2,8 @@ mod cli; mod constants; #[cfg(target_os = "linux")] pub mod linux_display; +#[cfg(target_os = "linux")] +pub mod linux_windowing; mod logging; mod markdown; mod server; diff --git a/packages/desktop/src-tauri/src/linux_windowing.rs b/packages/desktop/src-tauri/src/linux_windowing.rs new file mode 100644 index 000000000000..f2c084efb599 --- /dev/null +++ b/packages/desktop/src-tauri/src/linux_windowing.rs @@ -0,0 +1,475 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Backend { + Auto, + Wayland, + X11, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BackendDecision { + pub backend: Backend, + pub note: String, +} + +#[derive(Debug, Clone, Default)] +pub struct SessionEnv { + pub wayland_display: bool, + pub xdg_session_type: Option, + pub display: bool, + pub xdg_current_desktop: Option, + pub xdg_session_desktop: Option, + pub desktop_session: Option, + pub oc_allow_wayland: Option, + pub oc_force_x11: Option, + pub oc_force_wayland: Option, + pub oc_linux_decorations: Option, + pub oc_force_decorations: Option, + pub oc_no_decorations: Option, + pub i3_sock: bool, +} + +impl SessionEnv { + pub fn capture() -> Self { + Self { + wayland_display: std::env::var_os("WAYLAND_DISPLAY").is_some(), + xdg_session_type: std::env::var("XDG_SESSION_TYPE").ok(), + display: std::env::var_os("DISPLAY").is_some(), + xdg_current_desktop: std::env::var("XDG_CURRENT_DESKTOP").ok(), + xdg_session_desktop: std::env::var("XDG_SESSION_DESKTOP").ok(), + desktop_session: std::env::var("DESKTOP_SESSION").ok(), + oc_allow_wayland: std::env::var("OC_ALLOW_WAYLAND").ok(), + oc_force_x11: std::env::var("OC_FORCE_X11").ok(), + oc_force_wayland: std::env::var("OC_FORCE_WAYLAND").ok(), + oc_linux_decorations: std::env::var("OC_LINUX_DECORATIONS").ok(), + oc_force_decorations: std::env::var("OC_FORCE_DECORATIONS").ok(), + oc_no_decorations: std::env::var("OC_NO_DECORATIONS").ok(), + i3_sock: std::env::var_os("I3SOCK").is_some(), + } + } +} + +pub fn select_backend(env: &SessionEnv, prefer_wayland: bool) -> Option { + if is_truthy(env.oc_force_x11.as_deref()) { + return Some(BackendDecision { + backend: Backend::X11, + note: "Forcing X11 due to OC_FORCE_X11=1".into(), + }); + } + + if is_truthy(env.oc_force_wayland.as_deref()) { + return Some(BackendDecision { + backend: Backend::Wayland, + note: "Forcing native Wayland due to OC_FORCE_WAYLAND=1".into(), + }); + } + + if !is_wayland_session(env) { + return None; + } + + if prefer_wayland { + return Some(BackendDecision { + backend: Backend::Wayland, + note: "Wayland session detected; forcing native Wayland from settings".into(), + }); + } + + if is_truthy(env.oc_allow_wayland.as_deref()) { + return Some(BackendDecision { + backend: Backend::Wayland, + note: "Wayland session detected; forcing native Wayland due to OC_ALLOW_WAYLAND=1" + .into(), + }); + } + + Some(BackendDecision { + backend: Backend::Auto, + note: "Wayland session detected; using native Wayland first with X11 fallback (auto backend). Set OC_FORCE_X11=1 to force X11." + .into(), + }) +} + +pub fn use_decorations(env: &SessionEnv) -> bool { + if let Some(mode) = decoration_override(env.oc_linux_decorations.as_deref()) { + return match mode { + DecorationOverride::Native => true, + DecorationOverride::None => false, + DecorationOverride::Auto => default_use_decorations(env), + }; + } + + if is_truthy(env.oc_force_decorations.as_deref()) { + return true; + } + if is_truthy(env.oc_no_decorations.as_deref()) { + return false; + } + + default_use_decorations(env) +} + +fn default_use_decorations(env: &SessionEnv) -> bool { + if is_known_tiling_session(env) { + return false; + } + if !is_wayland_session(env) { + return true; + } + is_full_desktop_session(env) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum DecorationOverride { + Auto, + Native, + None, +} + +fn decoration_override(value: Option<&str>) -> Option { + let value = value?.trim().to_ascii_lowercase(); + if matches!(value.as_str(), "auto") { + return Some(DecorationOverride::Auto); + } + if matches!( + value.as_str(), + "native" | "server" | "de" | "wayland" | "on" | "true" | "1" + ) { + return Some(DecorationOverride::Native); + } + if matches!( + value.as_str(), + "none" | "off" | "false" | "0" | "client" | "csd" + ) { + return Some(DecorationOverride::None); + } + None +} + +fn is_truthy(value: Option<&str>) -> bool { + matches!( + value.map(|v| v.trim().to_ascii_lowercase()), + Some(v) if matches!(v.as_str(), "1" | "true" | "yes" | "on") + ) +} + +fn is_wayland_session(env: &SessionEnv) -> bool { + env.wayland_display + || matches!( + env.xdg_session_type.as_deref(), + Some(value) if value.eq_ignore_ascii_case("wayland") + ) +} + +fn is_full_desktop_session(env: &SessionEnv) -> bool { + desktop_tokens(env).any(|value| { + matches!( + value.as_str(), + "gnome" + | "kde" + | "plasma" + | "xfce" + | "xfce4" + | "x-cinnamon" + | "cinnamon" + | "mate" + | "lxqt" + | "budgie" + | "pantheon" + | "deepin" + | "unity" + | "cosmic" + ) + }) +} + +fn is_known_tiling_session(env: &SessionEnv) -> bool { + if env.i3_sock { + return true; + } + + desktop_tokens(env).any(|value| { + matches!( + value.as_str(), + "niri" + | "sway" + | "swayfx" + | "hyprland" + | "river" + | "i3" + | "i3wm" + | "bspwm" + | "dwm" + | "qtile" + | "xmonad" + | "leftwm" + | "dwl" + | "awesome" + | "herbstluftwm" + | "spectrwm" + | "worm" + | "i3-gnome" + ) + }) +} + +fn desktop_tokens<'a>(env: &'a SessionEnv) -> impl Iterator + 'a { + [ + env.xdg_current_desktop.as_deref(), + env.xdg_session_desktop.as_deref(), + env.desktop_session.as_deref(), + ] + .into_iter() + .flatten() + .flat_map(|desktop| desktop.split(':')) + .map(|value| value.trim().to_ascii_lowercase()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn prefers_wayland_first_on_wayland_session() { + let env = SessionEnv { + wayland_display: true, + display: true, + ..Default::default() + }; + + let decision = select_backend(&env, false).expect("missing decision"); + assert_eq!(decision.backend, Backend::Auto); + } + + #[test] + fn force_x11_override_wins() { + let env = SessionEnv { + wayland_display: true, + display: true, + oc_force_x11: Some("1".into()), + oc_allow_wayland: Some("1".into()), + oc_force_wayland: Some("1".into()), + ..Default::default() + }; + + let decision = select_backend(&env, true).expect("missing decision"); + assert_eq!(decision.backend, Backend::X11); + } + + #[test] + fn prefer_wayland_forces_wayland_backend() { + let env = SessionEnv { + wayland_display: true, + display: true, + ..Default::default() + }; + + let decision = select_backend(&env, true).expect("missing decision"); + assert_eq!(decision.backend, Backend::Wayland); + } + + #[test] + fn force_wayland_override_works_outside_wayland_session() { + let env = SessionEnv { + display: true, + oc_force_wayland: Some("1".into()), + ..Default::default() + }; + + let decision = select_backend(&env, false).expect("missing decision"); + assert_eq!(decision.backend, Backend::Wayland); + } + + #[test] + fn allow_wayland_forces_wayland_backend() { + let env = SessionEnv { + wayland_display: true, + display: true, + oc_allow_wayland: Some("1".into()), + ..Default::default() + }; + + let decision = select_backend(&env, false).expect("missing decision"); + assert_eq!(decision.backend, Backend::Wayland); + } + + #[test] + fn xdg_session_type_wayland_is_detected() { + let env = SessionEnv { + xdg_session_type: Some("wayland".into()), + ..Default::default() + }; + + let decision = select_backend(&env, false).expect("missing decision"); + assert_eq!(decision.backend, Backend::Auto); + } + + #[test] + fn returns_none_when_not_wayland_and_no_overrides() { + let env = SessionEnv { + display: true, + xdg_current_desktop: Some("GNOME".into()), + ..Default::default() + }; + + assert!(select_backend(&env, false).is_none()); + } + + #[test] + fn prefer_wayland_setting_does_not_override_x11_session() { + let env = SessionEnv { + display: true, + xdg_current_desktop: Some("GNOME".into()), + ..Default::default() + }; + + assert!(select_backend(&env, true).is_none()); + } + + #[test] + fn disables_decorations_on_niri() { + let env = SessionEnv { + xdg_current_desktop: Some("niri".into()), + wayland_display: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn keeps_decorations_on_gnome() { + let env = SessionEnv { + xdg_current_desktop: Some("GNOME".into()), + wayland_display: true, + ..Default::default() + }; + + assert!(use_decorations(&env)); + } + + #[test] + fn disables_decorations_when_session_desktop_is_tiling() { + let env = SessionEnv { + xdg_session_desktop: Some("Hyprland".into()), + wayland_display: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn disables_decorations_for_unknown_wayland_session() { + let env = SessionEnv { + xdg_current_desktop: Some("labwc".into()), + wayland_display: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn disables_decorations_for_dwm_on_x11() { + let env = SessionEnv { + xdg_current_desktop: Some("dwm".into()), + display: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn disables_decorations_for_i3_on_x11() { + let env = SessionEnv { + xdg_current_desktop: Some("i3".into()), + display: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn disables_decorations_for_i3sock_without_xdg_tokens() { + let env = SessionEnv { + display: true, + i3_sock: true, + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn keeps_decorations_for_gnome_on_x11() { + let env = SessionEnv { + xdg_current_desktop: Some("GNOME".into()), + display: true, + ..Default::default() + }; + + assert!(use_decorations(&env)); + } + + #[test] + fn no_decorations_override_wins() { + let env = SessionEnv { + xdg_current_desktop: Some("GNOME".into()), + oc_no_decorations: Some("1".into()), + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn linux_decorations_native_override_wins() { + let env = SessionEnv { + xdg_current_desktop: Some("niri".into()), + wayland_display: true, + oc_linux_decorations: Some("native".into()), + ..Default::default() + }; + + assert!(use_decorations(&env)); + } + + #[test] + fn linux_decorations_none_override_wins() { + let env = SessionEnv { + xdg_current_desktop: Some("GNOME".into()), + wayland_display: true, + oc_linux_decorations: Some("none".into()), + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn linux_decorations_auto_uses_default_policy() { + let env = SessionEnv { + xdg_current_desktop: Some("sway".into()), + wayland_display: true, + oc_linux_decorations: Some("auto".into()), + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } + + #[test] + fn linux_decorations_override_beats_legacy_overrides() { + let env = SessionEnv { + xdg_current_desktop: Some("GNOME".into()), + wayland_display: true, + oc_linux_decorations: Some("none".into()), + oc_force_decorations: Some("1".into()), + ..Default::default() + }; + + assert!(!use_decorations(&env)); + } +} diff --git a/packages/desktop/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs index 9eb86cdacc84..c0ce2a445aeb 100644 --- a/packages/desktop/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -4,6 +4,7 @@ // borrowed from https://github.com/skyline69/balatro-mod-manager #[cfg(target_os = "linux")] fn configure_display_backend() -> Option { + use opencode_lib::linux_windowing::{Backend, SessionEnv, select_backend}; use std::env; let set_env_if_absent = |key: &str, value: &str| { @@ -14,45 +15,28 @@ fn configure_display_backend() -> Option { } }; - let on_wayland = env::var_os("WAYLAND_DISPLAY").is_some() - || matches!( - env::var("XDG_SESSION_TYPE"), - Ok(v) if v.eq_ignore_ascii_case("wayland") - ); - if !on_wayland { - return None; - } - + let session = SessionEnv::capture(); let prefer_wayland = opencode_lib::linux_display::read_wayland().unwrap_or(false); - let allow_wayland = prefer_wayland - || matches!( - env::var("OC_ALLOW_WAYLAND"), - Ok(v) if matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes") - ); - if allow_wayland { - if prefer_wayland { - return Some("Wayland session detected; using native Wayland from settings".into()); - } - return Some("Wayland session detected; respecting OC_ALLOW_WAYLAND=1".into()); - } + let decision = select_backend(&session, prefer_wayland)?; - // Prefer XWayland when available to avoid Wayland protocol errors seen during startup. - if env::var_os("DISPLAY").is_some() { - set_env_if_absent("WINIT_UNIX_BACKEND", "x11"); - set_env_if_absent("GDK_BACKEND", "x11"); - set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); - return Some( - "Wayland session detected; forcing X11 backend to avoid compositor protocol errors. \ - Set OC_ALLOW_WAYLAND=1 to keep native Wayland." - .into(), - ); + match decision.backend { + Backend::X11 => { + set_env_if_absent("WINIT_UNIX_BACKEND", "x11"); + set_env_if_absent("GDK_BACKEND", "x11"); + set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } + Backend::Wayland => { + set_env_if_absent("WINIT_UNIX_BACKEND", "wayland"); + set_env_if_absent("GDK_BACKEND", "wayland"); + set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } + Backend::Auto => { + set_env_if_absent("GDK_BACKEND", "wayland,x11"); + set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } } - set_env_if_absent("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); - Some( - "Wayland session detected without X11; leaving Wayland enabled (set WINIT_UNIX_BACKEND/GDK_BACKEND manually if needed)." - .into(), - ) + Some(decision.note) } fn main() { diff --git a/packages/desktop/src-tauri/src/windows.rs b/packages/desktop/src-tauri/src/windows.rs index 056720055b55..f361cbe38d5c 100644 --- a/packages/desktop/src-tauri/src/windows.rs +++ b/packages/desktop/src-tauri/src/windows.rs @@ -7,6 +7,22 @@ use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindo use tauri_plugin_window_state::AppHandleExt; use tokio::sync::mpsc; +#[cfg(target_os = "linux")] +use std::sync::OnceLock; + +#[cfg(target_os = "linux")] +fn use_decorations() -> bool { + static DECORATIONS: OnceLock = OnceLock::new(); + *DECORATIONS.get_or_init(|| { + crate::linux_windowing::use_decorations(&crate::linux_windowing::SessionEnv::capture()) + }) +} + +#[cfg(not(target_os = "linux"))] +fn use_decorations() -> bool { + true +} + pub struct MainWindow(WebviewWindow); impl Deref for MainWindow { @@ -31,13 +47,13 @@ impl MainWindow { .ok() .map(|v| v.enabled) .unwrap_or(false); - + let decorations = use_decorations(); let window_builder = base_window_config( WebviewWindowBuilder::new(app, Self::LABEL, WebviewUrl::App("/".into())), app, + decorations, ) .title("OpenCode") - .decorations(true) .disable_drag_drop_handler() .zoom_hotkeys_enabled(false) .visible(true) @@ -113,9 +129,12 @@ impl LoadingWindow { pub const LABEL: &str = "loading"; pub fn create(app: &AppHandle) -> Result { + let decorations = use_decorations(); + let window_builder = base_window_config( WebviewWindowBuilder::new(app, Self::LABEL, tauri::WebviewUrl::App("/loading".into())), app, + decorations, ) .center() .resizable(false) @@ -129,8 +148,9 @@ impl LoadingWindow { fn base_window_config<'a, R: Runtime, M: Manager>( window_builder: WebviewWindowBuilder<'a, R, M>, _app: &AppHandle, + decorations: bool, ) -> WebviewWindowBuilder<'a, R, M> { - let window_builder = window_builder.decorations(true); + let window_builder = window_builder.decorations(decorations); #[cfg(windows)] let window_builder = window_builder From f7708efa5b87ae292c973d3fb409d060b5ed8f56 Mon Sep 17 00:00:00 2001 From: Jhin Lee Date: Mon, 16 Feb 2026 03:31:48 -0500 Subject: [PATCH 016/255] feat: add openai-compatible endpoint support for google-vertex provider (#10303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: BlueT - Matthew Lien - 練喆明 Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline --- packages/opencode/src/provider/provider.ts | 65 ++++++++++++- .../opencode/test/provider/provider.test.ts | 92 +++++++++++++++++++ 2 files changed, 153 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 44bcf8adb3de..e2385f1ac2e2 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -57,6 +57,39 @@ export namespace Provider { return isGpt5OrLater(modelID) && !modelID.startsWith("gpt-5-mini") } + function googleVertexVars(options: Record) { + const project = + Env.get("GOOGLE_VERTEX_PROJECT") ?? + options["project"] ?? + Env.get("GOOGLE_CLOUD_PROJECT") ?? + Env.get("GCP_PROJECT") ?? + Env.get("GCLOUD_PROJECT") + const location = + Env.get("GOOGLE_VERTEX_LOCATION") ?? + options["location"] ?? + Env.get("GOOGLE_CLOUD_LOCATION") ?? + Env.get("VERTEX_LOCATION") ?? + "us-central1" + const endpoint = + Env.get("GOOGLE_VERTEX_ENDPOINT") ?? + (location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`) + return { + GOOGLE_VERTEX_PROJECT: project, + GOOGLE_VERTEX_LOCATION: location, + GOOGLE_VERTEX_ENDPOINT: endpoint, + } + } + + function loadBaseURL(model: Model, options: Record) { + const raw = options["baseURL"] ?? model.api.url + if (typeof raw !== "string") return raw + const vars = model.providerID === "google-vertex" ? googleVertexVars(options) : undefined + return raw.replace(/\$\{([^}]+)\}/g, (match, key) => { + const val = Env.get(String(key)) ?? vars?.[String(key) as keyof typeof vars] + return val ?? match + }) + } + const BUNDLED_PROVIDERS: Record SDK> = { "@ai-sdk/amazon-bedrock": createAmazonBedrock, "@ai-sdk/anthropic": createAnthropic, @@ -353,9 +386,16 @@ export namespace Provider { }, } }, - "google-vertex": async () => { - const project = Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT") - const location = Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-east5" + "google-vertex": async (provider) => { + const project = + provider.options?.project ?? + Env.get("GOOGLE_CLOUD_PROJECT") ?? + Env.get("GCP_PROJECT") ?? + Env.get("GCLOUD_PROJECT") + + const location = + provider.options?.location ?? Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-central1" + const autoload = Boolean(project) if (!autoload) return { autoload: false } return { @@ -363,6 +403,18 @@ export namespace Provider { options: { project, location, + fetch: async (input: RequestInfo | URL, init?: RequestInit) => { + const { GoogleAuth } = await import(await BunProc.install("google-auth-library")) + const auth = new GoogleAuth() + const client = await auth.getApplicationDefault() + const credentials = await client.credential + const token = await credentials.getAccessToken() + + const headers = new Headers(init?.headers) + headers.set("Authorization", `Bearer ${token.token}`) + + return fetch(input, { ...init, headers }) + }, }, async getModel(sdk: any, modelID: string) { const id = String(modelID).trim() @@ -994,11 +1046,16 @@ export namespace Provider { const provider = s.providers[model.providerID] const options = { ...provider.options } + if (model.providerID === "google-vertex" && !model.api.npm.includes("@ai-sdk/openai-compatible")) { + delete options.fetch + } + if (model.api.npm.includes("@ai-sdk/openai-compatible") && options["includeUsage"] !== false) { options["includeUsage"] = true } - if (!options["baseURL"]) options["baseURL"] = model.api.url + const baseURL = loadBaseURL(model, options) + if (baseURL !== undefined) options["baseURL"] = baseURL if (options["apiKey"] === undefined && provider.key) options["apiKey"] = provider.key if (model.headers) options["headers"] = { diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 98cd49c02fd3..87ff9d9d07f0 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -2127,3 +2127,95 @@ test("custom model with variants enabled and disabled", async () => { }, }) }) + +test("Google Vertex: retains baseURL for custom proxy", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + provider: { + "vertex-proxy": { + name: "Vertex Proxy", + npm: "@ai-sdk/google-vertex", + api: "https://my-proxy.com/v1", + env: ["GOOGLE_APPLICATION_CREDENTIALS"], // Mock env var requirement + models: { + "gemini-pro": { + name: "Gemini Pro", + tool_call: true, + }, + }, + options: { + project: "test-project", + location: "us-central1", + baseURL: "https://my-proxy.com/v1", // Should be retained + }, + }, + }, + }), + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + init: async () => { + Env.set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds") + }, + fn: async () => { + const providers = await Provider.list() + expect(providers["vertex-proxy"]).toBeDefined() + expect(providers["vertex-proxy"].options.baseURL).toBe("https://my-proxy.com/v1") + }, + }) +}) + +test("Google Vertex: supports OpenAI compatible models", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + provider: { + "vertex-openai": { + name: "Vertex OpenAI", + npm: "@ai-sdk/google-vertex", + env: ["GOOGLE_APPLICATION_CREDENTIALS"], + models: { + "gpt-4": { + name: "GPT-4", + provider: { + npm: "@ai-sdk/openai-compatible", + api: "https://api.openai.com/v1", + }, + }, + }, + options: { + project: "test-project", + location: "us-central1", + }, + }, + }, + }), + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + init: async () => { + Env.set("GOOGLE_APPLICATION_CREDENTIALS", "test-creds") + }, + fn: async () => { + const providers = await Provider.list() + const model = providers["vertex-openai"].models["gpt-4"] + + expect(model).toBeDefined() + expect(model.api.npm).toBe("@ai-sdk/openai-compatible") + }, + }) +}) + From 089ab9defabc5887f741d8ae777249689bc0d2bf Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Mon, 16 Feb 2026 08:32:34 +0000 Subject: [PATCH 017/255] chore: generate --- packages/opencode/test/provider/provider.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 87ff9d9d07f0..0a5aa415131c 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -2218,4 +2218,3 @@ test("Google Vertex: supports OpenAI compatible models", async () => { }, }) }) - From 1d041c8861cdeb72fa2f31020991860a2cde8c28 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 16 Feb 2026 02:41:52 -0600 Subject: [PATCH 018/255] fix: google vertex var priority (#13816) --- packages/opencode/src/provider/provider.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index e2385f1ac2e2..cdbad6637848 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -59,20 +59,11 @@ export namespace Provider { function googleVertexVars(options: Record) { const project = - Env.get("GOOGLE_VERTEX_PROJECT") ?? - options["project"] ?? - Env.get("GOOGLE_CLOUD_PROJECT") ?? - Env.get("GCP_PROJECT") ?? - Env.get("GCLOUD_PROJECT") + options["project"] ?? Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT") const location = - Env.get("GOOGLE_VERTEX_LOCATION") ?? - options["location"] ?? - Env.get("GOOGLE_CLOUD_LOCATION") ?? - Env.get("VERTEX_LOCATION") ?? - "us-central1" - const endpoint = - Env.get("GOOGLE_VERTEX_ENDPOINT") ?? - (location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`) + options["location"] ?? Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-central1" + const endpoint = location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com` + return { GOOGLE_VERTEX_PROJECT: project, GOOGLE_VERTEX_LOCATION: location, From 3ebf27aab92ac9c25b24f18c7fbd151da0f778ea Mon Sep 17 00:00:00 2001 From: Chujiang <110hqc@gmail.com> Date: Mon, 16 Feb 2026 20:02:48 +0800 Subject: [PATCH 019/255] fix(docs): correct critical translation errors in Russian zen page (#13830) --- packages/web/src/content/docs/ru/zen.mdx | 68 ++++++++++++------------ 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/web/src/content/docs/ru/zen.mdx b/packages/web/src/content/docs/ru/zen.mdx index d0563bdcdf6d..3fe03a47fac1 100644 --- a/packages/web/src/content/docs/ru/zen.mdx +++ b/packages/web/src/content/docs/ru/zen.mdx @@ -1,29 +1,29 @@ --- title: Zen -description: Кураторский список моделей, предоставленный opencode. +description: Подобранный список моделей, предоставленный OpenCode. --- import config from "../../../../config.mjs" export const console = config.console export const email = `mailto:${config.email}` -OpenCode Zen — это список протестированных и проверенных моделей, предоставленный командой opencode. +OpenCode Zen — это список протестированных и проверенных моделей, предоставленный командой OpenCode. :::note OpenCode Zen в настоящее время находится в стадии бета-тестирования. ::: -Zen работает как любой другой провайдер в opencode. Вы входите в OpenCode Zen и получаете +Zen работает как любой другой провайдер в OpenCode. Вы входите в OpenCode Zen и получаете ваш ключ API. Это **совершенно необязательно**, и вам не обязательно использовать его для использования -Открытый код. +OpenCode. --- ## Предыстория -Существует большое количество моделей, но лишь некоторые из них. -эти модели хорошо работают в качестве агентов кодирования. Кроме того, большинство провайдеров -настроен совсем по-другому; так что вы получите совсем другую производительность и качество. +Существует большое количество моделей, но лишь некоторые из них +хорошо работают в качестве кодинг-агентов. Кроме того, большинство провайдеров +настроены совсем по-другому; так что вы получите совсем другую производительность и качество. :::tip Мы протестировали избранную группу моделей и поставщиков, которые хорошо работают с opencode. @@ -36,10 +36,9 @@ Zen работает как любой другой провайдер в openco 1. Мы протестировали избранную группу моделей и поговорили с их командами о том, как лучше всего запустить их. -2. Затем мы поработали с несколькими поставщиками услуг, чтобы убедиться, что они обслуживаются. - правильно. -3. Наконец, мы сравнили комбинацию модель/провайдер и пришли к выводу, что - со списком, который мы с удовольствием рекомендуем. +2. Затем мы поработали с несколькими поставщиками услуг, чтобы убедиться, что они обслуживаются правильно. +3. Наконец, мы сравнили комбинацию модель/провайдер и составили + список, который мы с удовольствием рекомендуем. OpenCode Zen — это шлюз искусственного интеллекта, который дает вам доступ к этим моделям. @@ -47,10 +46,10 @@ OpenCode Zen — это шлюз искусственного интеллект ## Как это работает -OpenCode Zen работает так же, как и любой другой поставщик opencode. +OpenCode Zen работает так же, как и любой другой поставщик OpenCode. -1. Вы входите в систему **OpenCode Zen**, добавляете свой платежный аккаунт. - подробности и скопируйте свой ключ API. +1. Вы входите в систему **OpenCode Zen**, добавляете платежные + данные и копируете свой ключ API. 2. Вы запускаете команду `/connect` в TUI, выбираете OpenCode Zen и вставляете свой ключ API. 3. Запустите `/models` в TUI, чтобы просмотреть список рекомендуемых нами моделей. @@ -82,8 +81,10 @@ OpenCode Zen работает так же, как и любой другой п | Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | | Gemini 3 Pro | gemini-3-pro | `https://opencode.ai/zen/v1/models/gemini-3-pro` | `@ai-sdk/google` | | Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` | +| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| MiniMax M2.1 Free | minimax-m2.1-free | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | +| GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.7 Free | glm-4.7-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.6 | glm-4.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | @@ -117,11 +118,14 @@ https://opencode.ai/zen/v1/models | Модель | Вход | Выход | Кэшированное чтение | Кэшированная запись | | -------------------------------------- | --------- | --------- | ------------------- | ------------------- | | Big Pickle | Бесплатно | Бесплатно | Бесплатно | - | -| MiniMax M2.1 Free | Бесплатно | Бесплатно | Бесплатно | - | +| MiniMax M2.5 Free | Бесплатно | Бесплатно | Бесплатно | - | +| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | - | | MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | -| GLM 4.7 Free | Бесплатно | Бесплатно | Бесплатно | - | +| GLM 5 | $1.00 | $3.20 | $0.20 | - | +| GLM 4.7 | $0.60 | $2.20 | $0.10 | - | | GLM 4.7 | $0.60 | $2.20 | $0.10 | - | | GLM 4.6 | $0.60 | $2.20 | $0.10 | - | +| GLM 4.7 Free | Бесплатно | Бесплатно | Бесплатно | - | | Kimi K2.5 Free | Бесплатно | Бесплатно | Бесплатно | - | | Kimi K2.5 | $0.60 | $3.00 | $0.08 | - | | Kimi K2 Thinking | $0.40 | $2.50 | - | - | @@ -158,10 +162,9 @@ https://opencode.ai/zen/v1/models Бесплатные модели: -- GLM 4.7 Free доступен на opencode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. -- Kimi K2.5 Free доступен на opencode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. -- MiniMax M2.1 Free доступен на opencode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. -- Big Pickle — это стелс-модель, которая доступна бесплатно на opencode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. +- Kimi K2.5 Free доступен на OpenCode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. +- MiniMax M2.5 Free доступен на OpenCode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. +- Big Pickle — это стелс-модель, которая доступна бесплатно на OpenCode в течение ограниченного времени. Команда использует это время для сбора отзывов и улучшения модели. Свяжитесь с нами, если у вас есть вопросы. @@ -171,7 +174,7 @@ https://opencode.ai/zen/v1/models Если ваш баланс упадет ниже 5 долларов, Zen автоматически пополнит 20 долларов. -Вы можете изменить сумму автопополнения. Вы также можете полностью отключить автоматическую перезагрузку. +Вы можете изменить сумму автопополнения. Вы также можете полностью отключить автопополнение. --- @@ -181,7 +184,7 @@ https://opencode.ai/zen/v1/models член вашей команды. Например, предположим, что вы установили ежемесячный лимит использования в размере 20 долларов США, Zen не будет использовать -более 20 долларов в месяц. Но если у вас включена автоматическая перезагрузка, Дзен может оказаться +более 20 долларов в месяц. Но если у вас включено автопополнение, Zen может взимать с вас более 20 долларов США, если ваш баланс опускается ниже 5 долларов США. --- @@ -191,9 +194,8 @@ https://opencode.ai/zen/v1/models Все наши модели размещены в США. Наши поставщики придерживаются политики нулевого хранения и не используют ваши данные для обучения моделей, за следующими исключениями: - Big Pickle: во время бесплатного периода собранные данные могут быть использованы для улучшения модели. -- GLM 4.7 Free: в течение бесплатного периода собранные данные могут использоваться для улучшения модели. - Kimi K2.5 Free: в течение бесплатного периода собранные данные могут использоваться для улучшения модели. -- MiniMax M2.1 Free: в течение бесплатного периода собранные данные могут использоваться для улучшения модели. +- MiniMax M2.5 Free: в течение бесплатного периода собранные данные могут использоваться для улучшения модели. - API OpenAI: запросы хранятся в течение 30 дней в соответствии с [Политикой данных OpenAI](https://platform.openai.com/docs/guides/your-data). - API-интерфейсы Anthropic: запросы хранятся в течение 30 дней в соответствии с [Политикой данных Anthropic](https://docs.anthropic.com/en/docs/claude-code/data-usage). @@ -201,15 +203,15 @@ https://opencode.ai/zen/v1/models ## Для команд -Дзен также отлично подходит для команд. Вы можете приглашать товарищей по команде, назначать роли, курировать +Zen также отлично подходит для команд. Вы можете приглашать товарищей по команде, назначать роли, выбирать модели, которые использует ваша команда, и многое другое. :::note Рабочие пространства в настоящее время бесплатны для команд в рамках бета-тестирования. ::: -Управление вашим рабочим пространством в настоящее время бесплатно для команд в рамках бета-тестирования. Мы будем -скоро поделимся более подробной информацией о ценах. +Управление вашим рабочим пространством в настоящее время бесплатно для команд в рамках бета-тестирования. Мы вскоре +поделимся более подробной информацией о ценах. --- @@ -233,7 +235,7 @@ https://opencode.ai/zen/v1/models --- -### Принесите свой ключ +### Использование собственных API-ключей Вы можете использовать свои собственные ключи API OpenAI или Anthropic, сохраняя при этом доступ к другим моделям в Zen. @@ -248,7 +250,7 @@ https://opencode.ai/zen/v1/models Мы создали OpenCode Zen, чтобы: -1. **Сравните** лучшие модели/поставщики агентов кодирования. -2. Получите доступ к вариантам **самого высокого качества**, не снижая производительность и не обращаясь к более дешевым поставщикам. -3. Не допускайте **падения цен**, продавая по себестоимости; поэтому единственная надбавка предназначена для покрытия наших сборов за обработку. -4. Не допускайте **привязки**, позволяя использовать его с любым другим агентом кодирования. И всегда позволяйте вам использовать любого другого провайдера с opencode. +1. **Сравнить** лучшие модели/поставщики кодинг-агентов. +2. Получить доступ к вариантам **наивысшего качества**, не снижая производительность и не обращаясь к более дешевым поставщикам. +3. Передавать **снижение цен**, продавая по себестоимости; поэтому единственная наценка предназначена для покрытия наших комиссий за обработку. +4. Исключить **привязку**, позволяя использовать его с любым другим кодинг-агентом. И всегда позволяя вам использовать любого другого провайдера с OpenCode. From 45fa5e7199b2306395e1d07b9544f2e7dbd1c9a5 Mon Sep 17 00:00:00 2001 From: Rafi Khardalian Date: Mon, 16 Feb 2026 04:04:20 -0800 Subject: [PATCH 020/255] fix(core): remove unnecessary per-message title LLM calls (#13804) --- packages/opencode/src/session/summary.ts | 43 ------------------------ 1 file changed, 43 deletions(-) diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index c3e14ddd691f..349336ba788f 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -1,5 +1,3 @@ -import { Provider } from "@/provider/provider" - import { fn } from "@/util/fn" import z from "zod" import { Session } from "." @@ -8,16 +6,10 @@ import { MessageV2 } from "./message-v2" import { Identifier } from "@/id/id" import { Snapshot } from "@/snapshot" -import { Log } from "@/util/log" import { Storage } from "@/storage/storage" import { Bus } from "@/bus" -import { LLM } from "./llm" -import { Agent } from "@/agent/agent" - export namespace SessionSummary { - const log = Log.create({ service: "session.summary" }) - function unquoteGitPath(input: string) { if (!input.startsWith('"')) return input if (!input.endsWith('"')) return input @@ -117,41 +109,6 @@ export namespace SessionSummary { diffs, } await Session.updateMessage(userMsg) - - const textPart = msgWithParts.parts.find((p) => p.type === "text" && !p.synthetic) as MessageV2.TextPart - if (textPart && !userMsg.summary?.title) { - const agent = await Agent.get("title") - if (!agent) return - const stream = await LLM.stream({ - agent, - user: userMsg, - tools: {}, - model: agent.model - ? await Provider.getModel(agent.model.providerID, agent.model.modelID) - : ((await Provider.getSmallModel(userMsg.model.providerID)) ?? - (await Provider.getModel(userMsg.model.providerID, userMsg.model.modelID))), - small: true, - messages: [ - { - role: "user" as const, - content: ` - The following is the text to summarize: - - ${textPart?.text ?? ""} - - `, - }, - ], - abort: new AbortController().signal, - sessionID: userMsg.sessionID, - system: [], - retries: 3, - }) - const result = await stream.text - log.info("title", { title: result }) - userMsg.summary.title = result - await Session.updateMessage(userMsg) - } } export const diff = fn( From b055f973dfd66965d998216db67df8534957e5e8 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 16 Feb 2026 07:58:18 -0600 Subject: [PATCH 021/255] chore: cleanup --- packages/app/src/pages/session.tsx | 28 -- .../app/src/pages/session/helpers.test.ts | 16 +- packages/app/src/pages/session/helpers.ts | 5 - .../pages/session/use-session-commands.tsx | 366 +++++++++--------- 4 files changed, 194 insertions(+), 221 deletions(-) diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 41646d2f6ce1..d958990c25ab 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -23,7 +23,6 @@ import { useSync } from "@/context/sync" import { useTerminal, type LocalPTY } from "@/context/terminal" import { useLayout } from "@/context/layout" import { checksum, base64Encode } from "@opencode-ai/util/encode" -import { findLast } from "@opencode-ai/util/array" import { useDialog } from "@opencode-ai/ui/context/dialog" import { DialogSelectFile } from "@/components/dialog-select-file" import FileTree from "@/components/file-tree" @@ -35,7 +34,6 @@ import { useSDK } from "@/context/sdk" import { usePrompt } from "@/context/prompt" import { useComments } from "@/context/comments" import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd" -import { usePermission } from "@/context/permission" import { showToast } from "@opencode-ai/ui/toast" import { SessionHeader, SessionContextTab, SortableTab, FileVisual, NewSessionView } from "@/components/session" import { navMark, navParams } from "@/utils/perf" @@ -101,7 +99,6 @@ export default function Page() { const sdk = useSDK() const prompt = usePrompt() const comments = useComments() - const permission = usePermission() const permRequest = createMemo(() => { const sessionID = params.id @@ -770,11 +767,6 @@ export default function Page() { return lines.slice(0, 2).join("\n") } - const addSelectionToContext = (path: string, selection: FileSelection) => { - const preview = selectionPreview(path, selection) - prompt.context.add({ type: "file", path, selection, preview }) - } - const addCommentToContext = (input: { file: string selection: SelectedLineRange @@ -913,31 +905,11 @@ export default function Page() { const focusInput = () => inputRef?.focus() useSessionCommands({ - command, - dialog, - file, - language, - local, - permission, - prompt, - sdk, - sync, - terminal, - layout, - params, - navigate, - tabs, - view, - info, - status, - userMessages, - visibleUserMessages, activeMessage, showAllFiles, navigateMessageByOffset, setExpanded: (id, fn) => setStore("expanded", id, fn), setActiveMessage, - addSelectionToContext, focusInput, }) diff --git a/packages/app/src/pages/session/helpers.test.ts b/packages/app/src/pages/session/helpers.test.ts index d877d5b2e221..8b9746507ef3 100644 --- a/packages/app/src/pages/session/helpers.test.ts +++ b/packages/app/src/pages/session/helpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import { combineCommandSections, createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "./helpers" +import { createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "./helpers" describe("createOpenReviewFile", () => { test("opens and loads selected review file", () => { @@ -46,20 +46,6 @@ describe("focusTerminalById", () => { }) }) -describe("combineCommandSections", () => { - test("keeps section order stable", () => { - const result = combineCommandSections([ - [{ id: "a", title: "A" }], - [ - { id: "b", title: "B" }, - { id: "c", title: "C" }, - ], - ]) - - expect(result.map((item) => item.id)).toEqual(["a", "b", "c"]) - }) -}) - describe("getTabReorderIndex", () => { test("returns target index for valid drag reorder", () => { expect(getTabReorderIndex(["a", "b", "c"], "a", "c")).toBe(2) diff --git a/packages/app/src/pages/session/helpers.ts b/packages/app/src/pages/session/helpers.ts index 6ead7a7f2f42..5ca355d1d291 100644 --- a/packages/app/src/pages/session/helpers.ts +++ b/packages/app/src/pages/session/helpers.ts @@ -1,4 +1,3 @@ -import type { CommandOption } from "@/context/command" import { batch } from "solid-js" export const focusTerminalById = (id: string) => { @@ -36,10 +35,6 @@ export const createOpenReviewFile = (input: { } } -export const combineCommandSections = (sections: readonly (readonly CommandOption[])[]) => { - return sections.flatMap((section) => section) -} - export const getTabReorderIndex = (tabs: readonly string[], from: string, to: string) => { const fromIndex = tabs.indexOf(from) const toIndex = tabs.indexOf(to) diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx index 81c71133f641..d2f74288f6b0 100644 --- a/packages/app/src/pages/session/use-session-commands.tsx +++ b/packages/app/src/pages/session/use-session-commands.tsx @@ -19,35 +19,14 @@ import { showToast } from "@opencode-ai/ui/toast" import { findLast } from "@opencode-ai/util/array" import { extractPromptFromParts } from "@/utils/prompt" import { UserMessage } from "@opencode-ai/sdk/v2" -import { combineCommandSections } from "@/pages/session/helpers" import { canAddSelectionContext } from "@/pages/session/session-command-helpers" export type SessionCommandContext = { - command: ReturnType - dialog: ReturnType - file: ReturnType - language: ReturnType - local: ReturnType - permission: ReturnType - prompt: ReturnType - sdk: ReturnType - sync: ReturnType - terminal: ReturnType - layout: ReturnType - params: ReturnType - navigate: ReturnType - tabs: () => ReturnType["tabs"]> - view: () => ReturnType["view"]> - info: () => { revert?: { messageID?: string }; share?: { url?: string } } | undefined - status: () => { type: string } - userMessages: () => UserMessage[] - visibleUserMessages: () => UserMessage[] activeMessage: () => UserMessage | undefined showAllFiles: () => void navigateMessageByOffset: (offset: number) => void setExpanded: (id: string, fn: (open: boolean | undefined) => boolean) => void setActiveMessage: (message: UserMessage | undefined) => void - addSelectionToContext: (path: string, selection: FileSelection) => void focusInput: () => void } @@ -58,45 +37,88 @@ const withCategory = (category: string) => { }) } -export const useSessionCommands = (input: SessionCommandContext) => { - const sessionCommand = withCategory(input.language.t("command.category.session")) - const fileCommand = withCategory(input.language.t("command.category.file")) - const contextCommand = withCategory(input.language.t("command.category.context")) - const viewCommand = withCategory(input.language.t("command.category.view")) - const terminalCommand = withCategory(input.language.t("command.category.terminal")) - const modelCommand = withCategory(input.language.t("command.category.model")) - const mcpCommand = withCategory(input.language.t("command.category.mcp")) - const agentCommand = withCategory(input.language.t("command.category.agent")) - const permissionsCommand = withCategory(input.language.t("command.category.permissions")) +export const useSessionCommands = (args: SessionCommandContext) => { + const command = useCommand() + const dialog = useDialog() + const file = useFile() + const language = useLanguage() + const local = useLocal() + const permission = usePermission() + const prompt = usePrompt() + const sdk = useSDK() + const sync = useSync() + const terminal = useTerminal() + const layout = useLayout() + const params = useParams() + const navigate = useNavigate() + + const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) + const tabs = createMemo(() => layout.tabs(sessionKey)) + const view = createMemo(() => layout.view(sessionKey)) + const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined)) + const idle = { type: "idle" as const } + const status = createMemo(() => sync.data.session_status[params.id ?? ""] ?? idle) + const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : [])) + const userMessages = createMemo(() => messages().filter((m) => m.role === "user") as UserMessage[]) + const visibleUserMessages = createMemo(() => { + const revert = info()?.revert?.messageID + if (!revert) return userMessages() + return userMessages().filter((m) => m.id < revert) + }) + + const selectionPreview = (path: string, selection: FileSelection) => { + const content = file.get(path)?.content?.content + if (!content) return undefined + const start = Math.max(1, Math.min(selection.startLine, selection.endLine)) + const end = Math.max(selection.startLine, selection.endLine) + const lines = content.split("\n").slice(start - 1, end) + if (lines.length === 0) return undefined + return lines.slice(0, 2).join("\n") + } + + const addSelectionToContext = (path: string, selection: FileSelection) => { + const preview = selectionPreview(path, selection) + prompt.context.add({ type: "file", path, selection, preview }) + } + + const sessionCommand = withCategory(language.t("command.category.session")) + const fileCommand = withCategory(language.t("command.category.file")) + const contextCommand = withCategory(language.t("command.category.context")) + const viewCommand = withCategory(language.t("command.category.view")) + const terminalCommand = withCategory(language.t("command.category.terminal")) + const modelCommand = withCategory(language.t("command.category.model")) + const mcpCommand = withCategory(language.t("command.category.mcp")) + const agentCommand = withCategory(language.t("command.category.agent")) + const permissionsCommand = withCategory(language.t("command.category.permissions")) const sessionCommands = createMemo(() => [ sessionCommand({ id: "session.new", - title: input.language.t("command.session.new"), + title: language.t("command.session.new"), keybind: "mod+shift+s", slash: "new", - onSelect: () => input.navigate(`/${input.params.dir}/session`), + onSelect: () => navigate(`/${params.dir}/session`), }), ]) const fileCommands = createMemo(() => [ fileCommand({ id: "file.open", - title: input.language.t("command.file.open"), - description: input.language.t("palette.search.placeholder"), + title: language.t("command.file.open"), + description: language.t("palette.search.placeholder"), keybind: "mod+p", slash: "open", - onSelect: () => input.dialog.show(() => ), + onSelect: () => dialog.show(() => ), }), fileCommand({ id: "tab.close", - title: input.language.t("command.tab.close"), + title: language.t("command.tab.close"), keybind: "mod+w", - disabled: !input.tabs().active(), + disabled: !tabs().active(), onSelect: () => { - const active = input.tabs().active() + const active = tabs().active() if (!active) return - input.tabs().close(active) + tabs().close(active) }, }), ]) @@ -104,30 +126,30 @@ export const useSessionCommands = (input: SessionCommandContext) => { const contextCommands = createMemo(() => [ contextCommand({ id: "context.addSelection", - title: input.language.t("command.context.addSelection"), - description: input.language.t("command.context.addSelection.description"), + title: language.t("command.context.addSelection"), + description: language.t("command.context.addSelection.description"), keybind: "mod+shift+l", disabled: !canAddSelectionContext({ - active: input.tabs().active(), - pathFromTab: input.file.pathFromTab, - selectedLines: input.file.selectedLines, + active: tabs().active(), + pathFromTab: file.pathFromTab, + selectedLines: file.selectedLines, }), onSelect: () => { - const active = input.tabs().active() + const active = tabs().active() if (!active) return - const path = input.file.pathFromTab(active) + const path = file.pathFromTab(active) if (!path) return - const range = input.file.selectedLines(path) as SelectedLineRange | null | undefined + const range = file.selectedLines(path) as SelectedLineRange | null | undefined if (!range) { showToast({ - title: input.language.t("toast.context.noLineSelection.title"), - description: input.language.t("toast.context.noLineSelection.description"), + title: language.t("toast.context.noLineSelection.title"), + description: language.t("toast.context.noLineSelection.description"), }) return } - input.addSelectionToContext(path, selectionFromLines(range)) + addSelectionToContext(path, selectionFromLines(range)) }, }), ]) @@ -135,50 +157,50 @@ export const useSessionCommands = (input: SessionCommandContext) => { const viewCommands = createMemo(() => [ viewCommand({ id: "terminal.toggle", - title: input.language.t("command.terminal.toggle"), + title: language.t("command.terminal.toggle"), keybind: "ctrl+`", slash: "terminal", - onSelect: () => input.view().terminal.toggle(), + onSelect: () => view().terminal.toggle(), }), viewCommand({ id: "review.toggle", - title: input.language.t("command.review.toggle"), + title: language.t("command.review.toggle"), keybind: "mod+shift+r", - onSelect: () => input.view().reviewPanel.toggle(), + onSelect: () => view().reviewPanel.toggle(), }), viewCommand({ id: "fileTree.toggle", - title: input.language.t("command.fileTree.toggle"), + title: language.t("command.fileTree.toggle"), keybind: "mod+\\", - onSelect: () => input.layout.fileTree.toggle(), + onSelect: () => layout.fileTree.toggle(), }), viewCommand({ id: "input.focus", - title: input.language.t("command.input.focus"), + title: language.t("command.input.focus"), keybind: "ctrl+l", - onSelect: () => input.focusInput(), + onSelect: () => args.focusInput(), }), terminalCommand({ id: "terminal.new", - title: input.language.t("command.terminal.new"), - description: input.language.t("command.terminal.new.description"), + title: language.t("command.terminal.new"), + description: language.t("command.terminal.new.description"), keybind: "ctrl+alt+t", onSelect: () => { - if (input.terminal.all().length > 0) input.terminal.new() - input.view().terminal.open() + if (terminal.all().length > 0) terminal.new() + view().terminal.open() }, }), viewCommand({ id: "steps.toggle", - title: input.language.t("command.steps.toggle"), - description: input.language.t("command.steps.toggle.description"), + title: language.t("command.steps.toggle"), + description: language.t("command.steps.toggle.description"), keybind: "mod+e", slash: "steps", - disabled: !input.params.id, + disabled: !params.id, onSelect: () => { - const msg = input.activeMessage() + const msg = args.activeMessage() if (!msg) return - input.setExpanded(msg.id, (open: boolean | undefined) => !open) + args.setExpanded(msg.id, (open: boolean | undefined) => !open) }, }), ]) @@ -186,61 +208,61 @@ export const useSessionCommands = (input: SessionCommandContext) => { const messageCommands = createMemo(() => [ sessionCommand({ id: "message.previous", - title: input.language.t("command.message.previous"), - description: input.language.t("command.message.previous.description"), + title: language.t("command.message.previous"), + description: language.t("command.message.previous.description"), keybind: "mod+arrowup", - disabled: !input.params.id, - onSelect: () => input.navigateMessageByOffset(-1), + disabled: !params.id, + onSelect: () => args.navigateMessageByOffset(-1), }), sessionCommand({ id: "message.next", - title: input.language.t("command.message.next"), - description: input.language.t("command.message.next.description"), + title: language.t("command.message.next"), + description: language.t("command.message.next.description"), keybind: "mod+arrowdown", - disabled: !input.params.id, - onSelect: () => input.navigateMessageByOffset(1), + disabled: !params.id, + onSelect: () => args.navigateMessageByOffset(1), }), ]) const agentCommands = createMemo(() => [ modelCommand({ id: "model.choose", - title: input.language.t("command.model.choose"), - description: input.language.t("command.model.choose.description"), + title: language.t("command.model.choose"), + description: language.t("command.model.choose.description"), keybind: "mod+'", slash: "model", - onSelect: () => input.dialog.show(() => ), + onSelect: () => dialog.show(() => ), }), mcpCommand({ id: "mcp.toggle", - title: input.language.t("command.mcp.toggle"), - description: input.language.t("command.mcp.toggle.description"), + title: language.t("command.mcp.toggle"), + description: language.t("command.mcp.toggle.description"), keybind: "mod+;", slash: "mcp", - onSelect: () => input.dialog.show(() => ), + onSelect: () => dialog.show(() => ), }), agentCommand({ id: "agent.cycle", - title: input.language.t("command.agent.cycle"), - description: input.language.t("command.agent.cycle.description"), + title: language.t("command.agent.cycle"), + description: language.t("command.agent.cycle.description"), keybind: "mod+.", slash: "agent", - onSelect: () => input.local.agent.move(1), + onSelect: () => local.agent.move(1), }), agentCommand({ id: "agent.cycle.reverse", - title: input.language.t("command.agent.cycle.reverse"), - description: input.language.t("command.agent.cycle.reverse.description"), + title: language.t("command.agent.cycle.reverse"), + description: language.t("command.agent.cycle.reverse.description"), keybind: "shift+mod+.", - onSelect: () => input.local.agent.move(-1), + onSelect: () => local.agent.move(-1), }), modelCommand({ id: "model.variant.cycle", - title: input.language.t("command.model.variant.cycle"), - description: input.language.t("command.model.variant.cycle.description"), + title: language.t("command.model.variant.cycle"), + description: language.t("command.model.variant.cycle.description"), keybind: "shift+mod+d", onSelect: () => { - input.local.model.variant.cycle() + local.model.variant.cycle() }, }), ]) @@ -249,22 +271,22 @@ export const useSessionCommands = (input: SessionCommandContext) => { permissionsCommand({ id: "permissions.autoaccept", title: - input.params.id && input.permission.isAutoAccepting(input.params.id, input.sdk.directory) - ? input.language.t("command.permissions.autoaccept.disable") - : input.language.t("command.permissions.autoaccept.enable"), + params.id && permission.isAutoAccepting(params.id, sdk.directory) + ? language.t("command.permissions.autoaccept.disable") + : language.t("command.permissions.autoaccept.enable"), keybind: "mod+shift+a", - disabled: !input.params.id || !input.permission.permissionsEnabled(), + disabled: !params.id || !permission.permissionsEnabled(), onSelect: () => { - const sessionID = input.params.id + const sessionID = params.id if (!sessionID) return - input.permission.toggleAutoAccept(sessionID, input.sdk.directory) + permission.toggleAutoAccept(sessionID, sdk.directory) showToast({ - title: input.permission.isAutoAccepting(sessionID, input.sdk.directory) - ? input.language.t("toast.permissions.autoaccept.on.title") - : input.language.t("toast.permissions.autoaccept.off.title"), - description: input.permission.isAutoAccepting(sessionID, input.sdk.directory) - ? input.language.t("toast.permissions.autoaccept.on.description") - : input.language.t("toast.permissions.autoaccept.off.description"), + title: permission.isAutoAccepting(sessionID, sdk.directory) + ? language.t("toast.permissions.autoaccept.on.title") + : language.t("toast.permissions.autoaccept.off.title"), + description: permission.isAutoAccepting(sessionID, sdk.directory) + ? language.t("toast.permissions.autoaccept.on.description") + : language.t("toast.permissions.autoaccept.off.description"), }) }, }), @@ -273,71 +295,71 @@ export const useSessionCommands = (input: SessionCommandContext) => { const sessionActionCommands = createMemo(() => [ sessionCommand({ id: "session.undo", - title: input.language.t("command.session.undo"), - description: input.language.t("command.session.undo.description"), + title: language.t("command.session.undo"), + description: language.t("command.session.undo.description"), slash: "undo", - disabled: !input.params.id || input.visibleUserMessages().length === 0, + disabled: !params.id || visibleUserMessages().length === 0, onSelect: async () => { - const sessionID = input.params.id + const sessionID = params.id if (!sessionID) return - if (input.status()?.type !== "idle") { - await input.sdk.client.session.abort({ sessionID }).catch(() => {}) + if (status()?.type !== "idle") { + await sdk.client.session.abort({ sessionID }).catch(() => {}) } - const revert = input.info()?.revert?.messageID - const message = findLast(input.userMessages(), (x) => !revert || x.id < revert) + const revert = info()?.revert?.messageID + const message = findLast(userMessages(), (x) => !revert || x.id < revert) if (!message) return - await input.sdk.client.session.revert({ sessionID, messageID: message.id }) - const parts = input.sync.data.part[message.id] + await sdk.client.session.revert({ sessionID, messageID: message.id }) + const parts = sync.data.part[message.id] if (parts) { - const restored = extractPromptFromParts(parts, { directory: input.sdk.directory }) - input.prompt.set(restored) + const restored = extractPromptFromParts(parts, { directory: sdk.directory }) + prompt.set(restored) } - const priorMessage = findLast(input.userMessages(), (x) => x.id < message.id) - input.setActiveMessage(priorMessage) + const priorMessage = findLast(userMessages(), (x) => x.id < message.id) + args.setActiveMessage(priorMessage) }, }), sessionCommand({ id: "session.redo", - title: input.language.t("command.session.redo"), - description: input.language.t("command.session.redo.description"), + title: language.t("command.session.redo"), + description: language.t("command.session.redo.description"), slash: "redo", - disabled: !input.params.id || !input.info()?.revert?.messageID, + disabled: !params.id || !info()?.revert?.messageID, onSelect: async () => { - const sessionID = input.params.id + const sessionID = params.id if (!sessionID) return - const revertMessageID = input.info()?.revert?.messageID + const revertMessageID = info()?.revert?.messageID if (!revertMessageID) return - const nextMessage = input.userMessages().find((x) => x.id > revertMessageID) + const nextMessage = userMessages().find((x) => x.id > revertMessageID) if (!nextMessage) { - await input.sdk.client.session.unrevert({ sessionID }) - input.prompt.reset() - const lastMsg = findLast(input.userMessages(), (x) => x.id >= revertMessageID) - input.setActiveMessage(lastMsg) + await sdk.client.session.unrevert({ sessionID }) + prompt.reset() + const lastMsg = findLast(userMessages(), (x) => x.id >= revertMessageID) + args.setActiveMessage(lastMsg) return } - await input.sdk.client.session.revert({ sessionID, messageID: nextMessage.id }) - const priorMsg = findLast(input.userMessages(), (x) => x.id < nextMessage.id) - input.setActiveMessage(priorMsg) + await sdk.client.session.revert({ sessionID, messageID: nextMessage.id }) + const priorMsg = findLast(userMessages(), (x) => x.id < nextMessage.id) + args.setActiveMessage(priorMsg) }, }), sessionCommand({ id: "session.compact", - title: input.language.t("command.session.compact"), - description: input.language.t("command.session.compact.description"), + title: language.t("command.session.compact"), + description: language.t("command.session.compact.description"), slash: "compact", - disabled: !input.params.id || input.visibleUserMessages().length === 0, + disabled: !params.id || visibleUserMessages().length === 0, onSelect: async () => { - const sessionID = input.params.id + const sessionID = params.id if (!sessionID) return - const model = input.local.model.current() + const model = local.model.current() if (!model) { showToast({ - title: input.language.t("toast.model.none.title"), - description: input.language.t("toast.model.none.description"), + title: language.t("toast.model.none.title"), + description: language.t("toast.model.none.description"), }) return } - await input.sdk.client.session.summarize({ + await sdk.client.session.summarize({ sessionID, modelID: model.id, providerID: model.provider.id, @@ -346,29 +368,27 @@ export const useSessionCommands = (input: SessionCommandContext) => { }), sessionCommand({ id: "session.fork", - title: input.language.t("command.session.fork"), - description: input.language.t("command.session.fork.description"), + title: language.t("command.session.fork"), + description: language.t("command.session.fork.description"), slash: "fork", - disabled: !input.params.id || input.visibleUserMessages().length === 0, - onSelect: () => input.dialog.show(() => ), + disabled: !params.id || visibleUserMessages().length === 0, + onSelect: () => dialog.show(() => ), }), ]) const shareCommands = createMemo(() => { - if (input.sync.data.config.share === "disabled") return [] + if (sync.data.config.share === "disabled") return [] return [ sessionCommand({ id: "session.share", - title: input.info()?.share?.url - ? input.language.t("session.share.copy.copyLink") - : input.language.t("command.session.share"), - description: input.info()?.share?.url - ? input.language.t("toast.session.share.success.description") - : input.language.t("command.session.share.description"), + title: info()?.share?.url ? language.t("session.share.copy.copyLink") : language.t("command.session.share"), + description: info()?.share?.url + ? language.t("toast.session.share.success.description") + : language.t("command.session.share.description"), slash: "share", - disabled: !input.params.id, + disabled: !params.id, onSelect: async () => { - if (!input.params.id) return + if (!params.id) return const write = (value: string) => { const body = typeof document === "undefined" ? undefined : document.body @@ -398,7 +418,7 @@ export const useSessionCommands = (input: SessionCommandContext) => { const ok = await write(url) if (!ok) { showToast({ - title: input.language.t("toast.session.share.copyFailed.title"), + title: language.t("toast.session.share.copyFailed.title"), variant: "error", }) return @@ -406,27 +426,27 @@ export const useSessionCommands = (input: SessionCommandContext) => { showToast({ title: existing - ? input.language.t("session.share.copy.copied") - : input.language.t("toast.session.share.success.title"), - description: input.language.t("toast.session.share.success.description"), + ? language.t("session.share.copy.copied") + : language.t("toast.session.share.success.title"), + description: language.t("toast.session.share.success.description"), variant: "success", }) } - const existing = input.info()?.share?.url + const existing = info()?.share?.url if (existing) { await copy(existing, true) return } - const url = await input.sdk.client.session - .share({ sessionID: input.params.id }) + const url = await sdk.client.session + .share({ sessionID: params.id }) .then((res) => res.data?.share?.url) .catch(() => undefined) if (!url) { showToast({ - title: input.language.t("toast.session.share.failed.title"), - description: input.language.t("toast.session.share.failed.description"), + title: language.t("toast.session.share.failed.title"), + description: language.t("toast.session.share.failed.description"), variant: "error", }) return @@ -437,25 +457,25 @@ export const useSessionCommands = (input: SessionCommandContext) => { }), sessionCommand({ id: "session.unshare", - title: input.language.t("command.session.unshare"), - description: input.language.t("command.session.unshare.description"), + title: language.t("command.session.unshare"), + description: language.t("command.session.unshare.description"), slash: "unshare", - disabled: !input.params.id || !input.info()?.share?.url, + disabled: !params.id || !info()?.share?.url, onSelect: async () => { - if (!input.params.id) return - await input.sdk.client.session - .unshare({ sessionID: input.params.id }) + if (!params.id) return + await sdk.client.session + .unshare({ sessionID: params.id }) .then(() => showToast({ - title: input.language.t("toast.session.unshare.success.title"), - description: input.language.t("toast.session.unshare.success.description"), + title: language.t("toast.session.unshare.success.title"), + description: language.t("toast.session.unshare.success.description"), variant: "success", }), ) .catch(() => showToast({ - title: input.language.t("toast.session.unshare.failed.title"), - description: input.language.t("toast.session.unshare.failed.description"), + title: language.t("toast.session.unshare.failed.title"), + description: language.t("toast.session.unshare.failed.description"), variant: "error", }), ) @@ -464,8 +484,8 @@ export const useSessionCommands = (input: SessionCommandContext) => { ] }) - input.command.register("session", () => - combineCommandSections([ + command.register("session", () => + [ sessionCommands(), fileCommands(), contextCommands(), @@ -475,6 +495,6 @@ export const useSessionCommands = (input: SessionCommandContext) => { permissionCommands(), sessionActionCommands(), shareCommands(), - ]), + ].flatMap((section) => section), ) } From bb30e06855fb979b5fd765796a6b7428b9177b91 Mon Sep 17 00:00:00 2001 From: Imanol Maiztegui Date: Mon, 16 Feb 2026 15:08:04 +0100 Subject: [PATCH 022/255] fix (tui): Inaccurate tips (#13845) --- packages/opencode/src/cli/cmd/tui/component/tips.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/tips.tsx b/packages/opencode/src/cli/cmd/tui/component/tips.tsx index 7870ab2ea448..d0a7e5b44eca 100644 --- a/packages/opencode/src/cli/cmd/tui/component/tips.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/tips.tsx @@ -2,7 +2,7 @@ import { createMemo, createSignal, For } from "solid-js" import { DEFAULT_THEMES, useTheme } from "@tui/context/theme" const themeCount = Object.keys(DEFAULT_THEMES).length -const themeTip = `Use {highlight}/theme{/highlight} or {highlight}Ctrl+X T{/highlight} to switch between ${themeCount} built-in themes` +const themeTip = `Use {highlight}/themes{/highlight} or {highlight}Ctrl+X T{/highlight} to switch between ${themeCount} built-in themes` type TipPart = { text: string; highlight: boolean } @@ -126,7 +126,7 @@ const TIPS = [ "Use {highlight}{file:path}{/highlight} to include file contents in config values", "Use {highlight}instructions{/highlight} in config to load additional rules files", "Set agent {highlight}temperature{/highlight} from 0.0 (focused) to 1.0 (creative)", - "Configure {highlight}maxSteps{/highlight} to limit agentic iterations per request", + "Configure {highlight}steps{/highlight} to limit agentic iterations per request", 'Set {highlight}"tools": {"bash": false}{/highlight} to disable specific tools', 'Set {highlight}"mcp_*": false{/highlight} to disable all tools from an MCP server', "Override global tool settings per agent configuration", @@ -147,7 +147,6 @@ const TIPS = [ "Commit your project's {highlight}AGENTS.md{/highlight} file to Git for team sharing", "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}/details{/highlight} to toggle tool execution details visibility", "Use {highlight}/rename{/highlight} to rename the current session", "Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell", ] From ef979ccfa899fe520d1cb15314dfbd487206a507 Mon Sep 17 00:00:00 2001 From: Vladimir Glafirov Date: Mon, 16 Feb 2026 17:01:17 +0100 Subject: [PATCH 023/255] fix: bump GitLab provider and auth plugin for mid-session token refresh (#13850) --- bun.lock | 8 ++++---- packages/opencode/package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index a29f00687886..c5cffffacb6f 100644 --- a/bun.lock +++ b/bun.lock @@ -288,8 +288,8 @@ "@ai-sdk/vercel": "1.0.33", "@ai-sdk/xai": "2.0.51", "@clack/prompts": "1.0.0-alpha.1", - "@gitlab/gitlab-ai-provider": "3.5.0", - "@gitlab/opencode-gitlab-auth": "1.3.2", + "@gitlab/gitlab-ai-provider": "3.5.1", + "@gitlab/opencode-gitlab-auth": "1.3.3", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "1.25.2", @@ -989,9 +989,9 @@ "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="], - "@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.5.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-OoAwCz4fOci3h/2l+PRHMclclh3IaFq8w1es2wvBJ8ca7vtglKsBYT7dvmYpsXlu7pg9mopbjcexvmVCQEUTAQ=="], + "@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.5.1", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-I8+EGdUeKmGJSjAdFobHtqpxM9Fm00w0j7NJbtln/D/XQ1SKEGoZIuqJko4v0pV2mkhGUIs7qezljH/2kbXovA=="], - "@gitlab/opencode-gitlab-auth": ["@gitlab/opencode-gitlab-auth@1.3.2", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-pvGrC+aDVLY8bRCC/fZaG/Qihvt2r4by5xbTo5JTSz9O7yIcR6xG2d9Wkuu4bcXFz674z2C+i5bUk+J/RSdBpg=="], + "@gitlab/opencode-gitlab-auth": ["@gitlab/opencode-gitlab-auth@1.3.3", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-FT+KsCmAJjtqWr1YAq0MywGgL9kaLQ4apmsoowAXrPqHtoYf2i/nY10/A+L06kNj22EATeEDRpbB1NWXMto/SA=="], "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 99be25372fb7..e3e4bc759a38 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -74,8 +74,8 @@ "@ai-sdk/vercel": "1.0.33", "@ai-sdk/xai": "2.0.51", "@clack/prompts": "1.0.0-alpha.1", - "@gitlab/gitlab-ai-provider": "3.5.0", - "@gitlab/opencode-gitlab-auth": "1.3.2", + "@gitlab/gitlab-ai-provider": "3.5.1", + "@gitlab/opencode-gitlab-auth": "1.3.3", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "1.25.2", From 8c1af9b445a45128d147f6f818dfd3ed7c4e75ef Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Mon, 16 Feb 2026 17:38:43 +0000 Subject: [PATCH 024/255] 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 fde268ba83ad..3fa1455fc019 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-5pgd2xuvIIkTbIOGIdK5MIXo6O9qRpvk1RKQZ1e1R+8=", - "aarch64-linux": "sha256-FZiHwihM4b82ipQ9XfW08X+sd5CvZhx/+pU/8X1zsns=", - "aarch64-darwin": "sha256-iZv0w1NthV53pY5uvuf3JlI14GeKmCu7WHwGSRdEQeM=", - "x86_64-darwin": "sha256-c3Zm3P1goFPgg3vNAZPMFOhHX/gyTmsCN/PKbGO/v0E=" + "x86_64-linux": "sha256-C3WIEER2XgzO85wk2sp3BzQ6dknW026zslD8nKZjo2U=", + "aarch64-linux": "sha256-+tTJHZMZ/+8fAjI/1fUTuca8J2MZfB+5vhBoZ7jgqcE=", + "aarch64-darwin": "sha256-vS82puFGBBToxyIBa8Zi0KLKdJYr64T6HZL2rL32mH8=", + "x86_64-darwin": "sha256-Tr8JMTCxV6WVt3dXV7iq3PNCm2Cn+RXAbU9+o7pKKV0=" } } From 5cc1d6097e02e2f157b7ae68de9e5df06531b53d Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Mon, 16 Feb 2026 13:45:00 -0500 Subject: [PATCH 025/255] feat(cli): add --continue and --fork flags to attach command (#13879) --- packages/opencode/src/cli/cmd/tui/attach.ts | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/attach.ts b/packages/opencode/src/cli/cmd/tui/attach.ts index 8b8979c831c7..a2559cfce679 100644 --- a/packages/opencode/src/cli/cmd/tui/attach.ts +++ b/packages/opencode/src/cli/cmd/tui/attach.ts @@ -1,4 +1,5 @@ import { cmd } from "../cmd" +import { UI } from "@/cli/ui" import { tui } from "./app" import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32" @@ -16,11 +17,20 @@ export const AttachCommand = cmd({ type: "string", description: "directory to run in", }) + .option("continue", { + alias: ["c"], + describe: "continue the last session", + type: "boolean", + }) .option("session", { alias: ["s"], type: "string", describe: "session id to continue", }) + .option("fork", { + type: "boolean", + describe: "fork the session when continuing (use with --continue or --session)", + }) .option("password", { alias: ["p"], type: "string", @@ -31,6 +41,12 @@ export const AttachCommand = cmd({ try { win32DisableProcessedInput() + if (args.fork && !args.continue && !args.session) { + UI.error("--fork requires --continue or --session") + process.exitCode = 1 + return + } + const directory = (() => { if (!args.dir) return undefined try { @@ -49,7 +65,11 @@ export const AttachCommand = cmd({ })() await tui({ url: args.url, - args: { sessionID: args.session }, + args: { + continue: args.continue, + sessionID: args.session, + fork: args.fork, + }, directory, headers, }) From fdad823edc13fbc8fbaf4bf54eae53b1286ee2e9 Mon Sep 17 00:00:00 2001 From: Dax Date: Mon, 16 Feb 2026 14:05:21 -0500 Subject: [PATCH 026/255] feat(cli): add db migrate command for JSON to SQLite migration (#13874) --- packages/opencode/src/cli/cmd/db.ts | 52 ++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/db.ts b/packages/opencode/src/cli/cmd/db.ts index 0ade4d3c4bf8..8ca4b9a42eb2 100644 --- a/packages/opencode/src/cli/cmd/db.ts +++ b/packages/opencode/src/cli/cmd/db.ts @@ -4,6 +4,8 @@ import { Database } from "../../storage/db" import { Database as BunDatabase } from "bun:sqlite" import { UI } from "../ui" import { cmd } from "./cmd" +import { JsonMigration } from "../../storage/json-migration" +import { EOL } from "os" const QueryCommand = cmd({ command: "$0 [query]", @@ -58,11 +60,59 @@ const PathCommand = cmd({ }, }) +const MigrateCommand = cmd({ + command: "migrate", + describe: "migrate JSON data to SQLite (merges with existing data)", + handler: async () => { + const sqlite = new BunDatabase(Database.Path) + const tty = process.stderr.isTTY + const width = 36 + const orange = "\x1b[38;5;214m" + const muted = "\x1b[0;2m" + const reset = "\x1b[0m" + let last = -1 + if (tty) process.stderr.write("\x1b[?25l") + try { + const stats = await JsonMigration.run(sqlite, { + progress: (event) => { + const percent = Math.floor((event.current / event.total) * 100) + if (percent === last) return + last = percent + if (tty) { + const fill = Math.round((percent / 100) * width) + const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}` + process.stderr.write( + `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.current}/${event.total}${reset} `, + ) + } else { + process.stderr.write(`sqlite-migration:${percent}${EOL}`) + } + }, + }) + if (tty) process.stderr.write("\n") + if (tty) process.stderr.write("\x1b[?25h") + else process.stderr.write(`sqlite-migration:done${EOL}`) + UI.println( + `Migration complete: ${stats.projects} projects, ${stats.sessions} sessions, ${stats.messages} messages`, + ) + if (stats.errors.length > 0) { + UI.println(`${stats.errors.length} errors occurred during migration`) + } + } catch (err) { + if (tty) process.stderr.write("\x1b[?25h") + UI.error(`Migration failed: ${err instanceof Error ? err.message : String(err)}`) + process.exit(1) + } finally { + sqlite.close() + } + }, +}) + export const DbCommand = cmd({ command: "db", describe: "database tools", builder: (yargs: Argv) => { - return yargs.command(QueryCommand).command(PathCommand).demandCommand() + return yargs.command(QueryCommand).command(PathCommand).command(MigrateCommand).demandCommand() }, handler: () => {}, }) From ae6e85b2a4d9addec1913ac2f770870456aa694a Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 16 Feb 2026 13:09:39 -0600 Subject: [PATCH 027/255] ignore: rm random comment on opencode.jsonc --- .opencode/opencode.jsonc | 3 --- 1 file changed, 3 deletions(-) diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index e2350c907b52..3497847a6765 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -1,8 +1,5 @@ { "$schema": "https://opencode.ai/config.json", - // "enterprise": { - // "url": "https://enterprise.dev.opencode.ai", - // }, "provider": { "opencode": { "options": {}, From 16332a858396c23c1bf6fa673964ae306d5414ab Mon Sep 17 00:00:00 2001 From: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:14:08 +0100 Subject: [PATCH 028/255] fix(tui): make use of server dir path for file references in prompts (#13781) --- .../opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 42cf82b421be..3240afab326a 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -247,7 +247,8 @@ export function Autocomplete(props: { const width = props.anchor().width - 4 options.push( ...sortedFiles.map((item): AutocompleteOption => { - const fullPath = `${process.cwd()}/${item}` + const baseDir = (sync.data.path.directory || process.cwd()).replace(/\/+$/, "") + const fullPath = `${baseDir}/${item}` const urlObj = pathToFileURL(fullPath) let filename = item if (lineRange && !item.endsWith("/")) { From 160ba295a88462844457342ca74fa036f19ecede Mon Sep 17 00:00:00 2001 From: Robert Schadek Date: Mon, 16 Feb 2026 20:14:35 +0100 Subject: [PATCH 029/255] feat(opencode): add `dfmt` formatter support for D language files (#13867) --- packages/opencode/src/format/formatter.ts | 9 +++++++++ packages/web/src/content/docs/ar/formatters.mdx | 1 + packages/web/src/content/docs/bs/formatters.mdx | 1 + packages/web/src/content/docs/da/formatters.mdx | 1 + packages/web/src/content/docs/de/formatters.mdx | 1 + packages/web/src/content/docs/es/formatters.mdx | 1 + packages/web/src/content/docs/formatters.mdx | 1 + packages/web/src/content/docs/fr/formatters.mdx | 1 + packages/web/src/content/docs/it/formatters.mdx | 1 + packages/web/src/content/docs/ja/formatters.mdx | 1 + packages/web/src/content/docs/ko/formatters.mdx | 1 + packages/web/src/content/docs/nb/formatters.mdx | 1 + packages/web/src/content/docs/pl/formatters.mdx | 1 + packages/web/src/content/docs/pt-br/formatters.mdx | 1 + packages/web/src/content/docs/ru/formatters.mdx | 1 + packages/web/src/content/docs/th/formatters.mdx | 1 + packages/web/src/content/docs/tr/formatters.mdx | 1 + packages/web/src/content/docs/zh-cn/formatters.mdx | 1 + packages/web/src/content/docs/zh-tw/formatters.mdx | 1 + 19 files changed, 27 insertions(+) diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index 418446958029..694c23d5583b 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -373,3 +373,12 @@ export const cljfmt: Info = { return Bun.which("cljfmt") !== null }, } + +export const dfmt: Info = { + name: "dfmt", + command: ["dfmt", "-i", "$FILE"], + extensions: [".d"], + async enabled() { + return Bun.which("dfmt") !== null + }, +} diff --git a/packages/web/src/content/docs/ar/formatters.mdx b/packages/web/src/content/docs/ar/formatters.mdx index a1d5478af3a3..22ded4b9efec 100644 --- a/packages/web/src/content/docs/ar/formatters.mdx +++ b/packages/web/src/content/docs/ar/formatters.mdx @@ -29,6 +29,7 @@ description: يستخدم OpenCode مُنسِّقات خاصة بكل لغة. | htmlbeautifier | .erb, .html.erb | يتوفر أمر `htmlbeautifier` | | air | .R | يتوفر أمر `air` | | dart | .dart | يتوفر أمر `dart` | +| dfmt | .d | يتوفر أمر `dfmt` | | ocamlformat | .ml, .mli | يتوفر أمر `ocamlformat` وملف إعداد `.ocamlformat` | | terraform | .tf, .tfvars | يتوفر أمر `terraform` | | gleam | .gleam | يتوفر أمر `gleam` | diff --git a/packages/web/src/content/docs/bs/formatters.mdx b/packages/web/src/content/docs/bs/formatters.mdx index 1b70e0906aa8..af0b103ef5b7 100644 --- a/packages/web/src/content/docs/bs/formatters.mdx +++ b/packages/web/src/content/docs/bs/formatters.mdx @@ -27,6 +27,7 @@ OpenCode dolazi sa nekoliko ugrađenih formatera za popularne jezike i okvire. I | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` komanda dostupna | | air | .R | `air` komanda dostupna | | dart | .dart | `dart` komanda dostupna | +| dfmt | .d | `dfmt` komanda dostupna | | ocamlformat | .ml, .mli | `ocamlformat` komanda dostupna i `.ocamlformat` konfiguracioni fajl | | terraform | .tf, .tfvars | `terraform` komanda dostupna | | gleam | .bleam | `gleam` komanda dostupna | diff --git a/packages/web/src/content/docs/da/formatters.mdx b/packages/web/src/content/docs/da/formatters.mdx index 09ff611a66ef..33a2d6ff3add 100644 --- a/packages/web/src/content/docs/da/formatters.mdx +++ b/packages/web/src/content/docs/da/formatters.mdx @@ -29,6 +29,7 @@ OpenCode leveres med flere indbyggede formatere til populære sprog og rammer. N | htmlbeautifier | .erb,.html.erb | `htmlbeautifier` kommando tilgængelig | | luft | .R | `air` kommando tilgængelig | | dart | .dart | `dart` kommando tilgængelig | +| dfmt | .d | `dfmt` kommando tilgængelig | | ocamlformat | .ml,.mli | `ocamlformat` kommando tilgængelig og `.ocamlformat` config fil | | terraform | .tf,.tfvars | `terraform` kommando tilgængelig | | glimt | .glimt | `gleam` kommando tilgængelig | diff --git a/packages/web/src/content/docs/de/formatters.mdx b/packages/web/src/content/docs/de/formatters.mdx index f953f16cb718..b50a91f7438a 100644 --- a/packages/web/src/content/docs/de/formatters.mdx +++ b/packages/web/src/content/docs/de/formatters.mdx @@ -29,6 +29,7 @@ OpenCode verfügt über mehrere integrierte Formatierer für gängige Sprachen u | htmlbeautifier | .erb, .html.erb | `htmlbeautifier`-Befehl verfügbar | | air | .R | `air`-Befehl verfügbar | | dart | .dart | `dart`-Befehl verfügbar | +| dfmt | .d | `dfmt`-Befehl verfügbar | | ocamlformat | .ml, .mli | `ocamlformat` Befehl verfügbar und `.ocamlformat` Konfigurationsdatei | | terraform | .tf, .tfvars | `terraform`-Befehl verfügbar | | gleam | .gleam | `gleam`-Befehl verfügbar | diff --git a/packages/web/src/content/docs/es/formatters.mdx b/packages/web/src/content/docs/es/formatters.mdx index f09ef925270d..c17baf6e16c1 100644 --- a/packages/web/src/content/docs/es/formatters.mdx +++ b/packages/web/src/content/docs/es/formatters.mdx @@ -29,6 +29,7 @@ OpenCode viene con varios formateadores integrados para lenguajes y marcos popul | htmlbeautifier | .erb, .html.erb | Comando `htmlbeautifier` disponible | | air | .R | Comando `air` disponible | | dart | .dart | Comando `dart` disponible | +| dfmt | .d | Comando `dfmt` disponible | | ocamlformat | .ml, .mli | Comando `ocamlformat` disponible y archivo de configuración `.ocamlformat` | | terraform | .tf, .tfvars | Comando `terraform` disponible | | gleam | .gleam | Comando `gleam` disponible | diff --git a/packages/web/src/content/docs/formatters.mdx b/packages/web/src/content/docs/formatters.mdx index 0cb947b08f01..dbee49dca6fd 100644 --- a/packages/web/src/content/docs/formatters.mdx +++ b/packages/web/src/content/docs/formatters.mdx @@ -19,6 +19,7 @@ OpenCode comes with several built-in formatters for popular languages and framew | clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file | | cljfmt | .clj, .cljs, .cljc, .edn | `cljfmt` command available | | dart | .dart | `dart` command available | +| dfmt | .d | `dfmt` command available | | gleam | .gleam | `gleam` command available | | gofmt | .go | `gofmt` command available | | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | diff --git a/packages/web/src/content/docs/fr/formatters.mdx b/packages/web/src/content/docs/fr/formatters.mdx index ef59b810819d..96b5911efed9 100644 --- a/packages/web/src/content/docs/fr/formatters.mdx +++ b/packages/web/src/content/docs/fr/formatters.mdx @@ -29,6 +29,7 @@ OpenCode est livré avec plusieurs formateurs intégrés pour les langages et fr | htmlbeautifier | .erb, .html.erb | Commande `htmlbeautifier` disponible | | air | .R | Commande `air` disponible | | dart | .dart | Commande `dart` disponible | +| dfmt | .d | Commande `dfmt` disponible | | ocamlformat | .ml, .mli | Commande `ocamlformat` disponible et fichier de configuration `.ocamlformat` | | terraform | .tf, .tfvars | Commande `terraform` disponible | | gleam | .gleam | Commande `gleam` disponible | diff --git a/packages/web/src/content/docs/it/formatters.mdx b/packages/web/src/content/docs/it/formatters.mdx index b662db47eba5..c264da3f4b75 100644 --- a/packages/web/src/content/docs/it/formatters.mdx +++ b/packages/web/src/content/docs/it/formatters.mdx @@ -29,6 +29,7 @@ OpenCode include diversi formattatori integrati per linguaggi e framework popola | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | | air | .R | `air` command available | | dart | .dart | `dart` command available | +| dfmt | .d | `dfmt` command available | | ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | | terraform | .tf, .tfvars | `terraform` command available | | gleam | .gleam | `gleam` command available | diff --git a/packages/web/src/content/docs/ja/formatters.mdx b/packages/web/src/content/docs/ja/formatters.mdx index 04f5227e0e2e..26bcbb5de5c5 100644 --- a/packages/web/src/content/docs/ja/formatters.mdx +++ b/packages/web/src/content/docs/ja/formatters.mdx @@ -29,6 +29,7 @@ OpenCode には、一般的な言語およびフレームワーク用のいく | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | | air | .R | `air` command available | | dart | .dart | `dart` command available | +| dfmt | .d | `dfmt` command available | | ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | | terraform | .tf, .tfvars | `terraform` command available | | gleam | .gleam | `gleam` command available | diff --git a/packages/web/src/content/docs/ko/formatters.mdx b/packages/web/src/content/docs/ko/formatters.mdx index c2e539be6f47..acdde12d3b41 100644 --- a/packages/web/src/content/docs/ko/formatters.mdx +++ b/packages/web/src/content/docs/ko/formatters.mdx @@ -28,6 +28,7 @@ opencode는 인기있는 언어 및 프레임 워크에 대한 몇 가지 내장 | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` 명령 사용 가능 | | Air | .R | `air` 명령 사용 가능 | | Dart | 다트 | `dart` 명령 | +| dfmt | .d | `dfmt` 명령 사용 가능 | | ocamlformat | .ml, .mli | `ocamlformat` 명령 사용 가능·`.ocamlformat` 설정 파일 | | Terraform | .tf, .tfvars | `terraform` 명령 사용 가능 | | gleam | .gleam | `gleam` 명령 사용 가능 | diff --git a/packages/web/src/content/docs/nb/formatters.mdx b/packages/web/src/content/docs/nb/formatters.mdx index b9fe99a4486e..9619c1470aac 100644 --- a/packages/web/src/content/docs/nb/formatters.mdx +++ b/packages/web/src/content/docs/nb/formatters.mdx @@ -29,6 +29,7 @@ OpenCode kommer med flere innebygde formattere for populære språk og rammeverk | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` kommando tilgjengelig | | air | .R | `air` kommando tilgjengelig | | dart | .dart | `dart` kommando tilgjengelig | +| dfmt | .d | `dfmt` kommando tilgjengelig | | ocamlformat | .ml, .mli | `ocamlformat` kommando tilgjengelig og `.ocamlformat` konfigurasjonsfil | | terraform | .tf, .tfvars | `terraform` kommando tilgjengelig | | gleam | .gleam | `gleam` kommando tilgjengelig | diff --git a/packages/web/src/content/docs/pl/formatters.mdx b/packages/web/src/content/docs/pl/formatters.mdx index 154c55ef8e1a..593e407f4f64 100644 --- a/packages/web/src/content/docs/pl/formatters.mdx +++ b/packages/web/src/content/docs/pl/formatters.mdx @@ -29,6 +29,7 @@ OpenCode zawiera kilka wbudowanych formaterów dla popularnych języków i frame | htmlbeautifier | .erb, .html.erb | Dostępne polecenie `htmlbeautifier` | | air | .R | Dostępne polecenie `air` | | dart | .dart | Dostępne polecenie `dart` | +| dfmt | .d | Dostępne polecenie `dfmt` | | ocamlformat | .ml, .mli | Dostępne polecenie `ocamlformat` i plik konfiguracyjny `.ocamlformat` | | terraform | .tf, .tfvars | Dostępne polecenie `terraform` | | gleam | .gleam | Dostępne polecenie `gleam` | diff --git a/packages/web/src/content/docs/pt-br/formatters.mdx b/packages/web/src/content/docs/pt-br/formatters.mdx index 3037e7b65b10..65fb5d2e60a6 100644 --- a/packages/web/src/content/docs/pt-br/formatters.mdx +++ b/packages/web/src/content/docs/pt-br/formatters.mdx @@ -29,6 +29,7 @@ O opencode vem com vários formatadores integrados para linguagens e frameworks | htmlbeautifier | .erb, .html.erb | Comando `htmlbeautifier` disponível | | air | .R | Comando `air` disponível | | dart | .dart | Comando `dart` disponível | +| dfmt | .d | Comando `dfmt` disponível | | ocamlformat | .ml, .mli | Comando `ocamlformat` disponível e arquivo de configuração `.ocamlformat` | | terraform | .tf, .tfvars | Comando `terraform` disponível | | gleam | .gleam | Comando `gleam` disponível | diff --git a/packages/web/src/content/docs/ru/formatters.mdx b/packages/web/src/content/docs/ru/formatters.mdx index 8712e39c63c1..ec7730483f8c 100644 --- a/packages/web/src/content/docs/ru/formatters.mdx +++ b/packages/web/src/content/docs/ru/formatters.mdx @@ -29,6 +29,7 @@ opencode поставляется с несколькими встроенным | htmlbeautifier | .erb, .html.erb | Доступна команда `htmlbeautifier` | | air | .R | Доступна команда `air` | | dart | .dart | Доступна команда `dart` | +| dfmt | .d | Доступна команда `dfmt` | | ocamlformat | .ml, .mli | Доступна команда `ocamlformat` и файл конфигурации `.ocamlformat`. | | terraform | .tf, .tfvars | Доступна команда `terraform` | | gleam | .gleam | Доступна команда `gleam` | diff --git a/packages/web/src/content/docs/th/formatters.mdx b/packages/web/src/content/docs/th/formatters.mdx index e852cb45dc33..8b6122ca6963 100644 --- a/packages/web/src/content/docs/th/formatters.mdx +++ b/packages/web/src/content/docs/th/formatters.mdx @@ -29,6 +29,7 @@ OpenCode มาพร้อมกับฟอร์แมตเตอร์ใ | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` คำสั่งใช้ได้ | | air | .r | `air` คำสั่งใช้ได้ | | dart | .dart | `dart` คำสั่งใช้ได้ | +| dfmt | .d | `dfmt` คำสั่งใช้ได้ | | ocamlformat | .ml, .mli | มีคำสั่ง `ocamlformat` และไฟล์ปรับแต่ง `.ocamlformat` | | terraform | .tf, .tfvars | `terraform` คำสั่งใช้ได้ | | gleam | .gleam | `gleam` คำสั่งใช้ได้ | diff --git a/packages/web/src/content/docs/tr/formatters.mdx b/packages/web/src/content/docs/tr/formatters.mdx index b6db406e167b..34311baa7a9d 100644 --- a/packages/web/src/content/docs/tr/formatters.mdx +++ b/packages/web/src/content/docs/tr/formatters.mdx @@ -29,6 +29,7 @@ opencode, popüler diller ve çerçeveler için çeşitli yerleşik biçimlendir | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` komutu mevcut | | air | .R | `air` komutu mevcut | | dart | .dart | `dart` komutu mevcut | +| dfmt | .d | `dfmt` komutu mevcut | | ocamlformat | .ml, .mli | `ocamlformat` komutu mevcut ve `.ocamlformat` yapılandırma dosyası | | terraform | .tf, .tfvars | `terraform` komutu mevcut | | gleam | .gleam | `gleam` komutu mevcut | diff --git a/packages/web/src/content/docs/zh-cn/formatters.mdx b/packages/web/src/content/docs/zh-cn/formatters.mdx index 8e080d85f8fe..8c4d7ef8fadf 100644 --- a/packages/web/src/content/docs/zh-cn/formatters.mdx +++ b/packages/web/src/content/docs/zh-cn/formatters.mdx @@ -29,6 +29,7 @@ opencode 附带了多个适用于流行语言和框架的内置格式化程序 | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | | air | .R | `air` command available | | dart | .dart | `dart` command available | +| dfmt | .d | `dfmt` command available | | ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | | terraform | .tf, .tfvars | `terraform` command available | | gleam | .gleam | `gleam` command available | diff --git a/packages/web/src/content/docs/zh-tw/formatters.mdx b/packages/web/src/content/docs/zh-tw/formatters.mdx index 2ded1419de14..45a7c6cd7189 100644 --- a/packages/web/src/content/docs/zh-tw/formatters.mdx +++ b/packages/web/src/content/docs/zh-tw/formatters.mdx @@ -29,6 +29,7 @@ opencode 附帶了多個適用於流行語言和框架的內建格式化程式 | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` 指令可用 | | air | .R | `air` 指令可用 | | dart | .dart | `dart` 指令可用 | +| dfmt | .d | `dfmt` 指令可用 | | ocamlformat | .ml, .mli | `ocamlformat` 指令可用,且存在 `.ocamlformat` 設定檔 | | terraform | .tf, .tfvars | `terraform` 指令可用 | | gleam | .gleam | `gleam` 指令可用 | From d8c25bfeb44771cc3a3ba17bf8de6ad2add9de2c Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 16 Feb 2026 19:57:09 +0000 Subject: [PATCH 030/255] release: v1.2.6 --- bun.lock | 30 +++++++++++++------------- 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/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 +- 18 files changed, 37 insertions(+), 37 deletions(-) diff --git a/bun.lock b/bun.lock index c5cffffacb6f..07e239a78323 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -73,7 +73,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -107,7 +107,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -134,7 +134,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -158,7 +158,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -182,7 +182,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -215,7 +215,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -244,7 +244,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -260,7 +260,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.2.5", + "version": "1.2.6", "bin": { "opencode": "./bin/opencode", }, @@ -369,7 +369,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -389,7 +389,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.2.5", + "version": "1.2.6", "devDependencies": { "@hey-api/openapi-ts": "0.90.10", "@tsconfig/node22": "catalog:", @@ -400,7 +400,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -413,7 +413,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -455,7 +455,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "zod": "catalog:", }, @@ -466,7 +466,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.2.5", + "version": "1.2.6", "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 0272e78bbf27..b92abb413d9e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.2.5", + "version": "1.2.6", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 0075a949d29f..768c9206018f 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 34626c1e9362..8e72a74b580b 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.2.5", + "version": "1.2.6", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 5e8c5841c90a..2852976364ca 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.2.5", + "version": "1.2.6", "$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 99a5ab7d936a..5ee81030fb95 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.2.5", + "version": "1.2.6", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index e89078b8935e..4365a8bba1d4 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 874f2ed21854..d300a62e4e31 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.2.5", + "version": "1.2.6", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 00af2842cdd2..19edacd44371 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.2.5" +version = "1.2.6" 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.2.5/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.6/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.6/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.5/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.6/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.2.5/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.6/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.2.5/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.6/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index d2c4e51e90a2..580667b962c5 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.2.5", + "version": "1.2.6", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e3e4bc759a38..da5287db9355 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.2.5", + "version": "1.2.6", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 82618195c9b5..f6c78674bf58 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.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 6b3494e0c60d..6e9a05956bd9 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.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/slack/package.json b/packages/slack/package.json index 45eb1d841d65..1b5daf0b71f7 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 56343983778e..f6a53f47d550 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.2.5", + "version": "1.2.6", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index 7a76bfdec2db..53743e6762e2 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.2.5", + "version": "1.2.6", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index ff2a7d2686fe..a2687f0dacc4 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.2.5", + "version": "1.2.6", "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 bea2f5712ddd..80e3d0cbff4b 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.2.5", + "version": "1.2.6", "publisher": "sst-dev", "repository": { "type": "git", From b0afdf6ea4c016c46762b649adc30c0456814a43 Mon Sep 17 00:00:00 2001 From: Ariane Emory <97994360+ariane-emory@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:15:34 -0500 Subject: [PATCH 031/255] feat(cli): add session delete command (#13571) --- .opencode/agent/translator.md | 1 + packages/opencode/src/cli/cmd/session.ts | 26 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.opencode/agent/translator.md b/.opencode/agent/translator.md index dec6fa6c4fc3..ad209a90c2c2 100644 --- a/.opencode/agent/translator.md +++ b/.opencode/agent/translator.md @@ -359,6 +359,7 @@ opencode serve --hostname 0.0.0.0 --port 4096 opencode serve [--port ] [--hostname ] [--cors ] opencode session [command] opencode session list +opencode session delete opencode stats opencode uninstall opencode upgrade diff --git a/packages/opencode/src/cli/cmd/session.ts b/packages/opencode/src/cli/cmd/session.ts index c6a1fd4138f2..1803f849522c 100644 --- a/packages/opencode/src/cli/cmd/session.ts +++ b/packages/opencode/src/cli/cmd/session.ts @@ -38,10 +38,34 @@ function pagerCmd(): string[] { export const SessionCommand = cmd({ command: "session", describe: "manage sessions", - builder: (yargs: Argv) => yargs.command(SessionListCommand).demandCommand(), + builder: (yargs: Argv) => yargs.command(SessionListCommand).command(SessionDeleteCommand).demandCommand(), async handler() {}, }) +export const SessionDeleteCommand = cmd({ + command: "delete ", + describe: "delete a session", + builder: (yargs: Argv) => { + return yargs.positional("sessionID", { + describe: "session ID to delete", + type: "string", + demandOption: true, + }) + }, + handler: async (args) => { + await bootstrap(process.cwd(), async () => { + try { + await Session.get(args.sessionID) + } catch { + UI.error(`Session not found: ${args.sessionID}`) + process.exit(1) + } + await Session.remove(args.sessionID) + UI.println(UI.Style.TEXT_SUCCESS_BOLD + `Session ${args.sessionID} deleted` + UI.Style.TEXT_NORMAL) + }) + }, +}) + export const SessionListCommand = cmd({ command: "list", describe: "list sessions", From 86e545a23ecdb2c1840ab01e82eca292117c6bbc Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Tue, 17 Feb 2026 04:16:17 +0800 Subject: [PATCH 032/255] fix(opencode): ACP sessions never get LLM-generated titles (#13095) --- packages/opencode/src/acp/session.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts index 18aa42313017..b96ebc1c8952 100644 --- a/packages/opencode/src/acp/session.ts +++ b/packages/opencode/src/acp/session.ts @@ -21,7 +21,6 @@ export class ACPSessionManager { const session = await this.sdk.session .create( { - title: `ACP Session ${crypto.randomUUID()}`, directory: cwd, }, { throwOnError: true }, From 9d3c81a68391399e46fab5307b03984511f92b09 Mon Sep 17 00:00:00 2001 From: ImmuneFOMO <60671130+ImmuneFOMO@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:20:41 +0000 Subject: [PATCH 033/255] feat(acp): add opt-in flag for question tool (#13562) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline --- .opencode/agent/translator.md | 1 + packages/opencode/src/acp/README.md | 10 ++++++++++ packages/opencode/src/flag/flag.ts | 1 + packages/opencode/src/tool/registry.ts | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.opencode/agent/translator.md b/.opencode/agent/translator.md index ad209a90c2c2..7886cf5f395e 100644 --- a/.opencode/agent/translator.md +++ b/.opencode/agent/translator.md @@ -599,6 +599,7 @@ OPENCODE_EXPERIMENTAL_MARKDOWN OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX OPENCODE_EXPERIMENTAL_OXFMT OPENCODE_EXPERIMENTAL_PLAN_MODE +OPENCODE_ENABLE_QUESTION_TOOL OPENCODE_FAKE_VCS OPENCODE_GIT_BASH_PATH OPENCODE_MODEL diff --git a/packages/opencode/src/acp/README.md b/packages/opencode/src/acp/README.md index d998cb22da8d..aab33259bb18 100644 --- a/packages/opencode/src/acp/README.md +++ b/packages/opencode/src/acp/README.md @@ -44,6 +44,16 @@ opencode acp opencode acp --cwd /path/to/project ``` +### Question Tool Opt-In + +ACP excludes `QuestionTool` by default. + +```bash +OPENCODE_ENABLE_QUESTION_TOOL=1 opencode acp +``` + +Enable this only for ACP clients that support interactive question prompts. + ### Programmatic ```typescript diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index dfcb88bc51a5..0049d716d095 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -30,6 +30,7 @@ export namespace Flag { export declare const OPENCODE_CLIENT: string export const OPENCODE_SERVER_PASSWORD = process.env["OPENCODE_SERVER_PASSWORD"] export const OPENCODE_SERVER_USERNAME = process.env["OPENCODE_SERVER_USERNAME"] + export const OPENCODE_ENABLE_QUESTION_TOOL = truthy("OPENCODE_ENABLE_QUESTION_TOOL") // Experimental export const OPENCODE_EXPERIMENTAL = truthy("OPENCODE_EXPERIMENTAL") diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 9a06cb59937b..3ff9cce8990f 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -94,10 +94,11 @@ export namespace ToolRegistry { async function all(): Promise { const custom = await state().then((x) => x.custom) const config = await Config.get() + const question = ["app", "cli", "desktop"].includes(Flag.OPENCODE_CLIENT) || Flag.OPENCODE_ENABLE_QUESTION_TOOL return [ InvalidTool, - ...(["app", "cli", "desktop"].includes(Flag.OPENCODE_CLIENT) ? [QuestionTool] : []), + ...(question ? [QuestionTool] : []), BashTool, ReadTool, GlobTool, From a580fb47d207150b0fdfe18297afb71edbdf577c Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:59:57 -0600 Subject: [PATCH 034/255] tweak: drop ids from attachments in tools, assign them in prompt.ts instead (#13890) --- packages/opencode/src/session/prompt.ts | 30 +++++++++++++------- packages/opencode/src/tool/batch.ts | 8 +++++- packages/opencode/src/tool/read.ts | 4 --- packages/opencode/src/tool/tool.ts | 2 +- packages/opencode/src/tool/webfetch.ts | 4 --- packages/opencode/test/tool/read.test.ts | 6 ++++ packages/opencode/test/tool/webfetch.test.ts | 3 ++ 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index f705f209aa9a..5890a4a73249 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -445,6 +445,12 @@ export namespace SessionPrompt { log.error("subtask execution failed", { error, agent: task.agent, description: task.description }) return undefined }) + const attachments = result?.attachments?.map((attachment) => ({ + ...attachment, + id: Identifier.ascending("part"), + sessionID, + messageID: assistantMessage.id, + })) await Plugin.trigger( "tool.execute.after", { @@ -467,7 +473,7 @@ export namespace SessionPrompt { title: result.title, metadata: result.metadata, output: result.output, - attachments: result.attachments, + attachments, time: { ...part.state.time, end: Date.now(), @@ -797,6 +803,15 @@ export namespace SessionPrompt { }, ) const result = await item.execute(args, ctx) + const output = { + ...result, + attachments: result.attachments?.map((attachment) => ({ + ...attachment, + id: Identifier.ascending("part"), + sessionID: ctx.sessionID, + messageID: input.processor.message.id, + })), + } await Plugin.trigger( "tool.execute.after", { @@ -805,9 +820,9 @@ export namespace SessionPrompt { callID: ctx.callID, args, }, - result, + output, ) - return result + return output }, }) } @@ -855,16 +870,13 @@ export namespace SessionPrompt { ) const textParts: string[] = [] - const attachments: MessageV2.FilePart[] = [] + const attachments: Omit[] = [] for (const contentItem of result.content) { if (contentItem.type === "text") { textParts.push(contentItem.text) } else if (contentItem.type === "image") { attachments.push({ - id: Identifier.ascending("part"), - sessionID: input.session.id, - messageID: input.processor.message.id, type: "file", mime: contentItem.mimeType, url: `data:${contentItem.mimeType};base64,${contentItem.data}`, @@ -876,9 +888,6 @@ export namespace SessionPrompt { } if (resource.blob) { attachments.push({ - id: Identifier.ascending("part"), - sessionID: input.session.id, - messageID: input.processor.message.id, type: "file", mime: resource.mimeType ?? "application/octet-stream", url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`, @@ -1157,6 +1166,7 @@ export namespace SessionPrompt { pieces.push( ...result.attachments.map((attachment) => ({ ...attachment, + id: Identifier.ascending("part"), synthetic: true, filename: attachment.filename ?? part.filename, messageID: info.id, diff --git a/packages/opencode/src/tool/batch.ts b/packages/opencode/src/tool/batch.ts index ba34eb48f5cf..eecbfe299006 100644 --- a/packages/opencode/src/tool/batch.ts +++ b/packages/opencode/src/tool/batch.ts @@ -77,6 +77,12 @@ export const BatchTool = Tool.define("batch", async () => { }) const result = await tool.execute(validatedParams, { ...ctx, callID: partID }) + const attachments = result.attachments?.map((attachment) => ({ + ...attachment, + id: Identifier.ascending("part"), + sessionID: ctx.sessionID, + messageID: ctx.messageID, + })) await Session.updatePart({ id: partID, @@ -91,7 +97,7 @@ export const BatchTool = Tool.define("batch", async () => { output: result.output, title: result.title, metadata: result.metadata, - attachments: result.attachments, + attachments, time: { start: callStartTime, end: Date.now(), diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 7f5a9a9bd333..fbca89ad7dbe 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -6,7 +6,6 @@ import { LSP } from "../lsp" import { FileTime } from "../file/time" import DESCRIPTION from "./read.txt" import { Instance } from "../project/instance" -import { Identifier } from "../id/id" import { assertExternalDirectory } from "./external-directory" import { InstructionPrompt } from "../session/instruction" @@ -127,9 +126,6 @@ export const ReadTool = Tool.define("read", { }, attachments: [ { - id: Identifier.ascending("part"), - sessionID: ctx.sessionID, - messageID: ctx.messageID, type: "file", mime, url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`, diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts index 3d17ea192d32..0e78ba665cfc 100644 --- a/packages/opencode/src/tool/tool.ts +++ b/packages/opencode/src/tool/tool.ts @@ -36,7 +36,7 @@ export namespace Tool { title: string metadata: M output: string - attachments?: MessageV2.FilePart[] + attachments?: Omit[] }> formatValidationError?(error: z.ZodError): string }> diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index cd0d8dcdec16..a66e66c097b8 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -3,7 +3,6 @@ import { Tool } from "./tool" import TurndownService from "turndown" import DESCRIPTION from "./webfetch.txt" import { abortAfterAny } from "../util/abort" -import { Identifier } from "../id/id" const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds @@ -103,9 +102,6 @@ export const WebFetchTool = Tool.define("webfetch", { metadata: {}, attachments: [ { - id: Identifier.ascending("part"), - sessionID: ctx.sessionID, - messageID: ctx.messageID, type: "file", mime, url: `data:${mime};base64,${base64Content}`, diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts index 095c7bce2d25..ba3e22edc992 100644 --- a/packages/opencode/test/tool/read.test.ts +++ b/packages/opencode/test/tool/read.test.ts @@ -349,6 +349,9 @@ describe("tool.read truncation", () => { 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") }, }) }) @@ -363,6 +366,9 @@ describe("tool.read truncation", () => { 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") }, }) }) diff --git a/packages/opencode/test/tool/webfetch.test.ts b/packages/opencode/test/tool/webfetch.test.ts index 10178af8fab7..0214700fedca 100644 --- a/packages/opencode/test/tool/webfetch.test.ts +++ b/packages/opencode/test/tool/webfetch.test.ts @@ -46,6 +46,9 @@ describe("tool.webfetch", () => { expect(result.attachments?.[0].type).toBe("file") expect(result.attachments?.[0].mime).toBe("image/png") expect(result.attachments?.[0].url.startsWith("data:image/png;base64,")).toBe(true) + expect(result.attachments?.[0]).not.toHaveProperty("id") + expect(result.attachments?.[0]).not.toHaveProperty("sessionID") + expect(result.attachments?.[0]).not.toHaveProperty("messageID") }, }) }, From d93cefd47af5cb18f4c5e0a978537e1da9d58658 Mon Sep 17 00:00:00 2001 From: James Long Date: Mon, 16 Feb 2026 17:39:28 -0500 Subject: [PATCH 035/255] fix(website): fix site in safari 18 (#13894) --- packages/console/app/src/routes/index.css | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/console/app/src/routes/index.css b/packages/console/app/src/routes/index.css index 1c734c9d0610..770280e6cc32 100644 --- a/packages/console/app/src/routes/index.css +++ b/packages/console/app/src/routes/index.css @@ -174,21 +174,6 @@ body { } } - input:-webkit-autofill, - input:-webkit-autofill:hover, - input:-webkit-autofill:focus, - input:-webkit-autofill:active { - transition: background-color 5000000s ease-in-out 0s; - } - - input:-webkit-autofill { - -webkit-text-fill-color: var(--color-text-strong) !important; - } - - input:-moz-autofill { - -moz-text-fill-color: var(--color-text-strong) !important; - } - [data-component="container"] { max-width: 67.5rem; margin: 0 auto; @@ -1249,4 +1234,19 @@ body { text-decoration: underline; } } + + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + input:-webkit-autofill:active { + transition: background-color 5000000s ease-in-out 0s; + } + + input:-webkit-autofill { + -webkit-text-fill-color: var(--color-text-strong) !important; + } + + input:-moz-autofill { + -moz-text-fill-color: var(--color-text-strong) !important; + } } From 9163611989678e7d8b585003655b6c8863e81f97 Mon Sep 17 00:00:00 2001 From: Goni Zahavy Date: Tue, 17 Feb 2026 01:31:38 +0200 Subject: [PATCH 036/255] ci: fixed apt cache not working in publish.yml (#13897) --- .github/workflows/publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a1b492258b73..f63802ac5aa1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -137,7 +137,7 @@ jobs: if: contains(matrix.settings.host, 'ubuntu') uses: actions/cache@v4 with: - path: /var/cache/apt/archives + path: ~/apt-cache key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }} restore-keys: | ${{ runner.os }}-${{ matrix.settings.target }}-apt- @@ -145,8 +145,10 @@ jobs: - name: install dependencies (ubuntu only) if: contains(matrix.settings.host, 'ubuntu') run: | + mkdir -p ~/apt-cache && chmod -R a+rw ~/apt-cache sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y --no-install-recommends -o dir::cache::archives="$HOME/apt-cache" libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + sudo chmod -R a+rw ~/apt-cache - name: install Rust stable uses: dtolnay/rust-toolchain@stable From 0e669b6016526d8966aae6ef548140765c93be9d Mon Sep 17 00:00:00 2001 From: Goni Zahavy Date: Tue, 17 Feb 2026 02:27:04 +0200 Subject: [PATCH 037/255] ci: use `useblacksmith/stickydisk` on linux runners only (#13909) --- .github/actions/setup-bun/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml index 65fbf0f3d601..8cf87c5d8e85 100644 --- a/.github/actions/setup-bun/action.yml +++ b/.github/actions/setup-bun/action.yml @@ -4,6 +4,7 @@ runs: using: "composite" steps: - name: Mount Bun Cache + if: ${{ runner.os == 'Linux' }} uses: useblacksmith/stickydisk@v1 with: key: ${{ github.repository }}-bun-cache-${{ runner.os }} From e35a4131d00729b9ef75ca86b03e70b656f00e2f Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:45:11 -0600 Subject: [PATCH 038/255] core: keep message part order stable when files resolve asynchronously (#13915) --- packages/opencode/src/session/prompt.ts | 36 ++++--------- .../test/session/prompt-missing-file.test.ts | 51 +++++++++++++++++++ 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 5890a4a73249..43ad9a09d399 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -974,17 +974,22 @@ export namespace SessionPrompt { } using _ = defer(() => InstructionPrompt.clear(info.id)) + type Draft = T extends MessageV2.Part ? Omit & { id?: string } : never + const assign = (part: Draft): MessageV2.Part => ({ + ...part, + id: part.id ?? Identifier.ascending("part"), + }) + const parts = await Promise.all( - input.parts.map(async (part): Promise => { + input.parts.map(async (part): Promise[]> => { if (part.type === "file") { // before checking the protocol we check if this is an mcp resource because it needs special handling if (part.source?.type === "resource") { const { clientName, uri } = part.source log.info("mcp resource", { clientName, uri, mime: part.mime }) - const pieces: MessageV2.Part[] = [ + const pieces: Draft[] = [ { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1007,7 +1012,6 @@ export namespace SessionPrompt { for (const content of contents) { if ("text" in content && content.text) { pieces.push({ - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1018,7 +1022,6 @@ export namespace SessionPrompt { // Handle binary content if needed const mimeType = "mimeType" in content ? content.mimeType : part.mime pieces.push({ - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1030,7 +1033,6 @@ export namespace SessionPrompt { pieces.push({ ...part, - id: part.id ?? Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, }) @@ -1038,7 +1040,6 @@ export namespace SessionPrompt { log.error("failed to read MCP resource", { error, clientName, uri }) const message = error instanceof Error ? error.message : String(error) pieces.push({ - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1055,7 +1056,6 @@ export namespace SessionPrompt { if (part.mime === "text/plain") { return [ { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1063,7 +1063,6 @@ export namespace SessionPrompt { text: `Called the Read tool with the following input: ${JSON.stringify({ filePath: part.filename })}`, }, { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1072,7 +1071,6 @@ export namespace SessionPrompt { }, { ...part, - id: part.id ?? Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, }, @@ -1129,9 +1127,8 @@ export namespace SessionPrompt { } const args = { filePath: filepath, offset, limit } - const pieces: MessageV2.Part[] = [ + const pieces: Draft[] = [ { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1155,7 +1152,6 @@ export namespace SessionPrompt { } const result = await t.execute(args, readCtx) pieces.push({ - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1166,7 +1162,6 @@ export namespace SessionPrompt { pieces.push( ...result.attachments.map((attachment) => ({ ...attachment, - id: Identifier.ascending("part"), synthetic: true, filename: attachment.filename ?? part.filename, messageID: info.id, @@ -1176,7 +1171,6 @@ export namespace SessionPrompt { } else { pieces.push({ ...part, - id: part.id ?? Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, }) @@ -1192,7 +1186,6 @@ export namespace SessionPrompt { }).toObject(), }) pieces.push({ - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1219,7 +1212,6 @@ export namespace SessionPrompt { const result = await ReadTool.init().then((t) => t.execute(args, listCtx)) return [ { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1227,7 +1219,6 @@ export namespace SessionPrompt { text: `Called the Read tool with the following input: ${JSON.stringify(args)}`, }, { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1236,7 +1227,6 @@ export namespace SessionPrompt { }, { ...part, - id: part.id ?? Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, }, @@ -1247,7 +1237,6 @@ export namespace SessionPrompt { FileTime.read(input.sessionID, filepath) return [ { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1255,7 +1244,7 @@ export namespace SessionPrompt { synthetic: true, }, { - id: part.id ?? Identifier.ascending("part"), + id: part.id, messageID: info.id, sessionID: input.sessionID, type: "file", @@ -1274,13 +1263,11 @@ export namespace SessionPrompt { const hint = perm.action === "deny" ? " . Invoked by user; guaranteed to exist." : "" return [ { - id: Identifier.ascending("part"), ...part, messageID: info.id, sessionID: input.sessionID, }, { - id: Identifier.ascending("part"), messageID: info.id, sessionID: input.sessionID, type: "text", @@ -1297,14 +1284,13 @@ export namespace SessionPrompt { return [ { - id: Identifier.ascending("part"), ...part, messageID: info.id, sessionID: input.sessionID, }, ] }), - ).then((x) => x.flat()) + ).then((x) => x.flat().map(assign)) await Plugin.trigger( "chat.message", diff --git a/packages/opencode/test/session/prompt-missing-file.test.ts b/packages/opencode/test/session/prompt-missing-file.test.ts index 081847c67920..c3f52f56cc74 100644 --- a/packages/opencode/test/session/prompt-missing-file.test.ts +++ b/packages/opencode/test/session/prompt-missing-file.test.ts @@ -2,6 +2,7 @@ import path from "path" import { describe, expect, test } from "bun:test" import { Instance } from "../../src/project/instance" import { Session } from "../../src/session" +import { MessageV2 } from "../../src/session/message-v2" import { SessionPrompt } from "../../src/session/prompt" import { tmpdir } from "../fixture/fixture" @@ -50,4 +51,54 @@ describe("session.prompt missing file", () => { }, }) }) + + test("keeps stored part order stable when file resolution is async", async () => { + await using tmp = await tmpdir({ + git: true, + config: { + agent: { + build: { + model: "openai/gpt-5.2", + }, + }, + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const session = await Session.create({}) + + const missing = path.join(tmp.path, "still-missing.ts") + const msg = await SessionPrompt.prompt({ + sessionID: session.id, + agent: "build", + noReply: true, + parts: [ + { + type: "file", + mime: "text/plain", + url: `file://${missing}`, + filename: "still-missing.ts", + }, + { type: "text", text: "after-file" }, + ], + }) + + if (msg.info.role !== "user") throw new Error("expected user message") + + const stored = await MessageV2.get({ + sessionID: session.id, + messageID: msg.info.id, + }) + const text = stored.parts.filter((part) => part.type === "text").map((part) => part.text) + + expect(text[0]?.startsWith("Called the Read tool with the following input:")).toBe(true) + expect(text[1]?.includes("Read tool failed to read")).toBe(true) + expect(text[2]).toBe("after-file") + + await Session.remove(session.id) + }, + }) + }) }) From 422609722803c9babf5c9d28527725f488e5dda4 Mon Sep 17 00:00:00 2001 From: Goni Zahavy Date: Tue, 17 Feb 2026 06:13:33 +0200 Subject: [PATCH 039/255] ci: fixed Rust cache for 'cargo install' in publish.yml (#13907) --- .github/workflows/publish.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f63802ac5aa1..0fd95b9cfcac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -171,13 +171,22 @@ jobs: GH_TOKEN: ${{ github.token }} GITHUB_RUN_ID: ${{ github.run_id }} + - name: Resolve tauri portable SHA + if: contains(matrix.settings.host, 'ubuntu') + run: echo "TAURI_PORTABLE_SHA=$(git ls-remote https://github.com/tauri-apps/tauri.git refs/heads/feat/truly-portable-appimage | cut -f1)" >> "$GITHUB_ENV" + # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released - name: Install tauri-cli from portable appimage branch + uses: taiki-e/cache-cargo-install-action@v3 if: contains(matrix.settings.host, 'ubuntu') - run: | - cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force - echo "Installed tauri-cli version:" - cargo tauri --version + with: + tool: tauri-cli + git: https://github.com/tauri-apps/tauri + # branch: feat/truly-portable-appimage + rev: ${{ env.TAURI_PORTABLE_SHA }} + + - name: Show tauri-cli version + run: cargo tauri --version - name: Build and upload artifacts uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a From ea2d089db0f4cc135234abcf8a231a49d23d53c5 Mon Sep 17 00:00:00 2001 From: Goni Zahavy Date: Tue, 17 Feb 2026 06:42:55 +0200 Subject: [PATCH 040/255] ci: fixed missing if condition (#13934) --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0fd95b9cfcac..431581f5966e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -186,6 +186,7 @@ jobs: rev: ${{ env.TAURI_PORTABLE_SHA }} - name: Show tauri-cli version + if: contains(matrix.settings.host, 'ubuntu') run: cargo tauri --version - name: Build and upload artifacts From d338bd528c010bdab481e0e9ecc637674a2d5246 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 17 Feb 2026 12:43:25 +0800 Subject: [PATCH 041/255] Hide server CLI on windows (#13936) --- packages/desktop/src-tauri/src/cli.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/desktop/src-tauri/src/cli.rs b/packages/desktop/src-tauri/src/cli.rs index 0f5cd2ff1cfa..2b60120bb3e0 100644 --- a/packages/desktop/src-tauri/src/cli.rs +++ b/packages/desktop/src-tauri/src/cli.rs @@ -317,9 +317,12 @@ pub fn spawn_command( cmd }; - cmd.stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + cmd.stdin(Stdio::null()); + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); + + #[cfg(windows)] + cmd.creation_flags(0x0800_0000); let mut wrap = CommandWrap::from(cmd); From ace63b3ddb99335b9ff71121336f70407c4b3ea5 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 17 Feb 2026 01:12:09 -0500 Subject: [PATCH 042/255] zen: glm 5 free --- packages/web/src/content/docs/zen.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/web/src/content/docs/zen.mdx b/packages/web/src/content/docs/zen.mdx index ae364fc9e1c0..482a4454f561 100644 --- a/packages/web/src/content/docs/zen.mdx +++ b/packages/web/src/content/docs/zen.mdx @@ -86,6 +86,7 @@ You can also access our models through the following API endpoints. | MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| GLM 5 Free | glm-5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.6 | glm-4.6 | `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` | @@ -121,6 +122,7 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**. | MiniMax M2.5 Free | Free | Free | Free | - | | MiniMax M2.5 | $0.30 | $1.20 | $0.06 | - | | MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | +| GLM 5 Free | Free | Free | Free | - | | GLM 5 | $1.00 | $3.20 | $0.20 | - | | GLM 4.7 | $0.60 | $2.20 | $0.10 | - | | GLM 4.6 | $0.60 | $2.20 | $0.10 | - | @@ -160,6 +162,7 @@ Credit card fees are passed along at cost (4.4% + $0.30 per transaction); we don The free models: +- GLM 5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - Kimi K2.5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - 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. - 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. @@ -192,6 +195,7 @@ charging you more than $20 if your balance goes below $5. All our models are hosted in the US. Our providers follow a zero-retention policy and do not use your data for model training, with the following exceptions: - Big Pickle: During its free period, collected data may be used to improve the model. +- GLM 5 Free: During its free period, collected data may be used to improve the model. - Kimi K2.5 Free: 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. - OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data). From a93a1b93e119a976935e5ab6f214ef7c33d60d45 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 17 Feb 2026 01:32:57 -0500 Subject: [PATCH 043/255] wip: zen --- infra/console.ts | 10 +++++ .../console/core/script/promote-models.ts | 2 +- packages/console/core/script/update-models.ts | 2 +- packages/console/core/src/model.ts | 12 +++++- packages/console/core/sst-env.d.ts | 40 +++++++++++++++++++ packages/console/function/sst-env.d.ts | 40 +++++++++++++++++++ packages/console/resource/sst-env.d.ts | 40 +++++++++++++++++++ packages/enterprise/sst-env.d.ts | 40 +++++++++++++++++++ packages/function/sst-env.d.ts | 40 +++++++++++++++++++ sst-env.d.ts | 40 +++++++++++++++++++ 10 files changed, 263 insertions(+), 3 deletions(-) diff --git a/infra/console.ts b/infra/console.ts index 4e5a14b04573..9089055821c4 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -145,6 +145,16 @@ const ZEN_MODELS = [ new sst.Secret("ZEN_MODELS18"), new sst.Secret("ZEN_MODELS19"), new sst.Secret("ZEN_MODELS20"), + new sst.Secret("ZEN_MODELS21"), + new sst.Secret("ZEN_MODELS22"), + new sst.Secret("ZEN_MODELS23"), + new sst.Secret("ZEN_MODELS24"), + new sst.Secret("ZEN_MODELS25"), + new sst.Secret("ZEN_MODELS26"), + new sst.Secret("ZEN_MODELS27"), + new sst.Secret("ZEN_MODELS28"), + new sst.Secret("ZEN_MODELS29"), + new sst.Secret("ZEN_MODELS30"), ] const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY") diff --git a/packages/console/core/script/promote-models.ts b/packages/console/core/script/promote-models.ts index 17715bedfb8a..aa9f20abc3ea 100755 --- a/packages/console/core/script/promote-models.ts +++ b/packages/console/core/script/promote-models.ts @@ -9,7 +9,7 @@ const stage = process.argv[2] if (!stage) throw new Error("Stage is required") const root = path.resolve(process.cwd(), "..", "..", "..") -const PARTS = 20 +const PARTS = 30 // read the secret const ret = await $`bun sst secret list`.cwd(root).text() diff --git a/packages/console/core/script/update-models.ts b/packages/console/core/script/update-models.ts index 9025a6526e5e..6d7f7662a4cd 100755 --- a/packages/console/core/script/update-models.ts +++ b/packages/console/core/script/update-models.ts @@ -7,7 +7,7 @@ import { ZenData } from "../src/model" const root = path.resolve(process.cwd(), "..", "..", "..") const models = await $`bun sst secret list`.cwd(root).text() -const PARTS = 20 +const PARTS = 30 // read the line starting with "ZEN_MODELS" const lines = models.split("\n") diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts index da2677844935..6011cac37683 100644 --- a/packages/console/core/src/model.ts +++ b/packages/console/core/src/model.ts @@ -102,7 +102,17 @@ export namespace ZenData { Resource.ZEN_MODELS17.value + Resource.ZEN_MODELS18.value + Resource.ZEN_MODELS19.value + - Resource.ZEN_MODELS20.value, + Resource.ZEN_MODELS20.value + + Resource.ZEN_MODELS21.value + + Resource.ZEN_MODELS22.value + + Resource.ZEN_MODELS23.value + + Resource.ZEN_MODELS24.value + + Resource.ZEN_MODELS25.value + + Resource.ZEN_MODELS26.value + + Resource.ZEN_MODELS27.value + + Resource.ZEN_MODELS28.value + + Resource.ZEN_MODELS29.value + + Resource.ZEN_MODELS30.value, ) const { models, providers, providerFamilies } = ModelsSchema.parse(json) return { diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index fea908213dfc..737af71d414a 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -181,10 +181,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index fea908213dfc..737af71d414a 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -181,10 +181,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index fea908213dfc..737af71d414a 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -181,10 +181,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index fea908213dfc..737af71d414a 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -181,10 +181,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index fea908213dfc..737af71d414a 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -181,10 +181,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string diff --git a/sst-env.d.ts b/sst-env.d.ts index f87d4d603f6a..b2ae763562c2 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -207,10 +207,50 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS21": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS22": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS23": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS24": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS25": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS26": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS27": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS28": { + "type": "sst.sst.Secret" + "value": string + } + "ZEN_MODELS29": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS3": { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS30": { + "type": "sst.sst.Secret" + "value": string + } "ZEN_MODELS4": { "type": "sst.sst.Secret" "value": string From ed4e4843c2a65018d6f23f24f86c6a471e391053 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Tue, 17 Feb 2026 01:05:56 -0600 Subject: [PATCH 044/255] ci: update triage workflow (#13944) --- .opencode/agent/triage.md | 70 ++++++++++++++++++++++--- .opencode/tool/github-triage.ts | 85 ++++++++++++++++++++---------- .opencode/tool/github-triage.txt | 88 ++------------------------------ packages/script/src/index.ts | 5 ++ 4 files changed, 129 insertions(+), 119 deletions(-) diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md index 5d1147a88594..ccf3f0c33e27 100644 --- a/.opencode/agent/triage.md +++ b/.opencode/agent/triage.md @@ -1,7 +1,7 @@ --- mode: primary hidden: true -model: opencode/claude-haiku-4-5 +model: opencode/minimax-m2.5 color: "#44BA81" tools: "*": false @@ -12,6 +12,8 @@ You are a triage agent responsible for triaging github issues. Use your github-triage tool to triage issues. +This file is the source of truth for ownership/routing rules. + ## Labels ### windows @@ -43,12 +45,30 @@ Desktop app issues: **Only** add if the issue explicitly mentions nix. +If the issue does not mention nix, do not add nix. + +If the issue mentions nix, assign to `rekram1-node`. + #### zen **Only** add if the issue mentions "zen" or "opencode zen" or "opencode black". If the issue doesn't have "zen" or "opencode black" in it then don't add zen label +#### core + +Use for core server issues in `packages/opencode/`, excluding `packages/opencode/src/cli/cmd/tui/`. + +Examples: + +- LSP server behavior +- Harness behavior (agent + tools) +- Feature requests for server behavior +- Agent context construction +- API endpoints +- Provider integration issues +- New, broken, or poor-quality models + #### docs Add if the issue requests better documentation or docs updates. @@ -66,13 +86,47 @@ TUI issues potentially caused by our underlying TUI library: When assigning to people here are the following rules: -adamdotdev: -ONLY assign adam if the issue will have the "desktop" label. +Desktop / Web: +Use for desktop-labeled issues only. + +- adamdotdevin +- iamdavidhill +- Brendonovich +- nexxeln + +Zen: +ONLY assign if the issue will have the "zen" label. + +- fwang +- MrMushrooooom + +TUI (`packages/opencode/src/cli/cmd/tui/...`): + +- thdxr for TUI UX/UI product decisions and interaction flow +- kommander for OpenTUI engine issues: rendering artifacts, keybind handling, terminal compatibility, SSH behavior, and low-level perf bottlenecks +- rekram1-node for TUI bugs that are not clearly OpenTUI engine issues + +Core (`packages/opencode/...`, excluding TUI subtree): + +- thdxr for sqlite/snapshot/memory bugs and larger architectural core features +- jlongster for opencode server + API feature work (tool currently remaps jlongster -> thdxr until assignable) +- rekram1-node for harness issues, provider issues, and other bug-squashing + +For core bugs that do not clearly map, either thdxr or rekram1-node is acceptable. + +Docs: + +- R44VC0RP + +Windows: + +- Hona (assign any issue that mentions Windows or is likely Windows-specific) -fwang: -ONLY assign fwang if the issue will have the "zen" label. +Determinism rules: -jayair: -ONLY assign jayair if the issue will have the "docs" label. +- If title + body does not contain "zen", do not add the "zen" label +- If "nix" label is added but title + body does not mention nix/nixos, the tool will drop "nix" +- If title + body mentions nix/nixos, assign to `rekram1-node` +- If "desktop" label is added, the tool will override assignee and randomly pick one Desktop / Web owner -In all other cases use best judgment. Avoid assigning to kommander needlessly, when in doubt assign to rekram1-node. +In all other cases, choose the team/section with the most overlap with the issue and assign a member from that team at random. diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts index 1e216f1c8daa..3a70c4e002bc 100644 --- a/.opencode/tool/github-triage.ts +++ b/.opencode/tool/github-triage.ts @@ -1,8 +1,22 @@ /// -// import { Octokit } from "@octokit/rest" import { tool } from "@opencode-ai/plugin" import DESCRIPTION from "./github-triage.txt" +const TEAM = { + desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"], + zen: ["fwang", "MrMushrooooom"], + tui: ["thdxr", "kommander", "rekram1-node"], + core: ["thdxr", "rekram1-node", "jlongster"], + docs: ["R44VC0RP"], + windows: ["Hona"], +} as const + +const ASSIGNEES = [...new Set(Object.values(TEAM).flat())] + +function pick(items: readonly T[]) { + return items[Math.floor(Math.random() * items.length)]! +} + function getIssueNumber(): number { const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10) if (!issue) throw new Error("ISSUE_NUMBER env var not set") @@ -29,60 +43,79 @@ export default tool({ description: DESCRIPTION, args: { assignee: tool.schema - .enum(["thdxr", "adamdotdevin", "rekram1-node", "fwang", "jayair", "kommander"]) + .enum(ASSIGNEES as [string, ...string[]]) .describe("The username of the assignee") .default("rekram1-node"), labels: tool.schema - .array(tool.schema.enum(["nix", "opentui", "perf", "desktop", "zen", "docs", "windows"])) + .array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"])) .describe("The labels(s) to add to the issue") .default([]), }, async execute(args) { const issue = getIssueNumber() - // const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }) const owner = "anomalyco" const repo = "opencode" const results: string[] = [] + let labels = [...new Set(args.labels.map((x) => (x === "desktop" ? "web" : x)))] + const web = labels.includes("web") + const text = `${process.env.ISSUE_TITLE ?? ""}\n${process.env.ISSUE_BODY ?? ""}`.toLowerCase() + const zen = /\bzen\b/.test(text) || text.includes("opencode black") + const nix = /\bnix(os)?\b/.test(text) + + if (labels.includes("nix") && !nix) { + labels = labels.filter((x) => x !== "nix") + results.push("Dropped label: nix (issue does not mention nix)") + } + + const assignee = nix + ? "rekram1-node" + : web + ? pick(TEAM.desktop) + : args.assignee === "jlongster" + ? "thdxr" + : args.assignee + + if (args.assignee === "jlongster" && assignee === "thdxr") { + results.push("Remapped assignee: jlongster -> thdxr (jlongster not assignable yet)") + } + + if (labels.includes("zen") && !zen) { + throw new Error("Only add the zen label when issue title/body contains 'zen'") + } + + if (web && !nix && !(TEAM.desktop as readonly string[]).includes(assignee)) { + throw new Error("Web issues must be assigned to adamdotdevin, iamdavidhill, Brendonovich, or nexxeln") + } + + if ((TEAM.zen as readonly string[]).includes(assignee) && !labels.includes("zen")) { + throw new Error("Only zen issues should be assigned to fwang or MrMushrooooom") + } - if (args.assignee === "adamdotdevin" && !args.labels.includes("desktop")) { - throw new Error("Only desktop issues should be assigned to adamdotdevin") + if (assignee === "Hona" && !labels.includes("windows")) { + throw new Error("Only windows issues should be assigned to Hona") } - if (args.assignee === "fwang" && !args.labels.includes("zen")) { - throw new Error("Only zen issues should be assigned to fwang") + if (assignee === "R44VC0RP" && !labels.includes("docs")) { + throw new Error("Only docs issues should be assigned to R44VC0RP") } - if (args.assignee === "kommander" && !args.labels.includes("opentui")) { + if (assignee === "kommander" && !labels.includes("opentui")) { throw new Error("Only opentui issues should be assigned to kommander") } - // await octokit.rest.issues.addAssignees({ - // owner, - // repo, - // issue_number: issue, - // assignees: [args.assignee], - // }) await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { method: "POST", - body: JSON.stringify({ assignees: [args.assignee] }), + body: JSON.stringify({ assignees: [assignee] }), }) - results.push(`Assigned @${args.assignee} to issue #${issue}`) - - const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label)) + results.push(`Assigned @${assignee} to issue #${issue}`) if (labels.length > 0) { - // await octokit.rest.issues.addLabels({ - // owner, - // repo, - // issue_number: issue, - // labels, - // }) await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, { method: "POST", body: JSON.stringify({ labels }), }) - results.push(`Added labels: ${args.labels.join(", ")}`) + results.push(`Added labels: ${labels.join(", ")}`) } return results.join("\n") diff --git a/.opencode/tool/github-triage.txt b/.opencode/tool/github-triage.txt index ae47cf4cb0a8..4369ed23512f 100644 --- a/.opencode/tool/github-triage.txt +++ b/.opencode/tool/github-triage.txt @@ -1,88 +1,6 @@ Use this tool to assign and/or label a GitHub issue. -You can assign the following users: -- thdxr -- adamdotdevin -- fwang -- jayair -- kommander -- rekram1-node +Choose labels and assignee using the current triage policy and ownership rules. +Pick the most fitting labels for the issue and assign one owner. - -You can use the following labels: -- nix -- opentui -- perf -- web -- zen -- docs - -Always try to assign an issue, if in doubt, assign rekram1-node to it. - -## Breakdown of responsibilities: - -### thdxr - -Dax is responsible for managing core parts of the application, for large feature requests, api changes, or things that require significant changes to the codebase assign him. - -This relates to OpenCode server primarily but has overlap with just about anything - -### adamdotdevin - -Adam is responsible for managing the Desktop/Web app. If there is an issue relating to the desktop app or `opencode web` command. Assign him. - - -### fwang - -Frank is responsible for managing Zen, if you see complaints about OpenCode Zen, maybe it's the dashboard, the model quality, billing issues, etc. Assign him to the issue. - -### jayair - -Jay is responsible for documentation. If there is an issue relating to documentation assign him. - -### kommander - -Sebastian is responsible for managing an OpenTUI (a library for building terminal user interfaces). OpenCode's TUI is built with OpenTUI. If there are issues about: -- random characters on screen -- keybinds not working on different terminals -- general terminal stuff -Then assign the issue to Him. - -### rekram1-node - -ALL BUGS SHOULD BE assigned to rekram1-node unless they have the `opentui` label. - -Assign Aiden to an issue as a catch all, if you can't assign anyone else. Most of the time this will be bugs/polish things. -If no one else makes sense to assign, assign rekram1-node to it. - -Always assign to aiden if the issue mentions "acp", "zed", or model performance issues - -## Breakdown of Labels: - -### nix - -Any issue that mentions nix, or nixos should have a nix label - -### opentui - -Anything relating to the TUI itself should have an opentui label - -### perf - -Anything related to slow performance, high ram, high cpu usage, or any other performance related issue should have a perf label - -### desktop - -Anything related to `opencode web` command or the desktop app should have a desktop label. Never add this label for anything terminal/tui related - -### zen - -Anything related to OpenCode Zen, billing, or model quality from Zen should have a zen label - -### docs - -Anything related to the documentation should have a docs label - -### windows - -Use for any issue that involves the windows OS +If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random. diff --git a/packages/script/src/index.ts b/packages/script/src/index.ts index a3f5e7a8e2a4..1f39d313888a 100644 --- a/packages/script/src/index.ts +++ b/packages/script/src/index.ts @@ -54,8 +54,13 @@ const team = [ "kommander", "jayair", "fwang", + "MrMushrooooom", "adamdotdevin", "iamdavidhill", + "Brendonovich", + "nexxeln", + "Hona", + "jlongster", "opencode-agent[bot]", "R44VC0RP", ] From 0186a8506340f6f6715262d4986c45740fb488d5 Mon Sep 17 00:00:00 2001 From: Ganesh <179367536+itskritix@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:34:11 +0530 Subject: [PATCH 045/255] fix(app): keep Escape handling local to prompt input on macOS desktop (#13963) --- packages/app/src/components/prompt-input.tsx | 40 +++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 984888c35d87..e21798738175 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -280,6 +280,7 @@ export const PromptInput: Component = (props) => { } const isFocused = createFocusSignal(() => editorRef) + const escBlur = () => platform.platform === "desktop" && platform.os === "macos" const closePopover = () => setStore("popover", null) @@ -842,13 +843,39 @@ export const PromptInput: Component = (props) => { return } } - if (store.mode === "shell") { - const { collapsed, cursorPosition, textLength } = getCaretState() - if (event.key === "Escape") { + + if (event.key === "Escape") { + if (store.popover) { + closePopover() + event.preventDefault() + event.stopPropagation() + return + } + + if (store.mode === "shell") { setStore("mode", "normal") event.preventDefault() + event.stopPropagation() + return + } + + if (working()) { + abort() + event.preventDefault() + event.stopPropagation() + return + } + + if (escBlur()) { + editorRef.blur() + event.preventDefault() + event.stopPropagation() return } + } + + if (store.mode === "shell") { + const { collapsed, cursorPosition, textLength } = getCaretState() if (event.key === "Backspace" && collapsed && cursorPosition === 0 && textLength === 0) { setStore("mode", "normal") event.preventDefault() @@ -927,13 +954,6 @@ export const PromptInput: Component = (props) => { if (event.key === "Enter" && !event.shiftKey) { handleSubmit(event) } - if (event.key === "Escape") { - if (store.popover) { - closePopover() - } else if (working()) { - abort() - } - } } return ( From 8d0a303af48da5e6c6d5287ef2144bfb49ca13d0 Mon Sep 17 00:00:00 2001 From: vynn <163614291+vynnlee@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:05:37 +0900 Subject: [PATCH 046/255] docs(ko): improve Korean translation accuracy and clarity in Zen docs (#13951) --- packages/web/src/content/docs/ko/zen.mdx | 148 ++++++++++------------- 1 file changed, 66 insertions(+), 82 deletions(-) diff --git a/packages/web/src/content/docs/ko/zen.mdx b/packages/web/src/content/docs/ko/zen.mdx index bf30ccc655f9..254341b98277 100644 --- a/packages/web/src/content/docs/ko/zen.mdx +++ b/packages/web/src/content/docs/ko/zen.mdx @@ -7,60 +7,51 @@ import config from "../../../../config.mjs" export const console = config.console export const email = `mailto:${config.email}` -OpenCode Zen은 opencode 팀에서 제공하는 테스트 및 검증된 모델 목록입니다. +OpenCode Zen은 OpenCode 팀이 제공하는, 테스트 및 검증을 완료한 모델 목록입니다. :::note -OpenCode Zen은 현재 베타입니다. +OpenCode Zen은 현재 베타(Beta) 단계에 있습니다. ::: -Zen은 opencode의 다른 공급자와 같이 작동합니다. OpenCode Zen에 로그인하고 -API 키. 그것은 ** 완전 옵션 ** 당신은 사용할 필요가 없습니다 -opencode입니다. +Zen은 OpenCode 내의 다른 공급자와 동일한 방식으로 작동합니다. 사용자는 OpenCode Zen에 로그인하여 API 키를 발급받을 수 있습니다. 본 서비스는 **전적으로 선택 사항**이며, OpenCode를 사용하기 위해 반드시 OpenCode Zen을 이용할 필요는 없습니다. --- ## 배경 -거기에 모델의 큰 수는 있지만 몇 가지의 -이 모형은 뿐 아니라 기호화 에이전트 작동합니다. 또한, 대부분의 공급자는 -아주 다르게 형성하십시오; 그래서 당신은 아주 다른 성과 및 질을 얻습니다. +현재 다양한 모델이 존재하지만, 이 중 코딩 에이전트로서 우수한 성능을 발휘하는 모델은 일부에 불과합니다. 또한 대부분의 공급자는 각기 다른 방식으로 구성되어 있어, 그에 따라 성능과 품질 또한 크게 달라질 수 있습니다. :::tip -opencode와 잘 작동하는 모델 및 공급자의 선택 그룹을 테스트했습니다. +OpenCode와 함께 원활하게 작동하는 일부 모델 및 제공자를 선별하여 테스트를 진행하였습니다. ::: -OpenRouter와 같은 무언가를 통해 모델을 사용하고 있다면 결코 할 수 없습니다. -당신이 원하는 모델의 최고의 버전을 얻는 경우. +따라서 OpenRouter와 같은 서비스를 통해 모델을 사용하는 경우, 사용자가 원하는 모델의 최적 버전을 실제로 사용하고 있는지 확신하기 어렵습니다. -이것을 해결하기 위해, 우리는 몇 가지 일을했다 : +이 문제를 해결하기 위해 다음과 같은 조치를 수행하였습니다. -1. 우리는 모형의 선택 그룹을 시험하고 그들의 팀에 대략 이야기했습니다 - 가장 좋은 것. -2. 우리는 그(것)들이 봉사한 것을 확인하기 위하여 몇몇 공급자와 일했습니다 - 견적 요청 -3. 마지막으로, 우리는 model/provider의 조합을 벤치마크하고 올랐습니다 - 우리가 좋은 권고를 느끼는 목록으로. +1. 일부 모델을 선별하여 테스트를 진행하고, 각 모델 팀과 협력하여 최적의 운영 방안을 논의하였습니다. +2. 일부 제공자와 협력하여 해당 모델이 올바르게 제공되도록 구성하였습니다. +3. 마지막으로 모델과 제공자 조합에 대한 벤치마크를 수행하여, 신뢰를 바탕으로 권장할 수 있는 목록을 도출하였습니다. -OpenCode Zen은 이 모델에 액세스 할 수있는 AI 게이트웨이입니다. +OpenCode Zen은 이러한 모델에 대한 접근을 제공하는 AI 게이트웨이입니다. --- -## 어떻게 작동합니까? +## 어떻게 작동하나요? -OpenCode Zen은 opencode의 다른 공급자와 같습니다. +OpenCode Zen은 OpenCode의 다른 제공자와 동일한 방식으로 작동합니다. -1. 로그인 **OpenCode Zen**, 청구 추가 - 세부 사항 및 API 키 복사. -2. TUI에서 `/connect` 명령을 실행하고 OpenCode Zen을 선택하고 API 키를 붙여 넣으십시오. -3. TUI의 `/models`를 실행하여 우리가 추천하는 모델 목록을 볼 수 있습니다. +1. **OpenCode Zen**에 로그인한 후, 결제 정보를 추가하고 API 키를 복사합니다. +2. TUI에서 `/connect` 명령어를 실행한 뒤, OpenCode Zen을 선택하고 API 키를 붙여넣습니다. +3. TUI에서 `/models` 명령어를 실행하여, 당사가 권장하는 모델 목록을 확인합니다. -요청에 따라 청구되며 계정에 크레딧을 추가 할 수 있습니다. +요금은 요청 단위로 부과되며, 계정에 크레딧을 추가하여 사용할 수 있습니다. --- ## 엔드포인트 -다음 API 엔드포인트를 통해 모델에 액세스할 수 있습니다. +다음 API 엔드포인트를 통해서도 당사의 모델에 접근할 수 있습니다. | 모델 | 모델 ID | 엔드포인트 | AI SDK 패키지 | | ------------------ | ------------------ | -------------------------------------------------- | --------------------------- | @@ -94,15 +85,14 @@ OpenCode Zen은 opencode의 다른 공급자와 같습니다. | Qwen3 Coder 480B | qwen3-coder | `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` | -opencode config에서 [model id](/docs/config/#models) -형식 `opencode/`를 사용합니다. 예를 들어, GPT 5.2 Codex의 경우, -설정에서 `opencode/gpt-5.2-codex`를 사용합니다. +OpenCode 설정 파일에서 사용하는 [모델 ID](/docs/config/#models)는 `opencode/` 형식을 따릅니다. +예를 들어 GPT 5.2 Codex의 경우 설정에서 `opencode/gpt-5.2-codex`와 같이 사용합니다. --- -## 모델 +### 모델 -사용할 수 있는 모델과 메타데이터의 전체 목록은 다음과 같습니다: +사용 가능한 전체 모델 목록과 해당 메타데이터는 다음 경로에서 확인할 수 있습니다: ``` https://opencode.ai/zen/v1/models @@ -110,9 +100,9 @@ https://opencode.ai/zen/v1/models --- -## 가격 +## 요금제 -우리는 pay-as-you-go 모델을 지원합니다. 아래는 1M 토큰 \*\* 당 가격입니다. +당사는 종량제(pay-as-you-go) 요금 모델을 지원합니다. 아래는 **1백만 토큰(1M tokens)당** 요금입니다. | 모델 | 입력 | 출력 | 캐시 읽기 | 캐시 쓰기 | | --------------------------------- | ------ | ------ | --------- | --------- | @@ -150,105 +140,99 @@ https://opencode.ai/zen/v1/models | GPT 5 Codex | $1.07 | $8.50 | $0.107 | - | | GPT 5 Nano | Free | Free | Free | - | -You might notice Claude Haiku 3.5 사용 역사. 이것은 세션의 제목을 생성하는 데 사용되는 [low cost model](/docs/config/#models)입니다. +사용 내역에서 _Claude Haiku 3.5_를 확인하실 수 있습니다. 이는 세션 제목을 생성하는 데 사용되는 [저비용 모델](/docs/config/#models)입니다. :::note -신용 카드 수수료는 비용 (4.4% + 거래 당 $0.30)에 따라 전달됩니다. 우리는 그 이상을 청구하지 않습니다. +신용카드 수수료는 원가 기준(거래당 4.4% + $0.30)으로 그대로 반영되며, 당사는 그 외의 추가 수수료를 부과하지 않습니다. ::: 무료 모델: -- GLM의 4.7 제한된 시간에 opencode에서 무료로 사용할 수 있습니다. 팀은 피드백을 수집하고 모델을 개선하기 위해이 시간을 사용합니다. -- 김이 K2.5 제한된 시간에 opencode에서 무료로 사용할 수 있습니다. 팀은 피드백을 수집하고 모델을 개선하기 위해이 시간을 사용합니다. -- 미니 맥스 M2.1 제한된 시간에 opencode에서 무료로 사용할 수 있습니다. 팀은 피드백을 수집하고 모델을 개선하기 위해이 시간을 사용합니다. -- Big Pickle은 제한된 시간 동안 opencode에서 무료 훔친 모델입니다. 팀은 피드백을 수집하고 모델을 개선하기 위해이 시간을 사용합니다. +- GLM 5 Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 해당 기간 동안 팀은 사용자 피드백을 수집하고 모델을 개선할 예정입니다. +- Kimi K2.5 Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 해당 기간 동안 팀은 사용자 피드백을 수집하고 모델을 개선할 예정입니다. +- MiniMax M2.5 Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 해당 기간 동안 팀은 사용자 피드백을 수집하고 모델을 개선할 예정입니다. +- Big Pickle은 한정된 기간 동안 OpenCode에서 무료로 제공되는 스텔스 모델입니다. 해당 기간 동안 팀은 사용자 피드백을 수집하고 모델을 개선할 예정입니다. -Contact us if you have any questions. +문의 사항이 있으시면 Contact us를 통해 연락해 주시기 바랍니다. --- -### 자동 로드 +### 자동 충전 -잔액이 $ 5 미만인 경우 Zen은 $ 20를 자동으로 다시로드합니다. +잔액이 $5 미만으로 내려가면, Zen은 자동으로 $20을 충전합니다. -자동로드 금액을 변경할 수 있습니다. 또한 완전히 자동 부하를 비활성화 할 수 있습니다. +자동 충전 금액은 변경할 수 있으며, 자동 충전 기능을 완전히 비활성화할 수도 있습니다. --- -## 월간 제한 +### 월간 사용 한도 -또한 전체 작업 공간에 대한 월간 사용 제한을 설정할 수 있습니다. -당신의 팀의 일원. +워크스페이스 전체 및 각 팀 구성원별로 월간 사용 한도를 설정할 수 있습니다. -예를 들어, 매달 사용 제한을 $20로 설정하면 Zen은 사용하지 않습니다. -한 달에 $ 20 이상. 그러나 자동 로드가 활성화되면 Zen이 종료 될 수 있습니다. -잔액이 $5 미만인 경우 $20 이상 충전. +예를 들어 월간 사용 한도를 $20로 설정한 경우, Zen은 한 달 동안 $20을 초과하여 사용하지 않습니다. +다만 자동 충전이 활성화되어 있는 경우, 잔액이 $5 미만으로 내려가면 자동으로 충전이 이루어질 수 있으므로 실제 청구 금액이 $20을 초과할 수 있습니다. --- -## 개인 정보 +## 개인정보 보호 -우리의 모든 모델은 미국에서 호스팅됩니다. 우리의 공급자는 제로 보유 정책을 따르고 모형 훈련을 위한 당신의 자료를 이용하지 않습니다, 뒤에 오는 예외로: +당사의 모든 모델은 미국에서 호스팅됩니다. 당사 제공자는 데이터 무보존(zero-retention) 정책을 따르며, 아래의 예외를 제외하고는 귀하의 데이터를 모델 학습에 사용하지 않습니다. -- Big Pickle : 무료 기간 동안 수집 된 데이터는 모델을 개선하기 위해 사용될 수있다. -- GLM 4.7 무료 : 무료 기간 동안 수집 된 데이터는 모델을 개선하기 위해 사용될 수있다. -- Kimi K2.5 무료 : 무료 기간 동안 수집 된 데이터는 모델을 개선하기 위해 사용될 수있다. -- MiniMax M2.1 무료 : 무료 기간 동안 수집 된 데이터는 모델을 개선하기 위해 사용될 수있다. -- OpenAI APIs: 요청은 [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data)에 따라 30일 동안 유지됩니다. -- Anthropic APIs : 요청은 [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage)에 따라 30 일 동안 유지됩니다. +- Big Pickle: 무료 제공 기간 동안 수집된 데이터는 모델 개선을 위해 사용될 수 있습니다. +- GLM 5 Free: 무료 제공 기간 동안 수집된 데이터는 모델 개선을 위해 사용될 수 있습니다. +- Kimi K2.5 Free: 무료 제공 기간 동안 수집된 데이터는 모델 개선을 위해 사용될 수 있습니다. +- MiniMax M2.5 Free: 무료 제공 기간 동안 수집된 데이터는 모델 개선을 위해 사용될 수 있습니다. +- OpenAI APIs: 요청 데이터는 [OpenAI의 데이터 정책](https://platform.openai.com/docs/guides/your-data)에 따라 30일간 보관됩니다. +- Anthropic APIs: 요청 데이터는 [Anthropic의 데이터 정책](https://docs.anthropic.com/en/docs/claude-code/data-usage)에 따라 30일간 보관됩니다. --- -## 팀 +## 팀을 위한 기능 -Zen은 팀을 위해 잘 작동합니다. 팀메이트를 초대 할 수 있습니다, 역할 할당, curate -당신의 팀 용도 및 더 많은 모델을. +Zen은 팀 환경에서도 효과적으로 활용할 수 있습니다. 팀원을 초대하고, 역할을 지정하며, 팀에서 사용할 모델을 선별하는 등 다양한 기능을 제공합니다. :::note -Workspaces는 현재 베타의 일부로 팀에 무료로 제공됩니다. +워크스페이스는 현재 베타 프로그램의 일환으로 팀에 무료로 제공되고 있습니다. ::: -작업 공간 관리는 현재 베타의 일부로 팀에 무료로 제공됩니다. 우리는 -가격을 곧 공유하십시오. +현재 베타 기간 동안 팀의 워크스페이스 관리 기능은 무료로 제공됩니다. 요금 정책에 대한 보다 자세한 내용은 추후 안내될 예정입니다. --- -## 역할 +### 역할 -작업 공간에 팀메이트를 초대하고 역할 할당 할 수 있습니다. +워크스페이스에 팀원을 초대하고 다음과 같은 역할을 부여할 수 있습니다. -- **Admin**: 모델, 회원, API 키 및 청구 -- **회원**: 자신의 API 키만 관리 +- **Admin**: 모델, 구성원, API 키 및 결제를 관리 +- **Member**: 본인의 API 키만 관리 -Admins는 또한 통제의 밑에 비용을 지키는 각 일원을 위한 매달 지출 한계를 놓을 수 있습니다. +관리자는 비용 통제를 위해 각 구성원별 월간 지출 한도를 설정할 수 있습니다. --- -## 모델 접근 +### 모델 접근 권한 -관리자는 작업 공간에 특정 모델을 활성화하거나 비활성화 할 수 있습니다. 장애인 모델에 요청은 오류를 반환합니다. +관리자는 워크스페이스에서 특정 모델의 사용을 활성화하거나 비활성화할 수 있습니다. 비활성화된 모델에 대한 요청은 오류를 반환합니다. -이것은 당신이 모델의 사용을 비활성화하려는 경우 유용합니다 -데이터 수집 +이는 데이터 수집이 이루어지는 모델의 사용을 제한하려는 경우에 유용합니다. --- ## BYOK (Bring Your Own Key) -OpenAI 또는 Anthropic API 키를 사용하여 Zen에서 다른 모델에 액세스 할 수 있습니다. +Zen에서 다른 모델을 계속 이용하면서도, OpenAI 또는 Anthropic의 자체 API 키를 사용할 수 있습니다. -자신의 키를 사용할 때, 토큰은 Zen에 의해 공급자에 의해 직접 청구됩니다. +자체 키를 사용하는 경우, 토큰 사용 요금은 Zen이 아닌 해당 제공자가 직접 청구합니다. -예를 들어, 조직은 이미 OpenAI 또는 Anthropic에 대한 열쇠가있을 수 있습니다. -Zen이 제공하는 것을 대신 사용하고 싶습니다. +예를 들어, 귀하의 조직이 이미 OpenAI 또는 Anthropic의 API 키를 보유하고 있는 경우, Zen에서 제공하는 키 대신 해당 키를 사용할 수 있습니다. --- ## 목표 -OpenCode Zen을 생성했습니다. +OpenCode Zen은 다음과 같은 목표를 바탕으로 개발되었습니다. -1. **Benchmark ** 코딩 에이전트에 대한 최고의 모델 / 프로바이더. -2. ** 고품질 ** 옵션에 액세스하고 더 싼 공급자에 고급 성능 또는 노선을 내리지 마십시오. -3. 비용에 판매해서 ** 가격 하락 **를 따라 통과하십시오; 그래서 단지 감적은 우리의 가공 비용을 커버하는 것입니다. -4. 잠금 해제 \*\* 다른 코딩 에이전트와 함께 사용할 수 있도록. 그리고 항상 opencode와 다른 모든 공급자를 사용할 수 있습니다. +1. 코딩 에이전트에 적합한 최상의 모델 및 제공자를 **벤치마킹**합니다. +2. 성능을 저하시키거나 더 저렴한 제공자로 우회하지 않고, **최고 품질의 옵션**에 접근할 수 있도록 합니다. +3. 가격 인하가 있을 경우 이를 원가로 반영하여 제공하며, 당사의 마진은 처리 수수료를 충당하기 위한 최소 수준으로 제한합니다. +4. 특정 서비스에 종속되지 않도록 하여, 다른 코딩 에이전트와도 자유롭게 함께 사용할 수 있도록 하며, OpenCode 내에서 다른 제공자 역시 언제든지 사용할 수 있도록 합니다. From 4fd3141ab5d43a55566042982fb4459b5716e140 Mon Sep 17 00:00:00 2001 From: chenmi Date: Tue, 17 Feb 2026 20:06:39 +0800 Subject: [PATCH 047/255] docs: improve zh-cn and zh-tw documentation translations (#13942) --- packages/web/src/content/docs/zh-cn/acp.mdx | 44 +- .../web/src/content/docs/zh-cn/agents.mdx | 189 +++-- packages/web/src/content/docs/zh-cn/cli.mdx | 318 ++++---- .../web/src/content/docs/zh-cn/commands.mdx | 95 ++- .../web/src/content/docs/zh-cn/config.mdx | 204 ++--- .../src/content/docs/zh-cn/custom-tools.mdx | 42 +- .../web/src/content/docs/zh-cn/ecosystem.mdx | 98 +-- .../web/src/content/docs/zh-cn/enterprise.mdx | 82 +-- .../web/src/content/docs/zh-cn/formatters.mdx | 115 +-- .../web/src/content/docs/zh-cn/github.mdx | 116 +-- .../web/src/content/docs/zh-cn/gitlab.mdx | 71 +- packages/web/src/content/docs/zh-cn/ide.mdx | 38 +- packages/web/src/content/docs/zh-cn/index.mdx | 134 ++-- .../web/src/content/docs/zh-cn/keybinds.mdx | 64 +- packages/web/src/content/docs/zh-cn/lsp.mdx | 136 ++-- .../src/content/docs/zh-cn/mcp-servers.mdx | 168 ++--- .../web/src/content/docs/zh-cn/models.mdx | 77 +- packages/web/src/content/docs/zh-cn/modes.mdx | 91 ++- .../web/src/content/docs/zh-cn/network.mdx | 20 +- .../src/content/docs/zh-cn/permissions.mdx | 94 +-- .../web/src/content/docs/zh-cn/plugins.mdx | 89 ++- .../web/src/content/docs/zh-cn/providers.mdx | 695 +++++++++--------- packages/web/src/content/docs/zh-cn/rules.mdx | 80 +- packages/web/src/content/docs/zh-cn/sdk.mdx | 268 ++++--- .../web/src/content/docs/zh-cn/server.mdx | 269 ++++--- packages/web/src/content/docs/zh-cn/share.mdx | 75 +- .../web/src/content/docs/zh-cn/skills.mdx | 114 +-- .../web/src/content/docs/zh-cn/themes.mdx | 108 +-- packages/web/src/content/docs/zh-cn/tools.mdx | 100 +-- .../content/docs/zh-cn/troubleshooting.mdx | 205 +++--- packages/web/src/content/docs/zh-cn/tui.mdx | 131 ++-- packages/web/src/content/docs/zh-cn/web.mdx | 62 +- .../src/content/docs/zh-cn/windows-wsl.mdx | 59 +- packages/web/src/content/docs/zh-cn/zen.mdx | 217 +++--- packages/web/src/content/docs/zh-tw/acp.mdx | 46 +- .../web/src/content/docs/zh-tw/agents.mdx | 226 +++--- packages/web/src/content/docs/zh-tw/cli.mdx | 326 ++++---- .../web/src/content/docs/zh-tw/commands.mdx | 102 +-- .../web/src/content/docs/zh-tw/config.mdx | 234 +++--- .../src/content/docs/zh-tw/custom-tools.mdx | 48 +- .../web/src/content/docs/zh-tw/ecosystem.mdx | 106 +-- .../web/src/content/docs/zh-tw/enterprise.mdx | 86 ++- .../web/src/content/docs/zh-tw/formatters.mdx | 115 +-- .../web/src/content/docs/zh-tw/github.mdx | 116 +-- .../web/src/content/docs/zh-tw/gitlab.mdx | 71 +- packages/web/src/content/docs/zh-tw/ide.mdx | 42 +- packages/web/src/content/docs/zh-tw/index.mdx | 113 ++- .../web/src/content/docs/zh-tw/keybinds.mdx | 66 +- packages/web/src/content/docs/zh-tw/lsp.mdx | 140 ++-- .../src/content/docs/zh-tw/mcp-servers.mdx | 168 ++--- .../web/src/content/docs/zh-tw/models.mdx | 85 ++- packages/web/src/content/docs/zh-tw/modes.mdx | 107 ++- .../web/src/content/docs/zh-tw/network.mdx | 20 +- .../src/content/docs/zh-tw/permissions.mdx | 92 +-- .../web/src/content/docs/zh-tw/plugins.mdx | 95 ++- .../web/src/content/docs/zh-tw/providers.mdx | 567 +++++++------- packages/web/src/content/docs/zh-tw/rules.mdx | 74 +- packages/web/src/content/docs/zh-tw/sdk.mdx | 240 +++--- .../web/src/content/docs/zh-tw/server.mdx | 205 +++--- packages/web/src/content/docs/zh-tw/share.mdx | 75 +- .../web/src/content/docs/zh-tw/skills.mdx | 100 +-- .../web/src/content/docs/zh-tw/themes.mdx | 104 +-- packages/web/src/content/docs/zh-tw/tools.mdx | 112 +-- .../content/docs/zh-tw/troubleshooting.mdx | 187 +++-- packages/web/src/content/docs/zh-tw/tui.mdx | 127 ++-- packages/web/src/content/docs/zh-tw/web.mdx | 60 +- .../src/content/docs/zh-tw/windows-wsl.mdx | 61 +- packages/web/src/content/docs/zh-tw/zen.mdx | 214 +++--- 68 files changed, 4652 insertions(+), 4546 deletions(-) diff --git a/packages/web/src/content/docs/zh-cn/acp.mdx b/packages/web/src/content/docs/zh-cn/acp.mdx index 47e0f3e05ce1..b07520c5e76c 100644 --- a/packages/web/src/content/docs/zh-cn/acp.mdx +++ b/packages/web/src/content/docs/zh-cn/acp.mdx @@ -1,31 +1,31 @@ --- title: ACP 支持 -description: 在任何 ACP 兼容编辑器中使用 opencode。 +description: 在任何兼容 ACP 的编辑器中使用 OpenCode。 --- -opencode 支持 [Agent Client Protocol](https://agentclientprotocol.com) (ACP),允许您直接在兼容的编辑器和 IDE 中使用它。 +OpenCode 支持 [Agent Client Protocol](https://agentclientprotocol.com)(ACP),允许你直接在兼容的编辑器和 IDE 中使用它。 :::tip -有关支持 ACP 的编辑器和工具的列表,请查看 [Zed ACP progress report](https://zed.dev/blog/acp-progress-report#available-now)。 +有关支持 ACP 的编辑器和工具列表,请查看 [ACP 进展报告](https://zed.dev/blog/acp-progress-report#available-now)。 ::: -ACP 是一种开放协议,用于标准化代码编辑器和 AI 编码代理之间的通信。 +ACP 是一个开放协议,用于标准化代码编辑器与 AI 编码代理之间的通信。 --- ## 配置 -要通过 ACP 使用 opencode,须编辑器配置为运行 `opencode acp` 命令。 +要通过 ACP 使用 OpenCode,请在编辑器中配置运行 `opencode acp` 命令。 -该命令将 opencode 作为 ACP 兼容的子进程启动,通过 stdio 通过 JSON-RPC 与您的编辑器进行通信。 +该命令会将 OpenCode 作为兼容 ACP 的子进程启动,通过 stdio 上的 JSON-RPC 与编辑器进行通信。 -以下是支持 ACP 的流行编辑器的示例。 +以下是支持 ACP 的常用编辑器的配置示例。 --- ### Zed -添加到您的 [Zed](https://zed.dev) 配置 (`~/.config/zed/settings.json`): +添加到你的 [Zed](https://zed.dev) 配置文件(`~/.config/zed/settings.json`)中: ```json title="~/.config/zed/settings.json" { @@ -38,9 +38,9 @@ ACP 是一种开放协议,用于标准化代码编辑器和 AI 编码代理之 } ``` -要打开它,请使用 **命令面板** 中的 `agent: new thread` 操作。 +打开方式:在**命令面板**中执行 `agent: new thread` 操作。 -您还可以通过编辑 `keymap.json` 来绑定键盘快捷键: +你也可以通过编辑 `keymap.json` 来绑定键盘快捷键: ```json title="keymap.json" [ @@ -67,9 +67,9 @@ ACP 是一种开放协议,用于标准化代码编辑器和 AI 编码代理之 --- -### JetBrains IDE +### JetBrains IDEs -根据 [文档](https://www.jetbrains.com/help/ai-assistant/acp.html) 添加到你的 [JetBrains IDE](https://www.jetbrains.com/) `acp.json`: +根据[文档](https://www.jetbrains.com/help/ai-assistant/acp.html),将以下内容添加到你的 [JetBrains IDE](https://www.jetbrains.com/) 的 acp.json 中: ```json title="acp.json" { @@ -82,13 +82,13 @@ ACP 是一种开放协议,用于标准化代码编辑器和 AI 编码代理之 } ``` -要打开它,请在 AI Chat 代理选择器中使用新的 "opencode" 代理。 +打开方式:在 AI Chat 代理选择器中选择新的 'OpenCode' 代理。 --- ### Avante.nvim -添加到您的 [Avante.nvim](https://github.com/yetone/avante.nvim) 配置: +添加到你的 [Avante.nvim](https://github.com/yetone/avante.nvim) 配置中: ```lua { @@ -121,7 +121,7 @@ ACP 是一种开放协议,用于标准化代码编辑器和 AI 编码代理之 ### CodeCompanion.nvim -相当于 opencode 网关 [CodeCompanion.nvim](https://github.com/olimorris/codecompanion.nvim) 中的 ACP 代理,接下来将以下内容添加到 Neovim 配置中: +要在 [CodeCompanion.nvim](https://github.com/olimorris/codecompanion.nvim) 中将 OpenCode 用作 ACP 代理,请将以下内容添加到你的 Neovim 配置中: ```lua require("codecompanion").setup({ @@ -136,21 +136,21 @@ require("codecompanion").setup({ }) ``` -此配置将 CodeCompanion.nvim 设置为使用 opencode 作为聊天的 ACP 代理。 +此配置将 CodeCompanion 设置为使用 OpenCode 作为聊天的 ACP 代理。 -如果您需要传递环境变量(如 `OPENCODE_API_KEY`),请参阅 CodeCompanion.nvim 文档中的 [Configuration: Adapters](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key) 了解完整信息。 +如果需要传递环境变量(如 `OPENCODE_API_KEY`),请参阅 CodeCompanion.nvim 文档中的[配置适配器:环境变量](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key)了解详细信息。 ## 支持 -opencode 通过 ACP 的工作方式与在终端中的工作方式相同。支持所有功能: +OpenCode 通过 ACP 使用时与在终端中使用的效果完全一致。所有功能均受支持: :::note -目前不支持某些内置斜杠命令,例如 `/undo` 和 `/redo`。 +部分内置斜杠命令(如 `/undo` 和 `/redo`)目前暂不支持。 ::: - 内置工具(文件操作、终端命令等) - 自定义工具和斜杠命令 -- 在 opencode 配置中配置的 MCP 服务器 -- `AGENTS.md` 的项目特定规则 -- 自定义程序和 linter +- 在 OpenCode 配置中配置的 MCP 服务器 +- 来自 `AGENTS.md` 的项目级规则 +- 自定义格式化工具和代码检查工具 - 代理和权限系统 diff --git a/packages/web/src/content/docs/zh-cn/agents.mdx b/packages/web/src/content/docs/zh-cn/agents.mdx index a94296f45257..2087c683668a 100644 --- a/packages/web/src/content/docs/zh-cn/agents.mdx +++ b/packages/web/src/content/docs/zh-cn/agents.mdx @@ -3,46 +3,45 @@ title: 代理 description: 配置和使用专门的代理。 --- -代理是专门的人工智能助手,可以针对特定任务和工作流程进行配置。它们允许您创建具有自定义提示、模型和工具访问权限的专用工具。 +代理是专门的 AI 助手,可以针对特定任务和工作流程进行配置。它们允许您创建具有自定义提示词、模型和工具访问权限的专用工具。 :::tip -使用 Plan 代理来分析代码并审查建议,而无需进行任何代码更改。 +使用 Plan 代理来分析代码和审查建议,而不会进行任何代码更改。 ::: -您可以在会话期间在代理之间切换,或使用 `@` 提及来调用它们。 +您可以在会话期间切换代理,或使用 `@` 提及来调用它们。 --- ## 类型 -OpenCode 有两种类型的代理;主代理和子代理。 +OpenCode 中有两种类型的代理:主代理和子代理。 --- ### 主代理 -主代理 (Primary) 是与您直接交互的主要助手。您可以使用 **Tab** 键或您配置的 `switch_agent` 键绑定循环浏览它们。这些代理处理您的主要对话。工具访问是通过权限配置的 - 例如,“Build”启用了所有工具,而“Plan”则受到限制。 +主代理是您直接交互的主要助手。您可以使用 **Tab** 键或配置的 `switch_agent` 快捷键来循环切换它们。这些代理处理您的主要对话。工具访问通过权限进行配置——例如,Build 启用了所有工具,而 Plan 则受到限制。 :::tip -您可以在会话期间使用 **Tab** 键在主代理之间进行切换。 +您可以在会话期间使用 **Tab** 键在主代理之间切换。 ::: -OpenCode 附带两个内置的主代理:**Build** 和 **Plan**。 -看看下面这些。 +OpenCode 内置了两个主代理:**Build** 和 **Plan**。我们将在下面介绍它们。 --- ### 子代理 -子代理 (Subagents) 是主代理可以调用​​来执行特定任务的专业助手。您还可以通过在消息中 **@提及** 它们来手动调用它们。 +子代理是主代理可以调用来执行特定任务的专业助手。您也可以通过在消息中 **@ 提及**它们来手动调用。 -OpenCode 附带两个内置子代理:**General** 和 **Explore**。我们将在下面看看这个。 +OpenCode 内置了两个子代理:**General** 和 **Explore**。我们将在下面介绍它们。 --- -## 内置 +## 内置代理 -OpenCode 附带两个内置主代理和两个内置子代理。 +OpenCode 内置了两个主代理和两个子代理。 --- @@ -50,7 +49,7 @@ OpenCode 附带两个内置主代理和两个内置子代理。 _模式_:`primary` -Build 是启用所有工具的 **默认** Primary 代理。这是用于需要完全访问文件操作和系统命令的开发工作的标准代理。 +Build 是启用了所有工具的**默认**主代理。这是用于需要完全访问文件操作和系统命令的开发工作的标准代理。 --- @@ -58,13 +57,13 @@ Build 是启用所有工具的 **默认** Primary 代理。这是用于需要完 _模式_:`primary` -专为规划和分析而设计的受限代理。我们使用权限系统为您提供更多控制并防止意外更改。 +一个专为规划和分析设计的受限代理。我们使用权限系统来为您提供更多控制权,并防止意外更改。 默认情况下,以下所有项均设置为 `ask`: -- `file edits`:所有书写、修复和编辑 +- `file edits`:所有写入、补丁和编辑 - `bash`:所有 bash 命令 -当您希望 LLM 分析代码、建议更改或创建计划而不是对代码库进行任何实际修改时,此代理非常有用。 +当您希望 LLM 分析代码、建议更改或创建计划,而不对代码库进行任何实际修改时,此代理非常有用。 --- @@ -72,7 +71,7 @@ _模式_:`primary` _模式_:`subagent` -用于研究复杂问题和执行多步骤任务的通用代理。它具有完整的工具访问权限(待办事项除外),因此可在需要时修改文件,并并行运行多个工作单元。 +一个用于研究复杂问题和执行多步骤任务的通用代理。拥有完整的工具访问权限(todo 除外),因此可以在需要时修改文件。可用于并行运行多个工作单元。 --- @@ -80,15 +79,15 @@ _模式_:`subagent` _模式_:`subagent` -用于探索代码库的快速只读代理。无法修改文件。当您需要按模式快速查找文件、搜索代码中的关键字或回答有关代码库的问题时,请使用此功能。 +一个用于探索代码库的快速只读代理。无法修改文件。当您需要按模式快速查找文件、搜索代码中的关键字或回答有关代码库的问题时,请使用此代理。 --- -### 使用 Compact +### 使用 Compaction _模式_:`primary` -隐藏的系统代理,将长上下文压缩为更小的抽象。它会在需要时自动运行,并且无法在 UI 中选择。 +隐藏的系统代理,将长上下文压缩为较小的摘要。它会在需要时自动运行,且无法在 UI 中选择。 --- @@ -96,7 +95,7 @@ _模式_:`primary` _模式_:`primary` -生成短会话标题的隐藏系统代理。它会自动运行,并且无法在 UI 中选择。 +隐藏的系统代理,用于生成简短的会话标题。它会自动运行,且无法在 UI 中选择。 --- @@ -104,33 +103,33 @@ _模式_:`primary` _模式_:`primary` -创建会话摘要的系统代理。它会自动运行,并且无法在 UI 中选择。 +隐藏的系统代理,用于创建会话摘要。它会自动运行,且无法在 UI 中选择。 --- ## 用法 -1. 对于主代理,请在会话期间使用 **Tab** 键循环浏览它们。您还可以使用配置的 `switch_agent` 键绑定。 +1. 对于主代理,在会话期间使用 **Tab** 键循环切换。您也可以使用配置的 `switch_agent` 快捷键。 -2. 可以调用子代理: - - **自动**由主代理根据其描述执行专门任务。 - - 通过在消息中 **@提及** 子代理手动进行。例如。 +2. 子代理可以通过以下方式调用: + - 由主代理根据其描述**自动**调用以执行专门任务。 + - 通过在消息中 **@ 提及**子代理来手动调用。例如: ```txt frame="none" @general help me search for this function ``` -3. **会话之间导航**:当子代理创建自己的子会话时,您可以使用以下命令在父会话和所有子会话之间导航: - - **\+Right**(或您配置的 `session_child_cycle` 键绑定)向前循环父级 → 子级 1 → 子级 2 → ... → 父级 - - **\+Left**(或您配置的 `session_child_cycle_reverse` 键绑定)向后循环父级 ← 子级 1 ← 子级 2 ← ... ← 父级 +3. **会话间导航**:当子代理创建自己的子会话时,您可以使用以下方式在父会话和所有子会话之间导航: + - **\+Right**(或配置的 `session_child_cycle` 快捷键)向前循环:父会话 → 子会话1 → 子会话2 → ... → 父会话 + - **\+Left**(或配置的 `session_child_cycle_reverse` 快捷键)向后循环:父会话 ← 子会话1 ← 子会话2 ← ... ← 父会话 - 这使您可以在主要对话和专门的子代理工作之间无缝切换。 + 这使您可以在主对话和专门的子代理工作之间无缝切换。 --- ## 配置 -您也可以自定义内置代理或通过配置创建您自己的代理。可以通过两种方式配置代理: +您可以自定义内置代理或通过配置创建自己的代理。代理可以通过两种方式进行配置: --- @@ -179,10 +178,10 @@ _模式_:`primary` ### Markdown -您还可以使用 Markdown 文件定义代理。将它们放入: +您还可以使用 Markdown 文件定义代理。将它们放在: - 全局:`~/.config/opencode/agents/` -- 每个项目:`.opencode/agents/` +- 项目级:`.opencode/agents/` ```markdown title="~/.config/opencode/agents/review.md" --- @@ -206,19 +205,19 @@ You are in code review mode. Focus on: Provide constructive feedback without making direct changes. ``` -Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代理。 +Markdown 文件名即为代理名称。例如,`review.md` 会创建一个名为 `review` 的代理。 --- ## 选项 -让我们详细看看这些配置选项。 +让我们详细了解这些配置选项。 --- ### 描述 -使用 `description` 选项提供代理的作用以及使用时的简要描述。 +使用 `description` 选项提供代理的功能及使用场景的简要描述。 ```json title="opencode.json" { @@ -230,15 +229,15 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -这是一个 **必需的** 配置选项。 +这是一个**必需的**配置选项。 --- ### 温度 -使用 `temperature` 配置控制 LLM 响应的随机性和创意。 +使用 `temperature` 配置控制 LLM 响应的随机性和创造力。 -较低的值使响应更加集中和确定,而较高的值则增加创造力和可变性。 +较低的值使响应更加集中和确定,而较高的值则增加创造力和多样性。 ```json title="opencode.json" { @@ -253,11 +252,11 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -温度值范围通常为 0.0 到 1.0: +温度值通常范围为 0.0 到 1.0: -- **0.0-0.2**:响应更集中、确定性更高,适合代码分析和规划 -- **0.3-0.5**:平衡型响应,兼顾稳定性与创造力 -- **0.6-1.0**:响应更有创意和多样性,适合头脑风暴和探索 +- **0.0-0.2**:非常集中和确定性的响应,适合代码分析和规划 +- **0.3-0.5**:平衡的响应,兼顾一定创造力,适合一般开发任务 +- **0.6-1.0**:更有创造力和多样性的响应,适合头脑风暴和探索 ```json title="opencode.json" { @@ -277,15 +276,15 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -如果未指定温度,OpenCode 将使用特定于模型的默认值;大多数模型通常为 0,Qwen 模型为 0.55。 +如果未指定温度,OpenCode 将使用模型特定的默认值;大多数模型通常为 0,Qwen 模型为 0.55。 --- ### 最大步数 -控制代理在被迫仅使用文本响应之前可以执行的最大代理迭代次数。这允许希望控制成本的用户对代理操作设置限制。 +控制代理在被强制以纯文本响应之前可以执行的最大代理迭代次数。这允许希望控制成本的用户对代理操作设置限制。 -如果未设置,代理将继续迭代,直到模型选择停止或用户中断会话。 +如果未设置此选项,代理将持续迭代,直到模型选择停止或用户中断会话。 ```json title="opencode.json" { @@ -299,17 +298,17 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -当达到限制时,代理会收到特殊的系统提示,指示其响应其工作摘要和建议的剩余任务。 +当达到限制时,代理会收到一个特殊的系统提示词,指示其回复工作摘要和建议的剩余任务。 :::caution -旧版 `maxSteps` 字段已废弃。请改用 `steps`。 +旧版 `maxSteps` 字段已弃用。请改用 `steps`。 ::: --- ### 禁用 -设置为 `true` 以取消代理。 +设置为 `true` 以禁用代理。 ```json title="opencode.json" { @@ -323,9 +322,9 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 --- -### 提示 +### 提示词 -使用 `prompt` 配置为代理指定自定义系统提示文件。提示文件应包含特定于代理目的的说明。 +使用 `prompt` 配置为代理指定自定义系统提示词文件。提示词文件应包含针对代理用途的具体指令。 ```json title="opencode.json" { @@ -337,16 +336,16 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -该路径相对于文件所在位置的配置。因此,这适用于全局 OpenCode 配置和项目特定配置。 +此路径相对于配置文件所在位置。因此它同时适用于全局 OpenCode 配置和项目级配置。 --- ### 模型 -使用 `model` 配置此代理的模型。对于使用针对不同任务优化的不同模型很有帮助。例如,更快的规划模型、更强大的实施模型。 +使用 `model` 配置为代理覆盖模型。适用于针对不同任务使用不同的优化模型。例如,用更快的模型进行规划,用更强大的模型进行实现。 :::tip -如果您不指定模型,主代理将使用 [全局配置的模型](/docs/config#models),而子代理将使用调用子代理的主代理的模型。 +如果您不指定模型,主代理将使用[全局配置的模型](/docs/config#models),而子代理将使用调用它的主代理所使用的模型。 ::: ```json title="opencode.json" @@ -359,13 +358,13 @@ Markdown 文件名成为代理名称。例如,`review.md` 创建 `review` 代 } ``` -OpenCode 配置中的模型 ID 使用格式 `provider/model-id`。例如,如果您使用 [OpenCode Zen](/docs/zen),则您将使用 `opencode/gpt-5.1-codex` 来表示 GPT 5.1 Codex。 +OpenCode 配置中的模型 ID 使用 `provider/model-id` 格式。例如,如果您使用 [OpenCode Zen](/docs/zen),则可以使用 `opencode/gpt-5.1-codex` 来表示 GPT 5.1 Codex。 --- ### 工具 -使用 `tools` 配置控制此代理中可用的工具。您可以通过将特定工具设置为 `true` 或 `false` 来启用或禁用特定工具。 +使用 `tools` 配置控制代理中可用的工具。您可以通过将特定工具设置为 `true` 或 `false` 来启用或禁用它们。 ```json title="opencode.json" {3-6,9-12} { @@ -386,7 +385,7 @@ OpenCode 配置中的模型 ID 使用格式 `provider/model-id`。例如,如 ``` :::note -特定于代理的配置会覆盖全局配置。 +代理级配置会覆盖全局配置。 ::: 您还可以使用通配符同时控制多个工具。例如,要禁用 MCP 服务器中的所有工具: @@ -406,17 +405,17 @@ OpenCode 配置中的模型 ID 使用格式 `provider/model-id`。例如,如 } ``` -[了解有关工具的更多信息](/docs/tools)。 +[了解更多关于工具的信息](/docs/tools)。 --- ### 权限 -您可以配置权限来管理代理可以执行的操作。 目前,`edit`、`bash` 和 `webfetch` 工具的权限可以配置为: +您可以配置权限来管理代理可以执行的操作。目前,`edit`、`bash` 和 `webfetch` 工具的权限可以配置为: -- `"ask"` — 运行工具提示批准之前 -- `"allow"` — 尚未批准所有操作 -- `"deny"` — 取消该工具 +- `"ask"` — 运行工具前提示审批 +- `"allow"` — 允许所有操作,无需审批 +- `"deny"` — 禁用该工具 ```json title="opencode.json" { @@ -427,7 +426,7 @@ OpenCode 配置中的模型 ID 使用格式 `provider/model-id`。例如,如 } ``` -您可以覆盖每个代理的这些权限。 +您可以按代理覆盖这些权限。 ```json title="opencode.json" {3-5,8-10} { @@ -464,7 +463,7 @@ permission: Only analyze code and suggest changes. ``` -您可以设置特定的 bash 命令的权限。 +您可以为特定的 bash 命令设置权限。 ```json title="opencode.json" {7} { @@ -482,7 +481,7 @@ Only analyze code and suggest changes. } ``` -这可以采用全局模式。 +这可以使用 glob 模式。 ```json title="opencode.json" {7} { @@ -500,7 +499,7 @@ Only analyze code and suggest changes. ``` 您还可以使用 `*` 通配符来管理所有命令的权限。 -由于最后一个匹配规则优先,因此将 `*` 通配符放在前面,将特定规则放在后面。 +由于最后匹配的规则优先,请将 `*` 通配符放在前面,将具体规则放在后面。 ```json title="opencode.json" {8} { @@ -518,13 +517,13 @@ Only analyze code and suggest changes. } ``` -[了解有关权限的更多信息](/docs/permissions)。 +[了解更多关于权限的信息](/docs/permissions)。 --- ### 模式 -使用 `mode` 配置控制代理的模式。 `mode` 选项用于确定如何使用代理。 +使用 `mode` 配置控制代理的模式。`mode` 选项用于确定代理的使用方式。 ```json title="opencode.json" { @@ -536,13 +535,13 @@ Only analyze code and suggest changes. } ``` -`mode` 选项可设置为 `primary`、`subagent` 或 `all`。如果未指定 `mode`,则默认为 `all`。 +`mode` 选项可以设置为 `primary`、`subagent` 或 `all`。如果未指定 `mode`,则默认为 `all`。 --- ### 隐藏 -使用 `hidden: true` 从 `@` 自动完成菜单隐藏子代理。对于只能由其他代理通过任务工具以编程方式调用的内部子代理很有帮助。 +使用 `hidden: true` 将子代理从 `@` 自动补全菜单中隐藏。适用于只应由其他代理通过 Task 工具以编程方式调用的内部子代理。 ```json title="opencode.json" { @@ -555,17 +554,17 @@ Only analyze code and suggest changes. } ``` -这仅影响自动完成菜单中的用户可见性。如果权限允许,模型仍然可以通过任务工具调用隐藏代理。 +这仅影响自动补全菜单中的用户可见性。如果权限允许,模型仍然可以通过 Task 工具调用隐藏的代理。 :::note -仅适用于 `mode: subagent` 代理。 +仅适用于 `mode: subagent` 的代理。 ::: --- ### 任务权限 -使用 `permission.task` 控制代理可以通过任务工具调用哪些子代理。使用 glob 模式进行灵活匹配。 +使用 `permission.task` 控制代理可以通过 Task 工具调用哪些子代理。使用 glob 模式进行灵活匹配。 ```json title="opencode.json" { @@ -584,21 +583,21 @@ Only analyze code and suggest changes. } ``` -当设置为 `deny` 时,子代理社区任务工具描述中因此完全删除,模型不会尝试调用它。 +当设置为 `deny` 时,子代理将从 Task 工具描述中完全移除,因此模型不会尝试调用它。 :::tip -规则按顺序评估,**最后匹配的规则触发**。在上面的示例中,`orchestrator-planner` 匹配 `*`(拒绝)和 `orchestrator-*`(允许),但由于 `orchestrator-*` 位于 `*` 之后,因此结果为 `allow`。 +规则按顺序评估,**最后匹配的规则优先**。在上面的示例中,`orchestrator-planner` 同时匹配 `*`(deny)和 `orchestrator-*`(allow),但由于 `orchestrator-*` 在 `*` 之后,所以结果为 `allow`。 ::: :::tip -用户始终可以通过 `@` 自动完成菜单直接调用任何子代理,即使代理的任务权限会拒绝它。 +用户始终可以通过 `@` 自动补全菜单直接调用任何子代理,即使代理的任务权限会拒绝它。 ::: --- ### 颜色 -在 UI 中的界面外观中使用 `color` 选项自定义代理。这会影响代理在界面中的显示方式。 +使用 `color` 选项自定义代理在 UI 中的视觉外观。这会影响代理在界面中的显示方式。 使用有效的十六进制颜色(例如 `#FF5733`)或主题颜色:`primary`、`secondary`、`accent`、`success`、`warning`、`error`、`info`。 @@ -619,7 +618,7 @@ Only analyze code and suggest changes. ### Top P -使用 `top_p` 选项控制响应多样性。控制随机性的温度替代方案。 +使用 `top_p` 选项控制响应多样性。这是控制随机性的温度替代方案。 ```json title="opencode.json" { @@ -635,11 +634,11 @@ Only analyze code and suggest changes. --- -### 其他 +### 其他选项 -您在代理配置中指定的任何其他选项都将作为模型选项 **直接** 传递给提供商。这允许您使用特定于提供商的功能和参数。 +您在代理配置中指定的任何其他选项都将作为模型选项**直接传递**给提供商。这允许您使用提供商特定的功能和参数。 -例如,使用 OpenAI 的推理模型,您可以控制推理工作: +例如,使用 OpenAI 的推理模型时,您可以控制推理力度: ```json title="opencode.json" {6,7} { @@ -654,10 +653,10 @@ Only analyze code and suggest changes. } ``` -这些附加选项是特定于模型和提供商的。检查提供商的文档以获取可用参数。 +这些附加选项是模型和提供商特定的。请查阅您的提供商文档以获取可用参数。 :::tip -运行 `opencode models` 查看可用模型的列表。 +运行 `opencode models` 查看可用模型列表。 ::: --- @@ -672,23 +671,23 @@ opencode agent create 此交互式命令将: -1. 询问代理保存在哪里;全局或特定项目。 +1. 询问代理的保存位置——全局或项目级。 2. 描述代理应该做什么。 -3. 生成适当的系统提示和标识符。 +3. 生成合适的系统提示词和标识符。 4. 让您选择代理可以访问哪些工具。 -5. 最后,使用代理配置创建一个 markdown 文件。 +5. 最后,创建一个包含代理配置的 Markdown 文件。 --- -## 使用案例 +## 使用场景 -以下是不同代理的一些常见用例。 +以下是不同代理的一些常见使用场景。 -- **Build Agent**:启用所有工具的完整开发工作 -- **Plan Agent**:分析规划,不做改动 -- **Review Agent**:具有只读访问权限和文档工具的代码审查 -- **Debug Agent**:专注于启用 bash 和读取工具的调查 -- **Docs Agent**:使用文件操作但不使用系统命令的文档编写 +- **Build 代理**:启用所有工具的完整开发工作 +- **Plan 代理**:分析和规划,不进行任何更改 +- **Review 代理**:具有只读访问权限和文档工具的代码审查 +- **Debug 代理**:专注于问题排查,启用 bash 和读取工具 +- **Docs 代理**:文档编写,具有文件操作但不使用系统命令 --- @@ -697,7 +696,7 @@ opencode agent create 以下是一些您可能会觉得有用的示例代理。 :::tip -您有想要分享的代理吗? [提交 PR](https://github.com/anomalyco/opencode)。 +您有想要分享的代理吗?[提交 PR](https://github.com/anomalyco/opencode)。 ::: --- @@ -724,7 +723,7 @@ Focus on: --- -### 安全审计员 +### 安全审计代理 ```markdown title="~/.config/opencode/agents/security-auditor.md" --- diff --git a/packages/web/src/content/docs/zh-cn/cli.mdx b/packages/web/src/content/docs/zh-cn/cli.mdx index 6d2ea032d115..490d59ca0bea 100644 --- a/packages/web/src/content/docs/zh-cn/cli.mdx +++ b/packages/web/src/content/docs/zh-cn/cli.mdx @@ -1,17 +1,17 @@ --- title: CLI -description: opencode CLI 选项和命令。 +description: OpenCode CLI 选项和命令。 --- import { Tabs, TabItem } from "@astrojs/starlight/components" -默认情况下,opencode CLI 在不带任何参数运行时启动 [TUI](/docs/tui)。 +OpenCode CLI 在不带任何参数运行时,默认启动 [TUI](/docs/tui)。 ```bash opencode ``` -但它也接受本页记录的命令。这允许您以编程方式与 opencode 交互。 +但它也接受本页面中记录的命令,使您可以通过编程方式与 OpenCode 进行交互。 ```bash opencode run "Explain how closures work in JavaScript" @@ -21,7 +21,7 @@ opencode run "Explain how closures work in JavaScript" ### tui -启动 opencode 终端用户界面。 +启动 OpenCode 终端用户界面。 ```bash opencode [project] @@ -32,25 +32,25 @@ opencode [project] | 标志 | 简写 | 描述 | | ------------ | ---- | --------------------------------------------------------- | | `--continue` | `-c` | 继续上一个会话 | -| `--session` | `-s` | 继续的会话 ID | -| `--fork` | | 继续时分叉会话(与 `--continue` 或 `--session` 一起使用) | -| `--prompt` | | 使用的提示 | -| `--model` | `-m` | 使用的模型 (provider/model) | -| `--agent` | | 使用的代理 | +| `--session` | `-s` | 要继续的会话 ID | +| `--fork` | | 继续时分叉会话(与 `--continue` 或 `--session` 配合使用) | +| `--prompt` | | 要使用的提示词 | +| `--model` | `-m` | 要使用的模型,格式为 provider/model | +| `--agent` | | 要使用的代理 | | `--port` | | 监听端口 | -| `--hostname` | | 监听的主机名 | +| `--hostname` | | 监听主机名 | --- ## 命令 -opencode CLI 还具有以下命令。 +OpenCode CLI 还提供以下命令。 --- ### agent -管理 opencode 代理。 +管理 OpenCode 的代理。 ```bash opencode agent [command] @@ -60,13 +60,13 @@ opencode agent [command] ### attach -将终端附加到通过 `serve` 或 `web` 命令启动的已运行 opencode 后端服务器。 +将终端连接到已通过 `serve` 或 `web` 命令启动的 OpenCode 后端服务器。 ```bash opencode attach [url] ``` -这允许将 TUI 与远程 opencode 后端一起使用。例如: +这允许将 TUI 与远程 OpenCode 后端配合使用。例如: ```bash # Start the backend server for web/mobile access @@ -81,19 +81,19 @@ opencode attach http://10.20.30.40:4096 | 标志 | 简写 | 描述 | | ----------- | ---- | ------------------- | | `--dir` | | 启动 TUI 的工作目录 | -| `--session` | `-s` | 继续的会话 ID | +| `--session` | `-s` | 要继续的会话 ID | --- #### create -使用自定义配置创建新代理。 +使用自定义配置创建新的代理。 ```bash opencode agent create ``` -此命令将指导您使用自定义系统提示和工具配置创建新代理。 +此命令将引导您使用自定义系统提示词和工具配置来创建新的代理。 --- @@ -109,7 +109,7 @@ opencode agent list ### auth -用于管理提供商的凭据和登录的命令。 +管理提供商的凭据和登录信息的命令。 ```bash opencode auth [command] @@ -119,25 +119,25 @@ opencode auth [command] #### login -opencode 由 [Models.dev](https://models.dev) 上的提供商列表支持,因此您可以使用 `opencode auth login` 来为您想要使用的任何提供商配置 API 密钥。它存储在 `~/.local/share/opencode/auth.json` 中。 +OpenCode 基于 [Models.dev](https://models.dev) 的提供商列表运行,因此您可以使用 `opencode auth login` 为任何想要使用的提供商配置 API 密钥。密钥存储在 `~/.local/share/opencode/auth.json` 中。 ```bash opencode auth login ``` -当 opencode 启动时,它会从凭据文件加载提供商。以及如果在您的环境或项目中的 `.env` 文件中定义了任何密钥。 +OpenCode 启动时会从凭据文件加载提供商信息,同时也会加载环境变量或项目中 `.env` 文件中定义的密钥。 --- #### list -列出凭据文件中存储的所有经过身份验证的提供商。 +列出凭据文件中存储的所有已认证提供商。 ```bash opencode auth list ``` -或者简短的版本。 +或使用简写版本。 ```bash opencode auth ls @@ -147,7 +147,7 @@ opencode auth ls #### logout -通过从凭据文件中清除提供商,将您从提供商中注销。 +从凭据文件中清除提供商信息以完成登出。 ```bash opencode auth logout @@ -157,7 +157,7 @@ opencode auth logout ### github -管理 GitHub 代理以实现存储库自动化。 +管理用于仓库自动化的 GitHub 代理。 ```bash opencode github [command] @@ -167,36 +167,36 @@ opencode github [command] #### install -在您的存储库中安装 GitHub 代理。 +在您的仓库中安装 GitHub 代理。 ```bash opencode github install ``` -这将设置必要的 GitHub Actions 工作流程并指导您完成配置过程。 [了解更多](/docs/github)。 +此命令会设置必要的 GitHub Actions 工作流并引导您完成配置过程。[了解更多](/docs/github)。 --- #### run -运行 GitHub 代理。这通常用在 GitHub Actions 中。 +运行 GitHub 代理。通常在 GitHub Actions 中使用。 ```bash opencode github run ``` -#### 标志 +##### 标志 | 标志 | 描述 | | --------- | ------------------------------ | | `--event` | 用于运行代理的 GitHub 模拟事件 | -| `--token` | GitHub 个人访问 Token | +| `--token` | GitHub 个人访问令牌 | --- ### mcp -管理模型上下文协议 (MCP) 服务器。 +管理 Model Context Protocol 服务器。 ```bash opencode mcp [command] @@ -212,7 +212,7 @@ opencode mcp [command] opencode mcp add ``` -此命令将指导您添加本地或远程 MCP 服务器。 +此命令将引导您添加本地或远程 MCP 服务器。 --- @@ -224,7 +224,7 @@ opencode mcp add opencode mcp list ``` -或者使用简短版本。 +或使用简写版本。 ```bash opencode mcp ls @@ -234,7 +234,7 @@ opencode mcp ls #### auth -使用启用 OAuth 的 MCP 服务器进行身份验证。 +对支持 OAuth 的 MCP 服务器进行认证。 ```bash opencode mcp auth [name] @@ -242,13 +242,13 @@ opencode mcp auth [name] 如果您不提供服务器名称,系统将提示您从可用的支持 OAuth 的服务器中进行选择。 -您还可以列出支持 OAuth 的服务器及其身份验证状态。 +您还可以列出支持 OAuth 的服务器及其认证状态。 ```bash opencode mcp auth list ``` -或者使用简短版本。 +或使用简写版本。 ```bash opencode mcp auth ls @@ -258,7 +258,7 @@ opencode mcp auth ls #### logout -删除 MCP 服务器的 OAuth 凭据。 +移除 MCP 服务器的 OAuth 凭据。 ```bash opencode mcp logout [name] @@ -284,11 +284,11 @@ opencode mcp debug opencode models [provider] ``` -此命令以 `provider/model` 格式显示您配置的提供商中可用的所有模型。 +此命令以 `provider/model` 的格式显示所有已配置提供商中可用的模型。 -这对于确定 [你的配置](/docs/config/) 中使用的确切模型名称很有帮助。 +这对于确定在[配置文件](/docs/config/)中使用的确切模型名称非常有用。 -您可以选择提供提供商 ID 并按该提供商筛选模型。 +您可以选择传入提供商 ID 来按提供商筛选模型。 ```bash opencode models anthropic @@ -299,9 +299,9 @@ opencode models anthropic | 标志 | 描述 | | ----------- | ---------------------------------------- | | `--refresh` | 从 models.dev 刷新模型缓存 | -| `--verbose` | 使用更详细的模型输出(包括成本等元数据) | +| `--verbose` | 使用更详细的模型输出(包含费用等元数据) | -使用 `--refresh` 标志来更新服务器的模型列表。当新模型已添加到提供商并且您希望在 opencode 中查看它们时,这非常有用。 +使用 `--refresh` 标志可以更新缓存的模型列表。当提供商新增了模型并且您希望在 OpenCode 中看到它们时,此功能非常有用。 ```bash opencode models --refresh @@ -311,19 +311,19 @@ opencode models --refresh ### run -通过直接传递提示以非交互模式运行 opencode。 +以非交互模式运行 OpenCode,直接传入提示词。 ```bash opencode run [message..] ``` -这对于编写脚本、自动化,或者当您想要快速获得答案而不是完整的 TUI 时非常有用。例如。 +这对于脚本编写、自动化或无需启动完整 TUI 即可快速获取答案的场景非常有用。例如: ```bash "opencode run" opencode run Explain the use of context in Go ``` -您还可以附加到正在运行的 `opencode serve` 实例,以避免每次运行时 MCP 服务器冷启动时间: +您还可以连接到正在运行的 `opencode serve` 实例,以避免每次运行时 MCP 服务器的冷启动时间: ```bash # Start a headless server in one terminal @@ -335,47 +335,47 @@ opencode run --attach http://localhost:4096 "Explain async/await in JavaScript" #### 标志 -| 标志 | 简写 | 描述 | -| ------------ | ---- | --------------------------------------------------------------- | -| `--command` | | 要运行的命令,使用消息作为参数 | -| `--continue` | `-c` | 继续上一个会话 | -| `--session` | `-s` | 继续的会话 ID | -| `--fork` | | 继续时分叉会话(与 `--continue` 或 `--session` 一起使用) | -| `--share` | | 分享会话 | -| `--model` | `-m` | 使用的模型 (provider/model) | -| `--agent` | | 使用的代理 | -| `--file` | `-f` | 要附加到消息的文件 | -| `--format` | | 格式:default(格式化)或 json(原始 JSON 事件) | -| `--title` | | 会话标题(如果未提供值,则使用截断的提示) | -| `--attach` | | 连接到正在运行的 opencode 服务器(例如,http://localhost:4096) | -| `--port` | | 本地服务器的端口(默认为随机端口) | +| 标志 | 简写 | 描述 | +| ------------ | ---- | -------------------------------------------------------------- | +| `--command` | | 要运行的命令,使用 message 作为参数 | +| `--continue` | `-c` | 继续上一个会话 | +| `--session` | `-s` | 要继续的会话 ID | +| `--fork` | | 继续时分叉会话(与 `--continue` 或 `--session` 配合使用) | +| `--share` | | 分享会话 | +| `--model` | `-m` | 要使用的模型,格式为 provider/model | +| `--agent` | | 要使用的代理 | +| `--file` | `-f` | 附加到消息的文件 | +| `--format` | | 格式:default(格式化输出)或 json(原始 JSON 事件) | +| `--title` | | 会话标题(未提供值时使用截断的提示词) | +| `--attach` | | 连接到正在运行的 opencode 服务器(例如 http://localhost:4096) | +| `--port` | | 本地服务器端口(默认为随机端口) | --- ### serve -启动无头 opencode 服务器以进行 API 访问。查看 [服务器文档](/docs/server) 以获取完整的 HTTP 接口。 +启动无界面的 OpenCode 服务器以提供 API 访问。查看[服务器文档](/docs/server)了解完整的 HTTP 接口。 ```bash opencode serve ``` -这将启动一个 HTTP 服务器,该服务器提供对 opencode 功能的 API 访问,无需 TUI 界面。设置 `OPENCODE_SERVER_PASSWORD` 以启用 HTTP 基本身份验证(用户名默认为 `opencode`)。 +此命令启动一个 HTTP 服务器,提供对 OpenCode 功能的 API 访问,无需 TUI 界面。设置 `OPENCODE_SERVER_PASSWORD` 可启用 HTTP 基本认证(用户名默认为 `opencode`)。 #### 标志 -| 标志 | 描述 | -| ------------ | ------------------------ | -| `--port` | 监听端口 | -| `--hostname` | 监听的主机名 | -| `--mdns` | 启用 mDNS 发现 | -| `--cors` | 允许 CORS 的其他浏览器源 | +| 标志 | 描述 | +| ------------ | -------------------------- | +| `--port` | 监听端口 | +| `--hostname` | 监听主机名 | +| `--mdns` | 启用 mDNS 发现 | +| `--cors` | 允许 CORS 的额外浏览器来源 | --- ### session -管理 opencode 会话。 +管理 OpenCode 会话。 ```bash opencode session [command] @@ -385,7 +385,7 @@ opencode session [command] #### list -列出所有 opencode 会话。 +列出所有 OpenCode 会话。 ```bash opencode session list @@ -393,16 +393,16 @@ opencode session list ##### 标志 -| 标志 | 简写 | 描述 | -| ------------- | ---- | ------------------------------ | -| `--max-count` | `-n` | 限制为最近的 N 个会话 | -| `--format` | | 输出格式:table 或 json(table) | +| 标志 | 简写 | 描述 | +| ------------- | ---- | ------------------------------------- | +| `--max-count` | `-n` | 限制为最近 N 个会话 | +| `--format` | | 输出格式:table 或 json(默认 table) | --- ### stats -显示 opencode 会话的 Token 使用情况和成本统计信息。 +显示 OpenCode 会话的 Token 用量和费用统计信息。 ```bash opencode stats @@ -410,12 +410,12 @@ opencode stats #### 标志 -| 标志 | 描述 | -| ----------- | -------------------------------------------------------- | -| `--days` | 显示过去 N 天(所有时间)的统计数据 | -| `--tools` | 显示工具数量(全部) | -| `--models` | 显示模型使用情况细分(默认隐藏)。输入一个数字来显示前 N | -| `--project` | 按项目过滤(所有项目,空字符串:当前项目) | +| 标志 | 描述 | +| ----------- | ------------------------------------------------------ | +| `--days` | 显示最近 N 天的统计信息(默认为所有时间) | +| `--tools` | 显示的工具数量(默认为全部) | +| `--models` | 显示模型用量明细(默认隐藏)。传入数字可显示前 N 个 | +| `--project` | 按项目筛选(默认为所有项目,传入空字符串表示当前项目) | --- @@ -433,13 +433,13 @@ opencode export [sessionID] ### import -从 JSON 文件或 opencode 共享 URL 导入会话数据。 +从 JSON 文件或 OpenCode 分享链接导入会话数据。 ```bash opencode import ``` -您可以从本地文件或 opencode 共享 URL 导入。 +您可以从本地文件或 OpenCode 分享链接导入。 ```bash opencode import session.json @@ -450,48 +450,48 @@ opencode import https://opncd.ai/s/abc123 ### web -使用 Web 界面启动无头 opencode 服务器。 +启动带有 Web 界面的无界面 OpenCode 服务器。 ```bash opencode web ``` -这将启动 HTTP 服务器并打开 Web 浏览器以通过 Web 界面访问 opencode。设置 `OPENCODE_SERVER_PASSWORD` 以启用 HTTP 基本身份验证(用户名默认为 `opencode`)。 +此命令启动一个 HTTP 服务器并打开浏览器,通过 Web 界面访问 OpenCode。设置 `OPENCODE_SERVER_PASSWORD` 可启用 HTTP 基本认证(用户名默认为 `opencode`)。 #### 标志 -| 标志 | 描述 | -| ------------ | ------------------------ | -| `--port` | 监听端口 | -| `--hostname` | 监听的主机名 | -| `--mdns` | 启用 mDNS 发现 | -| `--cors` | 允许 CORS 的其他浏览器源 | +| 标志 | 描述 | +| ------------ | -------------------------- | +| `--port` | 监听端口 | +| `--hostname` | 监听主机名 | +| `--mdns` | 启用 mDNS 发现 | +| `--cors` | 允许 CORS 的额外浏览器来源 | --- ### acp -启动 ACP (Agent Client Protocol) 服务器。 +启动 ACP(Agent Client Protocol)服务器。 ```bash opencode acp ``` -此命令启动一个 ACP 服务器,该服务器使用 nd-JSON 通过 stdin/stdout 进行通信。 +此命令启动一个通过 stdin/stdout 使用 nd-JSON 进行通信的 ACP 服务器。 #### 标志 -| 标志 | 描述 | -| ------------ | ------------ | -| `--cwd` | 工作目录 | -| `--port` | 监听端口 | -| `--hostname` | 监听的主机名 | +| 标志 | 描述 | +| ------------ | ---------- | +| `--cwd` | 工作目录 | +| `--port` | 监听端口 | +| `--hostname` | 监听主机名 | --- ### uninstall -卸载 opencode 并删除所有相关文件。 +卸载 OpenCode 并删除所有相关文件。 ```bash opencode uninstall @@ -499,30 +499,30 @@ opencode uninstall #### 标志 -| 标志 | 简写 | 描述 | -| --------------- | ---- | ---------------------------- | -| `--keep-config` | `-c` | 保留配置文件 | -| `--keep-data` | `-d` | 保留会话数据和快照 | -| `--dry-run` | | 显示将删除的内容但不实际删除 | -| `--force` | `-f` | 跳过确认提示 | +| 标志 | 简写 | 描述 | +| --------------- | ---- | ------------------------------ | +| `--keep-config` | `-c` | 保留配置文件 | +| `--keep-data` | `-d` | 保留会话数据和快照 | +| `--dry-run` | | 显示将被删除的内容但不实际删除 | +| `--force` | `-f` | 跳过确认提示 | --- ### upgrade -将 opencode 更新到最新版本或特定版本。 +将 OpenCode 更新到最新版本或指定版本。 ```bash opencode upgrade [target] ``` -升级到最新版本。 +更新到最新版本。 ```bash opencode upgrade ``` -升级到特定版本。 +更新到指定版本。 ```bash opencode upgrade v0.1.48 @@ -532,72 +532,72 @@ opencode upgrade v0.1.48 | 标志 | 简写 | 描述 | | ---------- | ---- | ------------------------------------------ | -| `--method` | `-m` | 使用的安装方法;curl, npm, pnpm, bun, brew | +| `--method` | `-m` | 使用的安装方式:curl、npm、pnpm、bun、brew | --- ## 全局标志 -opencode CLI 接受以下全局标志。 +OpenCode CLI 接受以下全局标志。 -| 标志 | 简写 | 描述 | -| -------------- | ---- | ----------------------------------- | -| `--help` | `-h` | 显示帮助 | -| `--version` | `-v` | 打印版本号 | -| `--print-logs` | | 将日志打印到 stderr | -| `--log-level` | | 日志级别 (DEBUG, INFO, WARN, ERROR) | +| 标志 | 简写 | 描述 | +| -------------- | ---- | ------------------------------------ | +| `--help` | `-h` | 显示帮助信息 | +| `--version` | `-v` | 打印版本号 | +| `--print-logs` | | 将日志输出到 stderr | +| `--log-level` | | 日志级别(DEBUG、INFO、WARN、ERROR) | --- ## 环境变量 -可以使用环境变量配置 opencode。 - -| 变量 | 类型 | 描述 | -| ------------------------------------- | ------- | ----------------------------------------- | -| `OPENCODE_AUTO_SHARE` | boolean | 自动共享会话 | -| `OPENCODE_GIT_BASH_PATH` | string | Windows 上 Git Bash 可执行文件的路径 | -| `OPENCODE_CONFIG` | string | 配置文件路径 | -| `OPENCODE_CONFIG_DIR` | string | 配置目录的路径 | -| `OPENCODE_CONFIG_CONTENT` | string | 内联 json 配置内容 | -| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | 禁用自动更新检查 | -| `OPENCODE_DISABLE_PRUNE` | boolean | 禁用数据的修剪 | -| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | 禁用自动终端标题更新 | -| `OPENCODE_PERMISSION` | string | 内联 json 权限配置 | -| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | 禁用默认插件 | -| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | 禁用自动 LSP 服务器下载 | -| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | 启用实验模型 | -| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | 禁用自动上下文压缩 | -| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | 禁用从 `.claude` 读取(提示+技能) | -| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | 禁用读取 `~/.claude/CLAUDE.md` | -| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | 禁用加载 `.claude/skills` | -| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | 禁用从远程源获取模型 | -| `OPENCODE_FAKE_VCS` | string | 用于测试目的的伪造 VCS | -| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | 禁用文件时间检查以进行优化 | -| `OPENCODE_CLIENT` | string | 客户端标识符(默认为 `cli`) | -| `OPENCODE_ENABLE_EXA` | boolean | 启用 Exa 网络搜索工具 | -| `OPENCODE_SERVER_PASSWORD` | string | 为 `serve`/`web` 启用基本身份验证 | -| `OPENCODE_SERVER_USERNAME` | string | 覆盖基本身份验证用户名(默认 `opencode`) | -| `OPENCODE_MODELS_URL` | string | 用于获取模型配置的自定义 URL | +OpenCode 可以通过环境变量进行配置。 + +| 变量 | 类型 | 描述 | +| ------------------------------------- | ------- | --------------------------------------- | +| `OPENCODE_AUTO_SHARE` | boolean | 自动分享会话 | +| `OPENCODE_GIT_BASH_PATH` | string | Windows 上 Git Bash 可执行文件的路径 | +| `OPENCODE_CONFIG` | string | 配置文件路径 | +| `OPENCODE_CONFIG_DIR` | string | 配置目录路径 | +| `OPENCODE_CONFIG_CONTENT` | string | 内联 JSON 配置内容 | +| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | 禁用自动更新检查 | +| `OPENCODE_DISABLE_PRUNE` | boolean | 禁用旧数据清理 | +| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | 禁用自动终端标题更新 | +| `OPENCODE_PERMISSION` | string | 内联 JSON 权限配置 | +| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | 禁用默认插件 | +| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | 禁用 LSP 服务器自动下载 | +| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | 启用实验性模型 | +| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | 禁用自动上下文压缩 | +| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | 禁用读取 `.claude`(提示词 + 技能) | +| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | 禁用读取 `~/.claude/CLAUDE.md` | +| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | 禁用加载 `.claude/skills` | +| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | 禁用从远程源获取模型 | +| `OPENCODE_FAKE_VCS` | string | 用于测试目的的模拟 VCS 提供商 | +| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | 禁用文件时间检查优化 | +| `OPENCODE_CLIENT` | string | 客户端标识符(默认为 `cli`) | +| `OPENCODE_ENABLE_EXA` | boolean | 启用 Exa 网络搜索工具 | +| `OPENCODE_SERVER_PASSWORD` | string | 为 `serve`/`web` 启用基本认证 | +| `OPENCODE_SERVER_USERNAME` | string | 覆盖基本认证用户名(默认为 `opencode`) | +| `OPENCODE_MODELS_URL` | string | 自定义模型配置获取 URL | --- ### 实验性功能 -这些环境变量启用可能会更改或删除的实验性功能。 - -| 变量 | 类型 | 描述 | -| ----------------------------------------------- | ------- | ----------------------------------- | -| `OPENCODE_EXPERIMENTAL` | boolean | 启用所有实验性功能 | -| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | 启用图标发现 | -| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | 在 TUI 中禁用选择时复制 | -| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | bash 命令的默认超时(以毫秒为单位) | -| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | LLM 响应的最大输出 Token | -| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | 为整个目录启用文件观察器 | -| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | 启用 oxfmt 格式化程序 | -| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | 启用实验性 LSP 工具 | -| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | 禁用文件观察器 | -| `OPENCODE_EXPERIMENTAL_EXA` | boolean | 启用实验性 Exa 功能 | -| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | 启用实验性 LSP 类型检查 | -| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | 启用实验性 Markdown 功能 | -| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | 启用计划模式 | +这些环境变量用于启用可能会更改或移除的实验性功能。 + +| 变量 | 类型 | 描述 | +| ----------------------------------------------- | ------- | ------------------------------- | +| `OPENCODE_EXPERIMENTAL` | boolean | 启用所有实验性功能 | +| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | 启用图标发现 | +| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | 禁用 TUI 中的选中即复制 | +| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | bash 命令的默认超时时间(毫秒) | +| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | LLM 响应的最大输出 Token 数 | +| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | 启用整个目录的文件监听器 | +| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | 启用 oxfmt 格式化器 | +| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | 启用实验性 LSP 工具 | +| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | 禁用文件监听器 | +| `OPENCODE_EXPERIMENTAL_EXA` | boolean | 启用实验性 Exa 功能 | +| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | 启用实验性 LSP 类型检查 | +| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | 启用实验性 Markdown 功能 | +| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | 启用计划模式 | diff --git a/packages/web/src/content/docs/zh-cn/commands.mdx b/packages/web/src/content/docs/zh-cn/commands.mdx index 548b681b0b7a..751a9ff27b18 100644 --- a/packages/web/src/content/docs/zh-cn/commands.mdx +++ b/packages/web/src/content/docs/zh-cn/commands.mdx @@ -3,13 +3,13 @@ title: 命令 description: 为重复任务创建自定义命令。 --- -自定义命令允许您指定在 TUI 中执行该命令时要运行的提示。 +自定义命令允许你指定一个提示词,当在 TUI 中执行该命令时会运行这个提示词。 ```bash frame="none" /my-command ``` -除了 `/init`、`/undo`、`/redo`、`/share`、`/help` 等内置命令之外,还有自定义命令。 [了解更多](/docs/tui#commands)。 +自定义命令是 `/init`、`/undo`、`/redo`、`/share`、`/help` 等内置命令之外的补充。[了解更多](/docs/tui#commands)。 --- @@ -30,9 +30,9 @@ Run the full test suite with coverage report and show any failures. Focus on the failing tests and suggest fixes. ``` -frontmatter 定义命令属性。内容成为模板。 +frontmatter 定义命令属性,内容则成为模板。 -通过键入 `/` 后跟命令名称来使用该命令。 +通过输入 `/` 后跟命令名称来使用该命令。 ```bash frame="none" "/test" @@ -42,13 +42,13 @@ frontmatter 定义命令属性。内容成为模板。 ## 配置 -您可以通过 opencode 配置或通过在 `commands/` 目录中创建 markdown 文件来添加自定义命令。 +你可以通过 OpenCode 配置或在 `commands/` 目录中创建 markdown 文件来添加自定义命令。 --- ### JSON -在 opencode [配置](/docs/config) 中使用 `command` 选项: +在 OpenCode [配置](/docs/config)中使用 `command` 选项: ```json title="opencode.jsonc" {4-12} { @@ -67,7 +67,7 @@ frontmatter 定义命令属性。内容成为模板。 } ``` -现在您可以在 TUI 中运行这个命令: +现在你可以在 TUI 中运行这个命令: ```bash frame="none" /test @@ -77,10 +77,10 @@ frontmatter 定义命令属性。内容成为模板。 ### Markdown -您还可以使用 Markdown 文件定义命令。将它们放入: +你还可以使用 markdown 文件定义命令。将它们放在: - 全局:`~/.config/opencode/commands/` -- 每个项目:`.opencode/commands/` +- 项目级:`.opencode/commands/` ```markdown title="~/.config/opencode/commands/test.md" --- @@ -93,8 +93,7 @@ Run the full test suite with coverage report and show any failures. Focus on the failing tests and suggest fixes. ``` -Markdown 文件名成为命令名。例如,`test.md` 让 -你运行: +markdown 文件名即为命令名。例如,`test.md` 允许你运行: ```bash frame="none" /test @@ -102,15 +101,15 @@ Markdown 文件名成为命令名。例如,`test.md` 让 --- -## 提示配置 +## 提示词配置 -自定义命令的提示支持几个特殊的占位符和语法。 +自定义命令的提示词支持多种特殊占位符和语法。 --- ### 参数 -使用 `$ARGUMENTS` 占位符将参数提交给命令。 +使用 `$ARGUMENTS` 占位符向命令传递参数。 ```md title=".opencode/commands/component.md" --- @@ -121,20 +120,20 @@ Create a new React component named $ARGUMENTS with TypeScript support. Include proper typing and basic structure. ``` -使用参数运行命令: +带参数运行命令: ```bash frame="none" /component Button ``` -`$ARGUMENTS` 将替换为 `Button`。 +`$ARGUMENTS` 将被替换为 `Button`。 -您还可以使用位置参数访问各个参数: +你还可以使用位置参数访问各个参数: - `$1` - 第一个参数 - `$2` - 第二个参数 - `$3` - 第三个参数 -- 等等... +- 以此类推... 例如: @@ -153,19 +152,19 @@ with the following content: $3 /create-file config.json src "{ \"key\": \"value\" }" ``` -这取代了: +替换结果为: -- `$1` 与 `config.json` -- `$2` 与 `src` -- `$3` 与 `{ "key": "value" }` +- `$1` 替换为 `config.json` +- `$2` 替换为 `src` +- `$3` 替换为 `{ "key": "value" }` --- ### Shell 输出 -使用 _!`command`_ 将 [bash 命令](/docs/tui#bash-commands) 输出注入到提示符中。 +使用 _!`command`_ 将 [bash 命令](/docs/tui#bash-commands)输出注入到提示词中。 -例如,要创建分析测试覆盖率的自定义命令: +例如,创建一个分析测试覆盖率的自定义命令: ```md title=".opencode/commands/analyze-coverage.md" --- @@ -191,13 +190,13 @@ Recent git commits: Review these changes and suggest any improvements. ``` -命令在项目的根目录中运行,其输出成为提示的一部分。 +命令在项目的根目录中运行,其输出会成为提示词的一部分。 --- ### 文件引用 -使用 `@` 后跟文件名将文件包含在命令中。 +使用 `@` 后跟文件名在命令中引用文件。 ```md title=".opencode/commands/review-component.md" --- @@ -208,19 +207,19 @@ Review the component in @src/components/Button.tsx. Check for performance issues and suggest improvements. ``` -文件内容会自动包含在提示中。 +文件内容会自动包含在提示词中。 --- ## 选项 -让我们详细看看配置选项。 +让我们详细了解各配置选项。 --- -### template +### Template -`template` 选项定义执行命令时将发送到 LLM 的提示。 +`template` 选项定义执行命令时发送给 LLM 的提示词。 ```json title="opencode.json" { @@ -232,11 +231,11 @@ Check for performance issues and suggest improvements. } ``` -这是一个 **必需的** 配置选项。 +这是一个**必需的**配置选项。 --- -### description +### Description 使用 `description` 选项提供命令功能的简要描述。 @@ -250,15 +249,15 @@ Check for performance issues and suggest improvements. } ``` -当您输入命令时,这将在 TUI 中显示为描述。 +当你输入命令时,这将在 TUI 中显示为描述。 --- -### agent +### Agent -使用 `agent` 配置选择指定哪个 [Agent](/docs/agents) 应执行此命令。 -如果是 [Subagents](/docs/agents/#subagents) 该命令将默认触发子代理调用。 -要取消此行为,则将 `subtask` 设置为 `false`。 +使用 `agent` 配置可选地指定由哪个[代理](/docs/agents)执行此命令。 +如果这是一个[子代理](/docs/agents/#subagents),该命令默认会触发子代理调用。 +要禁用此行为,请将 `subtask` 设置为 `false`。 ```json title="opencode.json" { @@ -270,15 +269,15 @@ Check for performance issues and suggest improvements. } ``` -这是一个 **可选** 配置选项。如果未指定,则默认为您当前的代理。 +这是一个**可选的**配置选项。如果未指定,默认使用你当前的代理。 --- -### subtask +### Subtask -使用 `subtask` 布尔值强制命令触发 [Subagents](/docs/agents/#subagents) 调用。 -如果您希望命令不污染您的主要上下文并且将 **强制** 代理充当子代理,那么这非常有用, -即使 `mode` 在 [Agent](/docs/agents) 配置上设置为 `primary`。 +使用 `subtask` 布尔值强制命令触发[子代理](/docs/agents/#subagents)调用。 +如果你希望命令不污染主要上下文,这会很有用,它会**强制**代理作为子代理运行, +即使[代理](/docs/agents)配置中的 `mode` 设置为 `primary`。 ```json title="opencode.json" { @@ -290,11 +289,11 @@ Check for performance issues and suggest improvements. } ``` -这是一个 **可选** 配置选项。 +这是一个**可选的**配置选项。 --- -### model +### Model 使用 `model` 配置覆盖此命令的默认模型。 @@ -308,16 +307,16 @@ Check for performance issues and suggest improvements. } ``` -这是一个 **可选** 配置选项。 +这是一个**可选的**配置选项。 --- -## 内置 +## 内置命令 -opencode 包含 `/init`、`/undo`、`/redo`、`/share`、`/help` 等内置命令;[了解更多](/docs/tui#commands)。 +opencode 包含多个内置命令,如 `/init`、`/undo`、`/redo`、`/share`、`/help`;[了解更多](/docs/tui#commands)。 :::note 自定义命令可以覆盖内置命令。 ::: -如果您定义同名的自定义命令,它将覆盖内置命令。 +如果你定义了同名的自定义命令,它将覆盖内置命令。 diff --git a/packages/web/src/content/docs/zh-cn/config.mdx b/packages/web/src/content/docs/zh-cn/config.mdx index e07e4601f69e..8ed3c8fbee31 100644 --- a/packages/web/src/content/docs/zh-cn/config.mdx +++ b/packages/web/src/content/docs/zh-cn/config.mdx @@ -1,15 +1,15 @@ --- title: 配置 -description: 使用 opencode JSON 配置。 +description: 使用 OpenCode JSON 配置。 --- -您可以使用 JSON 配置文件配置 opencode。 +您可以使用 JSON 配置文件来配置 OpenCode。 --- ## 格式 -opencode 支持 **JSON** 和 **JSONC**(带注释的 JSON)格式。 +OpenCode 支持 **JSON** 和 **JSONC**(带注释的 JSON)格式。 ```jsonc title="opencode.jsonc" { @@ -25,44 +25,44 @@ opencode 支持 **JSON** 和 **JSONC**(带注释的 JSON)格式。 ## 位置 -您可以将配置放置在几个不同的位置,它们有一个不同的优先顺序。 +您可以将配置放置在不同的位置,它们具有不同的优先级顺序。 :::note -配置文件**合并在一起**,而不是替换。 +配置文件是**合并在一起**的,而不是替换。 ::: -配置文件被合并在一起,而不是被替换。以下配置位置的设置被合并。仅当密钥冲突时,后面的配置才会覆盖前面的配置。保留所有配置中的非冲突设置。 +配置文件是合并在一起的,而不是被替换。来自以下配置位置的设置会被合并。后面的配置仅在键冲突时覆盖前面的配置。所有配置中的非冲突设置都会被保留。 -例如,如果您的全局配置设置 `theme: "opencode"` 和 `autoupdate: true`,并且您的项目配置设置 `model: "anthropic/claude-sonnet-4-5"`,则最终配置将包括所有三个设置。 +例如,如果您的全局配置设置了 `theme: "opencode"` 和 `autoupdate: true`,而您的项目配置设置了 `model: "anthropic/claude-sonnet-4-5"`,则最终配置将包含所有三个设置。 --- -### 优先级 +### 优先级顺序 配置源按以下顺序加载(后面的源覆盖前面的源): -1. **Remote config** (来自 `.well-known/opencode`) - 组织默认值 -2. **Global config** (`~/.config/opencode/opencode.json`) - 用户首选项 -3. **Custom config** (`OPENCODE_CONFIG` env var) - 自定义覆盖 -4. **Project config** (项目中的 `opencode.json`) - 项目特定的设置 +1. **远程配置**(来自 `.well-known/opencode`)- 组织默认值 +2. **全局配置**(`~/.config/opencode/opencode.json`)- 用户偏好 +3. **自定义配置**(`OPENCODE_CONFIG` 环境变量)- 自定义覆盖 +4. **项目配置**(项目中的 `opencode.json`)- 项目特定设置 5. **`.opencode` 目录** - 代理、命令、插件 -6. **Inline config** (`OPENCODE_CONFIG_CONTENT` env var) - 运行时覆盖 +6. **内联配置**(`OPENCODE_CONFIG_CONTENT` 环境变量)- 运行时覆盖 这意味着项目配置可以覆盖全局默认值,全局配置可以覆盖远程组织默认值。 :::note -`.opencode` 和 `~/.config/opencode` 目录对子目录使用 **复数名称**:`agents/`、`commands/`、`modes/`、`plugins/`、`skills/`、`tools/` 和 `themes/`。为了向后兼容,还支持单数名称(例如 `agent/`)。 +`.opencode` 和 `~/.config/opencode` 目录的子目录使用**复数名称**:`agents/`、`commands/`、`modes/`、`plugins/`、`skills/`、`tools/` 和 `themes/`。为了向后兼容,也支持单数名称(例如 `agent/`)。 ::: --- ### 远程 -组织可以通过 `.well-known/opencode` 端点提供默认配置。当您向支持的提供商进行身份验证时,会自动获取该信息。 +组织可以通过 `.well-known/opencode` 端点提供默认配置。当您使用支持该功能的提供商进行身份验证时,会自动获取此配置。 -首先加载远程配置,作为基础层。所有其他配置源(全局、项目)都可以覆盖这些默认值。 +远程配置最先加载,作为基础层。所有其他配置源(全局、项目)都可以覆盖这些默认值。 -例如,如果您的组织提供默认禁用的 MCP 服务器: +例如,如果您的组织提供了默认禁用的 MCP 服务器: ```json title="Remote config from .well-known/opencode" { @@ -94,7 +94,7 @@ opencode 支持 **JSON** 和 **JSONC**(带注释的 JSON)格式。 ### 全局 -将全局 opencode 配置放在 `~/.config/opencode/opencode.json` 中。使用全局配置来实现用户范围的首选项,例如主题、提供商或按键绑定。 +将全局 OpenCode 配置放在 `~/.config/opencode/opencode.json` 中。使用全局配置来设置用户级别的偏好,例如主题、提供商或快捷键。 全局配置覆盖远程组织默认值。 @@ -102,15 +102,15 @@ opencode 支持 **JSON** 和 **JSONC**(带注释的 JSON)格式。 ### 项目级 -在项目根目录中添加 `opencode.json`。项目配置在标准配置文件中具有最高优先级 - 它覆盖全局配置和远程配置。 +在项目根目录中添加 `opencode.json`。项目配置在标准配置文件中具有最高优先级——它会覆盖全局配置和远程配置。 :::tip 将项目特定配置放在项目的根目录中。 ::: -当 opencode 启动时,它会在当前目录中查找配置文件或向上遍历到最近的 Git 目录。 +当 OpenCode 启动时,它会在当前目录中查找配置文件,或向上遍历到最近的 Git 目录。 -这也可以安全地签入 Git 并使用与全局模式相同的架构。 +该配置文件也可以安全地提交到 Git 中,并使用与全局配置相同的 Schema。 --- @@ -123,34 +123,34 @@ export OPENCODE_CONFIG=/path/to/my/custom-config.json opencode run "Hello world" ``` -自定义配置按优先顺序在全局配置和项目配置之间加载。 +自定义配置在优先级顺序中位于全局配置和项目配置之间加载。 --- ### 自定义目录 -使用 `OPENCODE_CONFIG_DIR` 环境变量指定自定义配置目录。将在该目录中搜索代理、命令、模式和插件,就像标准 `.opencode` 目录一样,并且应该遵循相同的结构。 +使用 `OPENCODE_CONFIG_DIR` 环境变量指定自定义配置目录。该目录会像标准 `.opencode` 目录一样被搜索代理、命令、模式和插件,并且应遵循相同的结构。 ```bash export OPENCODE_CONFIG_DIR=/path/to/my/config-directory opencode run "Hello world" ``` -自定义目录在全局配置和 `.opencode` 目录加载后,因此 **可以覆盖** 它们的设置。 +自定义目录在全局配置和 `.opencode` 目录之后加载,因此**可以覆盖**它们的设置。 --- -## 模式 +## Schema -配置文件具有在 [**`opencode.ai/config.json`**](https://opencode.ai/config.json) 中定义的架构。 +配置文件具有在 [**`opencode.ai/config.json`**](https://opencode.ai/config.json) 中定义的 Schema。 -您的编辑器应该能够根据架构进行验证和自动完成。 +您的编辑器应该能够基于该 Schema 进行验证和自动补全。 --- ### TUI -您可以通过 `tui` 选项配置特定于 TUI 的设置。 +您可以通过 `tui` 选项配置 TUI 相关设置。 ```json title="opencode.json" { @@ -167,17 +167,17 @@ opencode run "Hello world" 可用选项: -- `scroll_acceleration.enabled` - 启用 macOS 风格的滚动加速。 **优先于 `scroll_speed`。** -- `scroll_speed` - 自定义滚动速度倍增(默认值:`3`,最小值:`1`)。如果 `scroll_acceleration.enabled` 是 `true`,则忽略。 -- `diff_style` - 控制差异渲染。 `"auto"` 适应终端宽度,`"stacked"` 始终显示单列。 +- `scroll_acceleration.enabled` - 启用 macOS 风格的滚动加速。**优先于 `scroll_speed`。** +- `scroll_speed` - 自定义滚动速度倍率(默认值:`3`,最小值:`1`)。如果 `scroll_acceleration.enabled` 为 `true`,则忽略此选项。 +- `diff_style` - 控制差异渲染方式。`"auto"` 根据终端宽度自适应,`"stacked"` 始终显示单列。 -[在此处了解有关使用 TUI 的更多信息](/docs/tui)。 +[在此了解更多关于 TUI 的信息](/docs/tui)。 --- ### 服务器 -您可以通过 `opencode serve` 选项为 `opencode web` 和 `server` 命令配置服务器设置。 +您可以通过 `server` 选项为 `opencode serve` 和 `opencode web` 命令配置服务器设置。 ```json title="opencode.json" { @@ -195,12 +195,12 @@ opencode run "Hello world" 可用选项: - `port` - 监听端口。 -- `hostname` - 监听的主机名。当 `mdns` 启用且未设置主机名时,默认为 `0.0.0.0`。 -- `mdns` - 启用 mDNS 服务发现。这允许网络上的其他设备发现您的 opencode 服务器。 -- `mdnsDomain` - mDNS 服务的自定义域名。默认为 `opencode.local`。对于在同一个网络上运行多个实例很有帮助。 -- `cors` - 从基于浏览器的客户端使用 HTTP 服务器时允许 CORS 的其他来源。值必须是完整来源(协议+主机+可选端口),例如 `https://app.example.com`。 +- `hostname` - 监听主机名。当 `mdns` 启用且未设置主机名时,默认为 `0.0.0.0`。 +- `mdns` - 启用 mDNS 服务发现。这允许网络上的其他设备发现您的 OpenCode 服务器。 +- `mdnsDomain` - mDNS 服务的自定义域名。默认为 `opencode.local`。适用于在同一网络上运行多个实例的场景。 +- `cors` - 从基于浏览器的客户端使用 HTTP 服务器时允许 CORS 的额外来源。值必须是完整的来源(协议 + 主机 + 可选端口),例如 `https://app.example.com`。 -[在此处了解有关服务器的更多信息](/docs/server)。 +[在此了解更多关于服务器的信息](/docs/server)。 --- @@ -218,13 +218,13 @@ opencode run "Hello world" } ``` -[在此处了解有关工具的更多信息](/docs/tools)。 +[在此了解更多关于工具的信息](/docs/tools)。 --- ### 模型 -您可以通过 `provider`、`model` 和 `small_model` 选项来配置要在 opencode 配置中使用的提供商和模型。 +您可以通过 `provider`、`model` 和 `small_model` 选项在 OpenCode 配置中设置要使用的提供商和模型。 ```json title="opencode.json" { @@ -235,7 +235,7 @@ opencode run "Hello world" } ``` -`small_model` 选项为标题生成等轻量级任务配置单独的模型。默认情况下,如果您的提供商可以提供更便宜的模型,opencode 会尝试使用更便宜的模型,否则它会退回到您的主模型。 +`small_model` 选项为标题生成等轻量级任务配置单独的模型。默认情况下,如果您的提供商有更便宜的模型可用,OpenCode 会尝试使用该模型,否则会回退到您的主模型。 提供商选项可以包括 `timeout` 和 `setCacheKey`: @@ -253,16 +253,16 @@ opencode run "Hello world" } ``` -- `timeout` - 请求超时以毫秒为单位(默认值:300000)。设置为 `false` 以禁用。 -- `setCacheKey` - 确保始终为指定的提供商设置缓存键。 +- `timeout` - 请求超时时间,单位为毫秒(默认值:300000)。设置为 `false` 可禁用超时。 +- `setCacheKey` - 确保始终为指定提供商设置缓存键。 -您还可以配置 [本地模型](/docs/models#local)。[了解更多](/docs/models)。 +您还可以配置[本地模型](/docs/models#local)。[了解更多](/docs/models)。 --- -#### 特定于提供商的选项 +#### 提供商特定选项 -有些提供商支持除通用 `timeout` 和 `apiKey` 之外的其他配置选项。 +一些提供商支持除通用 `timeout` 和 `apiKey` 设置之外的额外配置选项。 ##### Amazon Bedrock @@ -283,21 +283,21 @@ Amazon Bedrock 支持 AWS 特定配置: } ``` -- `region` - Bedrock 的 AWS 区域(默认为 `AWS_REGION` env var 或 `us-east-1`) -- `profile` - 来自 `~/.aws/credentials` 的 AWS 命名配置文件(默认为 `AWS_PROFILE` env var) -- `endpoint` - VPC 终端节点的自定义节点 URL。这是使用 AWS 特定术语的通用 `baseURL` 选项的别名。如果两者都指定,`endpoint` 优先。 +- `region` - Bedrock 的 AWS 区域(默认为 `AWS_REGION` 环境变量或 `us-east-1`) +- `profile` - 来自 `~/.aws/credentials` 的 AWS 命名配置文件(默认为 `AWS_PROFILE` 环境变量) +- `endpoint` - VPC 端点的自定义端点 URL。这是通用 `baseURL` 选项使用 AWS 特定术语的别名。如果两者都指定,`endpoint` 优先。 :::note -Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置文件的身份验证。详情请参见 [身份验证优先级](/docs/providers#authentication-precedence)。 +Bearer Token(`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`)优先于基于配置文件的身份验证。详情请参见[身份验证优先级](/docs/providers#authentication-precedence)。 ::: -[了解有关 Amazon Bedrock 配置的更多信息](/docs/providers#amazon-bedrock)。 +[了解更多关于 Amazon Bedrock 配置的信息](/docs/providers#amazon-bedrock)。 --- ### 主题 -您可以通过 opencode 配置中的 `theme` 选项配置要使用的主题。 +您可以通过 OpenCode 配置中的 `theme` 选项设置要使用的主题。 ```json title="opencode.json" { @@ -306,7 +306,7 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -[在这里了解更多](/docs/themes)。 +[在此了解更多](/docs/themes)。 --- @@ -332,13 +332,13 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -您还可以使用 `~/.config/opencode/agents/` 或 `.opencode/agents/` 中的 markdown 文件定义代理。 [在这里了解更多](/docs/agents)。 +您还可以使用 `~/.config/opencode/agents/` 或 `.opencode/agents/` 中的 Markdown 文件定义代理。[在此了解更多](/docs/agents)。 --- ### 默认代理 -您可以使用 `default_agent` 选项设置默认代理。当没有明确指定时,这将确定使用哪个代理。 +您可以使用 `default_agent` 选项设置默认代理。当未明确指定代理时,将使用该默认代理。 ```json title="opencode.json" { @@ -347,15 +347,15 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -默认代理必须是 Primary 代理(不是 Subagent)。这可以是内置代理(如 `"build"` 或 `"plan"`),也可以是您定义的 [Custom Agent](/docs/agents)。如果指定的代理不存在或者是子代理,opencode 将回退到 `"build"` 并发出警告。 +默认代理必须是主代理(不能是子代理)。可以是内置代理(如 `"build"` 或 `"plan"`),也可以是您定义的[自定义代理](/docs/agents)。如果指定的代理不存在或是子代理,OpenCode 将回退到 `"build"` 并发出警告。 -此设置适用于所有界面:TUI、CLI (`opencode run`)、桌面应用程序和 GitHub Action。 +此设置适用于所有界面:TUI、CLI(`opencode run`)、桌面应用和 GitHub Action。 --- ### 分享 -您可以通过 `share` 选项配置 [分享](/docs/share) 功能。 +您可以通过 `share` 选项配置[分享](/docs/share)功能。 ```json title="opencode.json" { @@ -364,13 +364,13 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -这需要: +该选项接受: -- `"manual"` - 允许通过命令手动共享(默认) -- `"auto"` - 自动分享新对话 -- `"disabled"` - 完全禁用共享 +- `"manual"` - 允许通过命令手动分享(默认) +- `"auto"` - 自动分享新会话 +- `"disabled"` - 完全禁用分享 -默认情况下,共享设置为手动模式,您需要使用 `/share` 命令显式共享对话。 +默认情况下,分享设置为手动模式,您需要使用 `/share` 命令显式分享会话。 --- @@ -396,13 +396,13 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -您还可以使用 `~/.config/opencode/commands/` 或 `.opencode/commands/` 中的 Markdown 文件定义命令。 [在这里了解更多](/docs/commands)。 +您还可以使用 `~/.config/opencode/commands/` 或 `.opencode/commands/` 中的 Markdown 文件定义命令。[在此了解更多](/docs/commands)。 --- ### 快捷键 -您可以通过 `keybinds` 选项自定义您的按键绑定。 +您可以通过 `keybinds` 选项自定义快捷键。 ```json title="opencode.json" { @@ -411,13 +411,13 @@ Bearer Tokens (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 优先于基于配置 } ``` -[在这里了解更多](/docs/keybinds)。 +[在此了解更多](/docs/keybinds)。 --- ### 自动更新 -opencode 将在启动时自动下载任何新的更新。您可以使用 `autoupdate` 选项禁用此功能。 +OpenCode 启动时会自动下载新版本。您可以使用 `autoupdate` 选项禁用此功能。 ```json title="opencode.json" { @@ -426,8 +426,8 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -如果您不想更新但希望在新版本可用时收到通知,则需将 `autoupdate` 设置为 `"notify"`。 -请注意,这仅在未使用 Homebrew 等包管理器安装时才有效。 +如果您不想自动更新但希望在新版本可用时收到通知,可将 `autoupdate` 设置为 `"notify"`。 +请注意,此功能仅在未通过 Homebrew 等包管理器安装时有效。 --- @@ -453,15 +453,15 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -[在这里了解有关格式化程序的更多信息](/docs/formatters)。 +[在此了解更多关于格式化程序的信息](/docs/formatters)。 --- ### 权限 -默认情况下,opencode **允许所有操作**,无需明确批准。您可以使用 `permission` 选项更改此设置。 +默认情况下,OpenCode **允许所有操作**,无需明确批准。您可以使用 `permission` 选项更改此行为。 -例如,要确保 `edit` 和 `bash` 工具需要用户批准: +例如,要让 `edit` 和 `bash` 工具需要用户确认: ```json title="opencode.json" { @@ -473,7 +473,7 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -[在此处了解有关权限的更多信息](/docs/permissions)。 +[在此了解更多关于权限的信息](/docs/permissions)。 --- @@ -486,19 +486,21 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup "$schema": "https://opencode.ai/config.json", "compaction": { "auto": true, - "prune": true + "prune": true, + "reserved": 10000 } } ``` - `auto` - 当上下文已满时自动压缩会话(默认值:`true`)。 -- `prune` - 删除旧工具输出以保存 Tokens(默认值:`true`)。 +- `prune` - 删除旧的工具输出以节省 Token(默认值:`true`)。 +- `reserved` - 压缩时的 Token 缓冲区。保留足够的窗口以避免压缩过程中溢出。 --- -### 观察器 +### 文件监视器 -您可以通过 `watcher` 选项配置文件观察器忽略模式。 +您可以通过 `watcher` 选项配置文件监视器的忽略模式。 ```json title="opencode.json" { @@ -509,7 +511,7 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -模式遵循 glob 语法。使用它可以从文件监视中排除嘈杂的目录。 +模式遵循 glob 语法。使用此选项可以从文件监视中排除频繁变动的目录。 --- @@ -524,13 +526,13 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -[在这里了解更多](/docs/mcp-servers)。 +[在此了解更多](/docs/mcp-servers)。 --- ### 插件 -[Plugins](/docs/plugins) 使用自定义工具、挂钩和集成扩展 opencode。 +[插件](/docs/plugins)通过自定义工具、钩子和集成来扩展 OpenCode。 将插件文件放置在 `.opencode/plugins/` 或 `~/.config/opencode/plugins/` 中。您还可以通过 `plugin` 选项从 npm 加载插件。 @@ -541,13 +543,13 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -[在这里了解更多](/docs/plugins)。 +[在此了解更多](/docs/plugins)。 --- ### 指令 -您可以通过 `instructions` 选项配置您正在使用的模型的说明。 +您可以通过 `instructions` 选项为所使用的模型配置指令。 ```json title="opencode.json" { @@ -556,13 +558,13 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -这需要指令文件的路径和 glob 模式数组。 [在此处了解有关规则的更多信息](/docs/rules)。 +该选项接受指令文件路径和 glob 模式的数组。[在此了解更多关于规则的信息](/docs/rules)。 --- ### 禁用提供商 -您可以通过 `disabled_providers` 选项禁用自动加载的提供商。当您想要阻止加载某些提供商(即使其凭据可用)时,这非常有用。 +您可以通过 `disabled_providers` 选项禁用自动加载的提供商。当您希望阻止某些提供商被加载(即使其凭据可用)时,此选项非常有用。 ```json title="opencode.json" { @@ -575,17 +577,17 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup `disabled_providers` 优先于 `enabled_providers`。 ::: -`disabled_providers` 选项接受提供商 ID 数组。当提供商被禁用时: +`disabled_providers` 选项接受提供商 ID 的数组。当某个提供商被禁用时: -- 即使设置了环境变量也不会加载。 -- 即使通过 `/connect` 命令配置 API 密钥,也不会加载它。 -- 提供商的模型不会出现在模型选择列表中。 +- 即使设置了环境变量,也不会被加载。 +- 即使通过 `/connect` 命令配置了 API 密钥,也不会被加载。 +- 该提供商的模型不会出现在模型选择列表中。 --- ### 启用提供商 -您可以通过 `enabled_providers` 选项指定允许的提供商列表。设置后,仅启用指定的提供商,所有其他提供商将被忽略。 +您可以通过 `enabled_providers` 选项指定允许使用的提供商白名单。设置后,仅启用指定的提供商,所有其他提供商将被忽略。 ```json title="opencode.json" { @@ -594,19 +596,19 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -当您想要限制 opencode 仅使用特定的提供商而不是逐一禁用它们时,这非常有用。 +当您希望限制 OpenCode 仅使用特定提供商,而不是逐一禁用其他提供商时,此选项非常有用。 :::note `disabled_providers` 优先于 `enabled_providers`。 ::: -如果提供商同时出现在 `enabled_providers` 和 `disabled_providers` 中,则 `disabled_providers` 优先以保持一致性。 +如果某个提供商同时出现在 `enabled_providers` 和 `disabled_providers` 中,为了向后兼容,`disabled_providers` 优先。 --- ### 实验性功能 -`experimental` 键包含正在积极开发的选项。 +`experimental` 键包含正在积极开发中的选项。 ```json title="opencode.json" { @@ -616,7 +618,7 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup ``` :::caution -实验选项不稳定。它们可能会更改或被删除,恕不另行通知。 +实验性选项不稳定。它们可能会在不另行通知的情况下被更改或移除。 ::: --- @@ -629,7 +631,7 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup ### 环境变量 -使用 `{env:VARIABLE_NAME}` 替换环境变量: +使用 `{env:VARIABLE_NAME}` 来替换环境变量: ```json title="opencode.json" { @@ -646,13 +648,13 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup } ``` -如果未设置环境变量,它将被替换为空字符串。 +如果环境变量未设置,它将被替换为空字符串。 --- ### 文件 -使用 `{file:path/to/file}` 替换文件的内容: +使用 `{file:path/to/file}` 来替换文件内容: ```json title="opencode.json" { @@ -670,11 +672,11 @@ opencode 将在启动时自动下载任何新的更新。您可以使用 `autoup 文件路径可以是: -- 相对于配置文件目录 -- 或者以 `/` 或 `~` 开头的绝对路径 +- 相对于配置文件所在目录的路径 +- 以 `/` 或 `~` 开头的绝对路径 -这些对于: +这些功能适用于: - 将 API 密钥等敏感数据保存在单独的文件中。 -- 包含大型指令文件,而不会弄乱您的配置。 -- 跨多个配置文件共享通用配置片段。 +- 引入大型指令文件而不会使配置变得杂乱。 +- 在多个配置文件之间共享通用配置片段。 diff --git a/packages/web/src/content/docs/zh-cn/custom-tools.mdx b/packages/web/src/content/docs/zh-cn/custom-tools.mdx index 211df7c0146c..81a90a2bcb57 100644 --- a/packages/web/src/content/docs/zh-cn/custom-tools.mdx +++ b/packages/web/src/content/docs/zh-cn/custom-tools.mdx @@ -1,30 +1,30 @@ --- title: 自定义工具 -description: 创建 LLM 可以在 opencode 中调用的工具。 +description: 创建 LLM 可在 opencode 中调用的工具。 --- -自定义工具是您创建的函数,LLM 可以在对话期间调用。它们与 opencode 的 [Built-in Tools](/docs/tools) 一起工作,例如 `read`、`write` 和 `bash`。 +自定义工具是你创建的函数,LLM 可以在对话过程中调用它们。它们与 opencode 的[内置工具](/docs/tools)(如 `read`、`write` 和 `bash`)协同工作。 --- ## 创建工具 -工具定义为 **TypeScript** 或 **JavaScript** 文件。但是,工具定义调用可以使用 **任何语言** 编写的脚本 - TypeScript 或 JavaScript 仅用于工具定义本身。 +工具以 **TypeScript** 或 **JavaScript** 文件的形式定义。不过,工具定义可以调用**任何语言**编写的脚本——TypeScript 或 JavaScript 仅用于工具定义本身。 --- ### 位置 -它们可以定义为: +工具可以在以下位置定义: -- 通过将它们放在项目的 `.opencode/tools/` 目录中来本地进行。 -- 或者在全局范围内,将它们放在 `~/.config/opencode/tools/` 中。 +- 本地定义:将工具文件放在项目的 `.opencode/tools/` 目录中。 +- 全局定义:将工具文件放在 `~/.config/opencode/tools/` 中。 --- ### 结构 -创建工具最简单的方法是使用 `tool()` 帮助程序,它提供类型安全和验证。 +创建工具最简单的方式是使用 `tool()` 辅助函数,它提供类型安全和参数校验。 ```ts title=".opencode/tools/database.ts" {1} import { tool } from "@opencode-ai/plugin" @@ -41,13 +41,13 @@ export default tool({ }) ``` -**文件名** 成为 **工具名称**。以上创建了一个 `database` 工具。 +**文件名**即为**工具名称**。上面的示例创建了一个名为 `database` 的工具。 --- -#### 每个文件多个工具 +#### 单文件多工具 -您还可以从单个文件导出多个工具。每个导出都会成为 **一个单独的工具**,名称为 **`_`**: +你也可以从单个文件中导出多个工具。每个导出都会成为**一个独立的工具**,命名格式为 **`_`**: ```ts title=".opencode/tools/math.ts" import { tool } from "@opencode-ai/plugin" @@ -75,13 +75,13 @@ export const multiply = tool({ }) ``` -这将创建两个工具:`math_add` 和 `math_multiply`。 +这会创建两个工具:`math_add` 和 `math_multiply`。 --- ### 参数 -您可以使用 `tool.schema`(即 [Zod](https://zod.dev))来定义参数类型。 +你可以使用 `tool.schema`(即 [Zod](https://zod.dev))来定义参数类型。 ```ts "tool.schema" args: { @@ -89,7 +89,7 @@ args: { } ``` -您还可以直接导入 [Zod](https://zod.dev) 并返回一个普通对象: +你也可以直接导入 [Zod](https://zod.dev) 并返回一个普通对象: ```ts {6} import { z } from "zod" @@ -110,7 +110,7 @@ export default { ### 上下文 -工具接收有关当前会话的上下文: +工具会接收当前会话的上下文信息: ```ts title=".opencode/tools/project.ts" {8} import { tool } from "@opencode-ai/plugin" @@ -126,18 +126,18 @@ export default tool({ }) ``` -使用 `context.directory` 作为会话工作目录。 -使用 `context.worktree` 作为 git 工作树根。 +使用 `context.directory` 获取会话的工作目录。 +使用 `context.worktree` 获取 git worktree 根目录。 --- ## 示例 -### 使用 Python 编写工具 +### 用 Python 编写工具 -您可以使用任何您想要的语言编写工具。下面是一个使用 Python 将两个数字相加的示例。 +你可以使用任何语言编写工具。以下示例展示了如何用 Python 实现两数相加。 -首先,使用创建 Python 脚本的工具: +首先,创建一个 Python 脚本作为工具: ```python title=".opencode/tools/add.py" import sys @@ -147,7 +147,7 @@ b = int(sys.argv[2]) print(a + b) ``` -然后创建调用它的工具定义: +然后创建调用该脚本的工具定义: ```ts title=".opencode/tools/python-add.ts" {10} import { tool } from "@opencode-ai/plugin" @@ -167,4 +167,4 @@ export default tool({ }) ``` -这里我们使用 [`Bun.$`](https://bun.com/docs/runtime/shell) 实用程序来运行 Python 脚本。 +这里我们使用 [`Bun.$`](https://bun.com/docs/runtime/shell) 工具函数来运行 Python 脚本。 diff --git a/packages/web/src/content/docs/zh-cn/ecosystem.mdx b/packages/web/src/content/docs/zh-cn/ecosystem.mdx index 25689beb11ce..c77cc0542bc1 100644 --- a/packages/web/src/content/docs/zh-cn/ecosystem.mdx +++ b/packages/web/src/content/docs/zh-cn/ecosystem.mdx @@ -1,52 +1,52 @@ --- title: 生态系统 -description: 使用 opencode 构建的项目和集成。 +description: 基于 OpenCode 构建的项目与集成。 --- -基于 opencode 的社区项目集合。 +基于 OpenCode 构建的社区项目合集。 :::note -想要将您的 opencode 相关项目添加到此列表中吗?提交 PR。 +想将您的 OpenCode 相关项目添加到此列表中?欢迎提交 PR。 ::: -您还可以查看 [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) 和 [opencode.cafe](https://opencode.cafe),这是一个聚合生态系统和社区的社区。 +您还可以查看 [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) 和 [opencode.cafe](https://opencode.cafe),这是一个聚合生态系统与社区资源的社区。 --- ## 插件 -| 名称 | 描述 | -| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| [opencode-daytona](https://github.com/jamesmurdza/daytona/blob/main/guides/typescript/opencode/README.md) | 在隔离的 Daytona 沙箱中自动运行 opencode 会话,并使用 git 同步和实时预览 | -| [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) | 自动注入 Helicone 会话标头以进行请求包 | -| [opencode-type-inject](https://github.com/nick-vi/opencode-type-inject) | 使用查找工具将 TypeScript/Svelte 类型自动注入到文件读取中 | -| [opencode-openai-codex-auth](https://github.com/numman-ali/opencode-openai-codex-auth) | 使用您的 ChatGPT Plus 或 Pro 订阅而不是 API 积分 | -| [opencode-gemini-auth](https://github.com/jenslys/opencode-gemini-auth) | 使用您现有的 Gemini 计划而不是 API 密钥 | -| [opencode-antigravity-auth](https://github.com/NoeFabris/opencode-antigravity-auth) | 使用 Antigravity 的免费模型代替 API | -| [opencode-devcontainers](https://github.com/athal7/opencode-devcontainers) | 具有浅克隆和自动分配端口的多分支开发容器隔离 | -| [opencode-google-antigravity-auth](https://github.com/shekohex/opencode-google-antigravity-auth) | Google Antigravity OAuth 插件,支持 Google 搜索和更强大的 API 处理 | -| [opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) | 通过修剪过时的工具输出来优化 token 使用 | -| [opencode-websearch-cited](https://github.com/ghoulr/opencode-websearch-cited.git) | 为具有 Google 接地风格的受支持增加本机网络搜索支持 | -| [opencode-pty](https://github.com/shekohex/opencode-pty.git) | 使 AI 代理能够在 PTY 中运行后台进程,末端发送其交互输入。 | -| [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy) | 非交互式 shell 命令指令 - 防止依赖 TTY 的操作挂起 | -| [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | 使用 Wakatime 跟踪 opencode 使用情况 | -| [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | 清理 LLM 生成的 markdown 表 | -| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 使用 Morph Fast Apply API 和取消编辑标记将代码编辑速度提高 10 倍 | -| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | 后台代理、预构建的 LSP/AST/MCP 工具、精选代理、兼容 Claude Code | -| [opencode-notificator](https://github.com/panta82/opencode-notificator) | opencode 会话的桌面通知和声音警报 | -| [opencode-notifier](https://github.com/mohak34/opencode-notifier) | 针对权限、完成和错误事件的桌面通知和声音警报 | -| [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | 基于 opencode 上下文的 AI 支持的自动 Zellij 会话命名 | -| [opencode-skillful](https://github.com/zenobi-us/opencode-skillful) | 允许 opencode 代理通过技能发现和注入失败延迟加载提示 | -| [opencode-supermemory](https://github.com/supermemoryai/opencode-supermemory) | 使用 Supermemory 跨会话持久内存 | -| [@plannotator/opencode](https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin) | 具有视觉注释和私人/离线共享的交互式计划审查 | -| [@openspoon/subtask2](https://github.com/spoons-and-mirrors/subtask2) | 将 opencode/命令扩展为具有精细流程控制的强大编排系统 | -| [opencode-scheduler](https://github.com/different-ai/opencode-scheduler) | 使用带 cron 语法的 launchd (Mac) 或 systemd (Linux) 安排重复作业 | -| [micode](https://github.com/vtemian/micode) | 塑造头脑风暴 → 计划 → 实施具有会议连续性的工作流程 | -| [octto](https://github.com/vtemian/octto) | 用于通过多问题形式进行 AI 头脑风暴的交互式浏览器 UI | -| [opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents) | 具有异步委托和上下文持久性的 Claude Code 风格后台代理 | -| [opencode-notify](https://github.com/kdcokenny/opencode-notify) | opencode 的本机操作系统通知 – 了解任务何时完成 | -| [opencode-workspace](https://github.com/kdcokenny/opencode-workspace) | 一堆多代理编排工具 – 16个,组件一次安装 | -| [opencode-worktree](https://github.com/kdcokenny/opencode-worktree) | opencode 的零难度 git 工作树 | +| 名称 | 描述 | +| --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| [opencode-daytona](https://github.com/jamesmurdza/daytona/blob/main/guides/typescript/opencode/README.md) | 在隔离的 Daytona 沙箱中自动运行 OpenCode 会话,支持 git 同步和实时预览 | +| [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) | 自动注入 Helicone 会话头信息,用于请求分组 | +| [opencode-type-inject](https://github.com/nick-vi/opencode-type-inject) | 通过查找工具自动将 TypeScript/Svelte 类型注入到文件读取中 | +| [opencode-openai-codex-auth](https://github.com/numman-ali/opencode-openai-codex-auth) | 使用您的 ChatGPT Plus/Pro 订阅替代 API 额度 | +| [opencode-gemini-auth](https://github.com/jenslys/opencode-gemini-auth) | 使用您现有的 Gemini 套餐替代 API 计费 | +| [opencode-antigravity-auth](https://github.com/NoeFabris/opencode-antigravity-auth) | 使用 Antigravity 的免费模型替代 API 计费 | +| [opencode-devcontainers](https://github.com/athal7/opencode-devcontainers) | 多分支开发容器隔离,支持浅克隆和自动分配端口 | +| [opencode-google-antigravity-auth](https://github.com/shekohex/opencode-google-antigravity-auth) | Google Antigravity OAuth 插件,支持 Google 搜索及更强健的 API 处理 | +| [opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) | 通过修剪过时的工具输出来优化 Token 使用 | +| [opencode-websearch-cited](https://github.com/ghoulr/opencode-websearch-cited.git) | 为受支持的提供商添加原生网页搜索支持,采用 Google grounded 风格 | +| [opencode-pty](https://github.com/shekohex/opencode-pty.git) | 使 AI 代理能够在 PTY 中运行后台进程,并向其发送交互式输入 | +| [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy) | 非交互式 shell 命令指令——防止依赖 TTY 的操作导致挂起 | +| [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | 使用 Wakatime 追踪 OpenCode 的使用情况 | +| [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | 清理 LLM 生成的 Markdown 表格 | +| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 通过 Morph Fast Apply API 和惰性编辑标记实现 10 倍更快的代码编辑 | +| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | 后台代理、预构建的 LSP/AST/MCP 工具、精选代理,兼容 Claude Code | +| [opencode-notificator](https://github.com/panta82/opencode-notificator) | OpenCode 会话的桌面通知和声音提醒 | +| [opencode-notifier](https://github.com/mohak34/opencode-notifier) | 针对权限请求、任务完成和错误事件的桌面通知与声音提醒 | +| [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | 基于 OpenCode 上下文的 AI 驱动自动 Zellij 会话命名 | +| [opencode-skillful](https://github.com/zenobi-us/opencode-skillful) | 允许 OpenCode 代理通过技能发现和注入按需延迟加载提示词 | +| [opencode-supermemory](https://github.com/supermemoryai/opencode-supermemory) | 使用 Supermemory 实现跨会话的持久记忆 | +| [@plannotator/opencode](https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin) | 支持可视化标注和私有/离线分享的交互式计划审查 | +| [@openspoon/subtask2](https://github.com/spoons-and-mirrors/subtask2) | 将 OpenCode /commands 扩展为具有精细流程控制的强大编排系统 | +| [opencode-scheduler](https://github.com/different-ai/opencode-scheduler) | 使用 cron 语法通过 launchd (Mac) 或 systemd (Linux) 调度周期性任务 | +| [micode](https://github.com/vtemian/micode) | 结构化的头脑风暴 → 计划 → 实现工作流,支持会话连续性 | +| [octto](https://github.com/vtemian/octto) | 用于 AI 头脑风暴的交互式浏览器 UI,支持多问题表单 | +| [opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents) | Claude Code 风格的后台代理,支持异步委托和上下文持久化 | +| [opencode-notify](https://github.com/kdcokenny/opencode-notify) | OpenCode 的原生操作系统通知——随时了解任务完成情况 | +| [opencode-workspace](https://github.com/kdcokenny/opencode-workspace) | 捆绑式多代理编排套件——16 个组件,一次安装 | +| [opencode-worktree](https://github.com/kdcokenny/opencode-worktree) | OpenCode 的零摩擦 git worktree 管理 | --- @@ -54,17 +54,17 @@ description: 使用 opencode 构建的项目和集成。 | 名称 | 描述 | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- | -| [kimaki](https://github.com/remorses/kimaki) | 用于控制 opencode 会话的 Discord 机器人,基于 SDK 构建 | -| [opencode.nvim](https://github.com/NickvanDyke/opencode.nvim) | Neovim 插件,用于编辑器采集提示,基于 API 构建 | -| [portal](https://github.com/hosenur/portal) | 通过 Tailscale/VPN 实现 opencode 的移动优先 Web UI | -| [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | 用于构建 opencode 插件的模板 | -| [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | Neovim opencode 前端 - 基于终端的 AI 编码代理 | -| [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK 提供商,用于通过 @opencode-ai/sdk 使用 opencode | -| [OpenChamber](https://github.com/btriapitsyn/openchamber) | opencode 的 Web/桌面应用程序和 VS Code 扩展 | -| [OpenCode-Obsidian](https://github.com/mtymek/opencode-obsidian) | 在 Obsidian 的 UI 中嵌入 opencode 的 Obsidian 插件 | -| [OpenWork](https://github.com/different-ai/openwork) | Claude Cowork 的替代开源方案,由 opencode 提供支持 | -| [ocx](https://github.com/kdcokenny/ocx) | opencode 扩展管理器具有可移植、隔离的配置文件。 | -| [CodeNomad](https://github.com/NeuralNomadsAI/CodeNomad) | opencode 的桌面、Web、移动和远程客户端应用程序 | +| [kimaki](https://github.com/remorses/kimaki) | 用于控制 OpenCode 会话的 Discord 机器人,基于 SDK 构建 | +| [opencode.nvim](https://github.com/NickvanDyke/opencode.nvim) | Neovim 插件,提供编辑器感知的提示词,基于 API 构建 | +| [portal](https://github.com/hosenur/portal) | 通过 Tailscale/VPN 使用的移动优先 OpenCode Web UI | +| [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | 用于构建 OpenCode 插件的模板 | +| [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | OpenCode 的 Neovim 前端——基于终端的 AI 编码代理 | +| [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK 提供商,用于通过 @opencode-ai/sdk 使用 OpenCode | +| [OpenChamber](https://github.com/btriapitsyn/openchamber) | OpenCode 的 Web / 桌面应用和 VS Code 扩展 | +| [OpenCode-Obsidian](https://github.com/mtymek/opencode-obsidian) | 将 OpenCode 嵌入 Obsidian UI 的 Obsidian 插件 | +| [OpenWork](https://github.com/different-ai/openwork) | Claude Cowork 的开源替代方案,由 OpenCode 驱动 | +| [ocx](https://github.com/kdcokenny/ocx) | OpenCode 扩展管理器,支持可移植的隔离配置 | +| [CodeNomad](https://github.com/NeuralNomadsAI/CodeNomad) | OpenCode 的桌面、Web、移动和远程客户端应用 | --- @@ -72,5 +72,5 @@ description: 使用 opencode 构建的项目和集成。 | 名称 | 描述 | | ----------------------------------------------------------------- | ---------------------------------------- | -| [Agentic](https://github.com/Cluster444/agentic) | 用于格式化开发的 Agentic AI 代理和命令 | -| [opencode-agents](https://github.com/darrenhinde/opencode-agents) | 用于增强工作流程的配置、提示、代理和插件 | +| [Agentic](https://github.com/Cluster444/agentic) | 用于结构化开发的模块化 AI 代理和命令 | +| [opencode-agents](https://github.com/darrenhinde/opencode-agents) | 用于增强工作流的配置、提示词、代理和插件 | diff --git a/packages/web/src/content/docs/zh-cn/enterprise.mdx b/packages/web/src/content/docs/zh-cn/enterprise.mdx index d4ababa2d6d7..83e823e93aec 100644 --- a/packages/web/src/content/docs/zh-cn/enterprise.mdx +++ b/packages/web/src/content/docs/zh-cn/enterprise.mdx @@ -1,47 +1,47 @@ --- title: 企业版 -description: 在您的组织中安全地使用 opencode。 +description: 在您的组织中安全地使用 OpenCode。 --- import config from "../../../../config.mjs" export const email = `mailto:${config.email}` -opencode 企业版适用于希望确保其代码和数据永远不会离开其基础设施的组织。它可以通过使用与 SSO 和内部 AI 网关集成的集中方式配置来实现此目的。 +OpenCode 企业版面向希望确保代码和数据始终留在自有基础设施内的组织。它通过集中式配置与您的 SSO 和内部 AI 网关集成来实现这一目标。 :::note -opencode 不存储您的任何代码或上下文数据。 +OpenCode 不会存储您的任何代码或上下文数据。 ::: -要开始使用 opencode 企业版: +开始使用 OpenCode 企业版: -1. 与您的团队进行内部试用。 -2. **联系我们** 讨论定价和实施选项。 +1. 在团队内部进行试用。 +2. **联系我们**,讨论定价和实施方案。 --- ## 试用 -opencode 是开源的,不存储您的任何代码或上下文数据,因此您的开发人员只需 [开始吧](/docs/) 并进行试用。 +OpenCode 是开源的,不会存储您的任何代码或上下文数据,因此您的开发人员可以直接[开始使用](/docs/)并进行试用。 --- ### 数据处理 -**opencode 不会存储您的代码或上下文数据。** 所有处理都在本地进行或通过直接 API 调用您的 AI 提供商。 +**OpenCode 不会存储您的代码或上下文数据。**所有处理均在本地完成,或通过直接 API 调用发送至您的 AI 提供商。 -这意味着只要您使用您信任的提供商或内部 AI 网关,您就可以安全使用 opencode。 +这意味着,只要您使用的是信任的提供商或内部 AI 网关,就可以安全地使用 OpenCode。 -这里唯一需要注意的是可选的 `/share` 功能。 +唯一需要注意的是可选的 `/share` 功能。 --- #### 分享对话 -如果用户启用 `/share` 功能,对话和关联的数据将被发送到我们用于在 opencode.ai 上托管这些共享页面的服务。 +如果用户启用了 `/share` 功能,对话及其关联数据将被发送到我们用于在 opencode.ai 上托管共享页面的服务。 -数据当前通过我们的 CDN 边缘网络提供服务,并缓存在用户附近的边缘。 +数据目前通过我们 CDN 的边缘网络提供服务,并缓存在靠近用户的边缘节点上。 -我们建议您在试用时禁用此功能。 +我们建议您在试用期间禁用此功能。 ```json title="opencode.json" { @@ -56,110 +56,110 @@ opencode 是开源的,不存储您的任何代码或上下文数据,因此 ### 代码所有权 -**您拥有 opencode 生成的所有代码。** 没有许可限制或所有权主张。 +**您拥有 OpenCode 生成的所有代码。**不存在任何许可限制或所有权声明。 --- ## 定价 -我们对 opencode Enterprise 使用按席位模型。如果您有自己的 LLM 网关,我们不会对使用的 Token 收取费用。有关定价和实施选项的更多详细信息,请 **联系我们**。 +OpenCode 企业版采用按席位定价模型。如果您拥有自己的 LLM 网关,我们不会对使用的 Token 收取费用。有关定价和实施方案的更多详情,请**联系我们**。 --- ## 部署 -完成试用并准备好在您的组织中使用 opencode 后,您可以 **联系我们** 进行讨论定价和实施选项。 +完成试用并准备好在组织中使用 OpenCode 后,您可以**联系我们**,讨论定价和实施方案。 --- -### 中央配置 +### 集中式配置 -我们可以将 opencode 设置为您的整个组织使用单一的中央配置。 +我们可以为您的整个组织设置 OpenCode 的统一集中式配置。 -这种集中式配置可以与您的 SSO 提供商集成,并确保所有用户仅访问您的内部 AI 网关。 +该集中式配置可与您的 SSO 提供商集成,确保所有用户仅访问您的内部 AI 网关。 --- ### SSO 集成 -通过中央配置,opencode 可以与您组织的 SSO 提供商集成以进行身份验证。 +通过集中式配置,OpenCode 可以与您组织的 SSO 提供商集成进行身份验证。 -这使得 opencode 能够通过现有的身份管理系统获取内部 AI 网关的凭据。 +这使得 OpenCode 能够通过您现有的身份管理系统获取内部 AI 网关的凭据。 --- ### 内部 AI 网关 -通过中央配置,opencode 还可以配置为仅使用您的内部 AI 网关。 +通过集中式配置,OpenCode 还可以被配置为仅使用您的内部 AI 网关。 -您还可以禁用所有其他 AI 提供商,确保所有请求都通过组织批准的基础设施。 +您还可以禁用所有其他 AI 提供商,确保所有请求都经过组织批准的基础设施。 --- ### 自托管 -虽然我们建议禁用共享页面以确保您的数据永远不会离开您的组织,我们还可以帮助您在您的基础设施上自行托管它们。 +虽然我们建议禁用共享页面以确保您的数据始终不会离开组织,但我们也可以帮助您在自己的基础设施上自行托管这些页面。 -目前这已在我们的路线图上。如果您有兴趣,**让我们知道**。 +此功能目前已列入我们的路线图。如果您感兴趣,请**告诉我们**。 --- ## 常见问题
-什么是 opencode 企业版? +什么是 OpenCode 企业版? -opencode 企业版适用于希望确保其代码和数据永远不会离开其基础设施的组织。它可以通过使用与 SSO 和内部 AI 网关集成的集中方式配置来实现此目的。 +OpenCode 企业版面向希望确保代码和数据始终留在自有基础设施内的组织。它通过集中式配置与您的 SSO 和内部 AI 网关集成来实现这一目标。
-如何开始使用 opencode 企业版? +如何开始使用 OpenCode 企业版? -与您的团队进行内部实验即可。opencode 默认情况下不存储您的代码或上下文数据,可以轻松上手。 +只需在团队内部开始试用即可。OpenCode 默认不存储您的代码或上下文数据,因此可以轻松上手。 -然后 **联系我们** 讨论定价和实施选项。 +然后**联系我们**,讨论定价和实施方案。
-企业定价如何运作? +企业版定价如何运作? -我们提供按席位企业定价。如果您有自己的 LLM 网关,我们不会对使用的 Token 收取费用。如需了解更多详情,请 **联系我们**,获取根据您组织的需求定制的报价。 +我们提供按席位的企业版定价。如果您拥有自己的 LLM 网关,我们不会对使用的 Token 收取费用。如需了解更多详情,请**联系我们**,获取根据您组织需求定制的报价。
-opencode 企业版保证我的数据安全吗? +我的数据在 OpenCode 企业版中是否安全? -是的。opencode 不存储您的代码或上下文数据。所有处理都在本地进行或通过直接 API 调用您的 AI 提供商。通过中央配置和 SSO 集成,您的数据在组织的基础架构中保持安全。 +是的。OpenCode 不会存储您的代码或上下文数据。所有处理均在本地完成,或通过直接 API 调用发送至您的 AI 提供商。通过集中式配置和 SSO 集成,您的数据将安全地保留在组织的基础设施内。
-我们可以使用自己的私有 npm 注册表吗? +我们可以使用自己的私有 NPM 注册表吗? -opencode 通过 Bun 的本机 `.npmrc` 文件支持来支持私有 npm 注册表。如果您的组织使用私有注册表,例如 JFrog Artifactory、Nexus 或类似的,请确保开发人员在运行 opencode 之前经过身份验证。 +OpenCode 通过 Bun 原生的 `.npmrc` 文件支持来支持私有 npm 注册表。如果您的组织使用私有注册表(例如 JFrog Artifactory、Nexus 或类似产品),请确保开发人员在运行 OpenCode 之前已完成身份验证。 -要使用您的私有注册表设置身份验证: +要设置私有注册表的身份验证: ```bash npm login --registry=https://your-company.jfrog.io/api/npm/npm-virtual/ ``` -这将创建带有身份验证详细信息的 `~/.npmrc`。 opencode 会自动读取这个。 +这会创建包含身份验证信息的 `~/.npmrc` 文件。OpenCode 会自动识别并使用它。 :::caution -在运行 opencode 之前,您必须登录私有注册表。 +在运行 OpenCode 之前,您必须先登录私有注册表。 ::: -或者,您可以手动配置 `.npmrc` 文件: +或者,您也可以手动配置 `.npmrc` 文件: ```bash title="~/.npmrc" registry=https://your-company.jfrog.io/api/npm/npm-virtual/ //your-company.jfrog.io/api/npm/npm-virtual/:_authToken=${NPM_AUTH_TOKEN} ``` -开发人员必须在运行 opencode 之前登录私有注册表,以确保可以从您的企业注册表安装软件包。 +开发人员必须在运行 OpenCode 之前登录私有注册表,以确保能够从您的企业注册表安装软件包。
diff --git a/packages/web/src/content/docs/zh-cn/formatters.mdx b/packages/web/src/content/docs/zh-cn/formatters.mdx index 8c4d7ef8fadf..1f4035fda6b5 100644 --- a/packages/web/src/content/docs/zh-cn/formatters.mdx +++ b/packages/web/src/content/docs/zh-cn/formatters.mdx @@ -1,63 +1,64 @@ --- -title: 格式化程序 -description: opencode 使用特定于语言的格式化程序。 +title: 格式化工具 +description: OpenCode 使用特定语言的格式化工具。 --- -使用特定于语言的格式化程序编写或编辑文件后,opencode 会自动格式化文件。这可以确保生成的代码遵循项目的代码风格。 +OpenCode 会在文件写入或编辑后,自动使用特定语言的格式化工具对其进行格式化。这确保了生成的代码遵循你项目的代码风格。 --- -## 内置 - -opencode 附带了多个适用于流行语言和框架的内置格式化程序。下面是格式化程序、支持的文件扩展名以及所需的命令或配置选项的列表。 - -| 格式化程序 | 扩展名 | 要求 | -| -------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| gofmt | .go | `gofmt` command available | -| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available | -| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in `package.json` | -| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json(c)` config file | -| zig | .zig, .zon | `zig` command available | -| clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file | -| ktlint | .kt, .kts | `ktlint` command available | -| ruff | .py, .pyi | `ruff` command available with config | -| rustfmt | .rs | `rustfmt` command available | -| cargofmt | .rs | `cargo fmt` command available | -| uv | .py, .pyi | `uv` command available | -| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available | -| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available | -| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | -| air | .R | `air` command available | -| dart | .dart | `dart` command available | -| dfmt | .d | `dfmt` command available | -| ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file | -| terraform | .tf, .tfvars | `terraform` command available | -| gleam | .gleam | `gleam` command available | -| nixfmt | .nix | `nixfmt` command available | -| shfmt | .sh, .bash | `shfmt` command available | -| pint | .php | `laravel/pint` dependency in `composer.json` | -| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `oxfmt` dependency in `package.json` and an [experimental env variable flag](/docs/cli/#experimental) | -| ormolu | .hs | `ormolu` command available | - -因此,如果您的项目的 `prettier` 中有 `package.json`,opencode 将自动使用它。 +## 内置格式化工具 + +OpenCode 内置了多种适用于主流语言和框架的格式化工具。下表列出了各格式化工具、支持的文件扩展名以及所需的命令或配置选项。 + +| 格式化工具 | 扩展名 | 要求 | +| -------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| air | .R | `air` 命令可用 | +| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml 及[更多](https://biomejs.dev/) | `biome.json(c)` 配置文件 | +| cargofmt | .rs | `cargo fmt` 命令可用 | +| clang-format | .c, .cpp, .h, .hpp, .ino 及[更多](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` 配置文件 | +| cljfmt | .clj, .cljs, .cljc, .edn | `cljfmt` 命令可用 | +| dart | .dart | `dart` 命令可用 | +| dfmt | .d | `dfmt` 命令可用 | +| gleam | .gleam | `gleam` 命令可用 | +| gofmt | .go | `gofmt` 命令可用 | +| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` 命令可用 | +| ktlint | .kt, .kts | `ktlint` 命令可用 | +| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` 命令可用 | +| nixfmt | .nix | `nixfmt` 命令可用 | +| ocamlformat | .ml, .mli | `ocamlformat` 命令可用且存在 `.ocamlformat` 配置文件 | +| ormolu | .hs | `ormolu` 命令可用 | +| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `package.json` 中有 `oxfmt` 依赖,且设置了[实验性环境变量标志](/docs/cli/#experimental) | +| pint | .php | `composer.json` 中有 `laravel/pint` 依赖 | +| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml 及[更多](https://prettier.io/docs/en/index.html) | `package.json` 中有 `prettier` 依赖 | +| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` 命令可用 | +| ruff | .py, .pyi | `ruff` 命令可用且有相应配置 | +| rustfmt | .rs | `rustfmt` 命令可用 | +| shfmt | .sh, .bash | `shfmt` 命令可用 | +| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` 命令可用 | +| terraform | .tf, .tfvars | `terraform` 命令可用 | +| uv | .py, .pyi | `uv` 命令可用 | +| zig | .zig, .zon | `zig` 命令可用 | + +因此,如果你的项目 `package.json` 中包含 `prettier`,OpenCode 会自动使用它进行格式化。 --- -## 它是如何工作的 +## 工作原理 -当 opencode 写入或编辑文件时,它: +当 OpenCode 写入或编辑文件时,它会: -1. 根据所有启用的格式化程序检查文件扩展名。 -2. 对文件运行适当的格式化程序命令。 -3. 自动应用格式更改。 +1. 根据所有已启用的格式化工具检查文件扩展名。 +2. 对文件运行相应的格式化命令。 +3. 自动应用格式化更改。 -此过程在后台进行,确保无需任何手动步骤即可维护您的代码样式。 +整个过程在后台完成,无需任何手动操作即可保持代码风格的一致性。 --- ## 配置 -您可以通过 opencode 配置中的 `formatter` 部分自定义格式化程序。 +你可以通过 OpenCode 配置中的 `formatter` 部分自定义格式化工具。 ```json title="opencode.json" { @@ -66,22 +67,22 @@ opencode 附带了多个适用于流行语言和框架的内置格式化程序 } ``` -每个格式化程序配置支持以下内容: +每个格式化工具的配置支持以下属性: -| 属性 | 类型 | 描述 | -| ------------- | -------- | ---------------------------------- | -| `disabled` | boolean | 将其设置为 `true` 以禁用格式化程序 | -| `command` | string[] | 格式化运行的命令 | -| `environment` | object | 运行格式化程序时要设置的环境变量 | -| `extensions` | string[] | 此格式化程序应处理的文件扩展名 | +| 属性 | 类型 | 描述 | +| ------------- | -------- | ------------------------------ | +| `disabled` | boolean | 设为 `true` 可禁用该格式化工具 | +| `command` | string[] | 执行格式化的命令 | +| `environment` | object | 运行格式化工具时设置的环境变量 | +| `extensions` | string[] | 该格式化工具处理的文件扩展名 | -让我们看一些例子。 +下面来看一些示例。 --- -### 禁用格式化程序 +### 禁用格式化工具 -要全局禁用 **所有** 格式化程序,将 `formatter` 设置为 `false`: +要全局禁用**所有**格式化工具,将 `formatter` 设为 `false`: ```json title="opencode.json" {3} { @@ -90,7 +91,7 @@ opencode 附带了多个适用于流行语言和框架的内置格式化程序 } ``` -要禁用 **特定** 格式化程序,将 `disabled` 设置为 `true`: +要禁用**特定**格式化工具,将 `disabled` 设为 `true`: ```json title="opencode.json" {5} { @@ -105,9 +106,9 @@ opencode 附带了多个适用于流行语言和框架的内置格式化程序 --- -### 自定义格式化程序 +### 自定义格式化工具 -您可以覆盖内置格式化程序或通过指定命令、环境变量和文件扩展名添加新格式化程序: +你可以通过指定命令、环境变量和文件扩展名来覆盖内置格式化工具或添加新的格式化工具: ```json title="opencode.json" {4-14} { @@ -128,4 +129,4 @@ opencode 附带了多个适用于流行语言和框架的内置格式化程序 } ``` -命令中的 **`$FILE` 占位符** 将替换为正在格式化的文件的路径。 +命令中的 **`$FILE` 占位符**会被替换为待格式化文件的路径。 diff --git a/packages/web/src/content/docs/zh-cn/github.mdx b/packages/web/src/content/docs/zh-cn/github.mdx index b9b80d0dd6f0..01847f193820 100644 --- a/packages/web/src/content/docs/zh-cn/github.mdx +++ b/packages/web/src/content/docs/zh-cn/github.mdx @@ -1,43 +1,43 @@ --- title: GitHub -description: 在 GitHub 问题和拉取请求中使用 opencode。 +description: 在 GitHub Issue 和 Pull Request 中使用 OpenCode。 --- -opencode 与您的 GitHub 工作流程集成。在评论中提及 `/opencode` 或 `/oc`,opencode 将在您的 GitHub Actions 运行器中执行任务。 +OpenCode 可以与你的 GitHub 工作流集成。在评论中提及 `/opencode` 或 `/oc`,OpenCode 就会在你的 GitHub Actions 运行器中执行任务。 --- -## 功能 +## 功能特性 -- **Triage issues**: 要求 opencode 调查问题并向您解释。 -- **Fix and implement**: 要求 opencode 修复问题或实施功能。将在一个新的分支中工作并提交包含所有更改的 PR。 -- **Secure**: opencode 在 GitHub 的运行器中运行。 +- **问题分类**:让 OpenCode 调查某个 Issue 并为你做出解释。 +- **修复与实现**:让 OpenCode 修复 Issue 或实现某个功能。它会在新分支中工作,并提交包含所有变更的 PR。 +- **安全可靠**:OpenCode 在你自己的 GitHub 运行器中运行。 --- ## 安装 -在 GitHub 存储库中的项目中运行以下命令: +在一个位于 GitHub 仓库中的项目里运行以下命令: ```bash opencode github install ``` -这将引导您完成安装 GitHub 应用程序、创建工作流程和设置机密。 +该命令会引导你完成 GitHub App 的安装、工作流的创建以及密钥的配置。 --- ### 手动设置 -或者您可以手动设置。 +你也可以手动进行设置。 -1. **Install the GitHub app** +1. **安装 GitHub App** - 前往 [**github.com/apps/opencode-agent**](https://github.com/apps/opencode-agent)。确保它​​已安装在目标存储库上。 + 前往 [**github.com/apps/opencode-agent**](https://github.com/apps/opencode-agent),确保已在目标仓库中安装该应用。 -2. **Add the workflow** +2. **添加工作流** - 将以下工作流程文件添加到存储库中的 `.github/workflows/opencode.yml` 中。确保在 `model` 中设置适当的 `env` 和所需的 API 密钥。 + 将以下工作流文件添加到仓库的 `.github/workflows/opencode.yml` 中。请确保在 `env` 中设置合适的 `model` 及所需的 API 密钥。 ```yml title=".github/workflows/opencode.yml" {24,26} name: opencode @@ -73,21 +73,21 @@ opencode github install # github_token: xxxx ``` -3. **Store the API keys in secrets** +3. **将 API 密钥存储到 Secrets 中** - 在您的组织或项目的 **Settings** 中,展开左侧的 **Secrets and variables**,然后选择 **Actions**。并添加所需的 API 密钥。 + 在你的组织或项目的 **Settings** 中,展开左侧的 **Secrets and variables**,然后选择 **Actions**,添加所需的 API 密钥。 --- ## 配置 -- `model`:与 opencode 一起使用的模型。采用 `provider/model` 格式。这是 **必需的**。 -- `agent`:要使用的代理。必须是一级代理。如果未找到,则从配置回退到 `default_agent` 或 `"build"`。 -- `share`:是否共享 opencode 会话。对于公共存储库,默认为 **true**。 -- `prompt`:可选的自定义提示以覆盖默认行为。使用它来自定义 opencode 处理请求的方式。 -- `token`:可选的 GitHub 访问 Token,用于执行创建评论、提交更改和打开拉取请求等操作。默认情况下,opencode 使用来自 opencode GitHub 应用程序的安装访问 Token,因此提交、评论和拉取请求显示为来自应用。 +- `model`:OpenCode 使用的模型,格式为 `provider/model`。此项为**必填**。 +- `agent`:要使用的代理,必须是主代理。如果未找到,则回退到配置中的 `default_agent`,若仍未找到则使用 `"build"`。 +- `share`:是否共享 OpenCode 会话。对于公开仓库,默认为 **true**。 +- `prompt`:可选的自定义提示词,用于覆盖默认行为。可通过此项自定义 OpenCode 处理请求的方式。 +- `token`:可选的 GitHub 访问 Token,用于执行创建评论、提交变更和创建 Pull Request 等操作。默认情况下,OpenCode 使用 OpenCode GitHub App 的安装访问 Token,因此提交、评论和 Pull Request 会显示为来自该应用。 - 或者,您可以使用 GitHub Action 运行程序的 [内置 `GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token),而无需安装 opencode GitHub 应用程序。只需确保在您的工作流程中所需的权限: + 你也可以使用 GitHub Action 运行器内置的 [`GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token),而无需安装 OpenCode GitHub App。只需确保在工作流中授予所需的权限: ```yaml permissions: @@ -97,26 +97,26 @@ opencode github install issues: write ``` - 您可以在 GitHub Actions 文档中了解有关配置工作流程的更多信息。 + 如果你愿意,也可以使用[个人访问令牌](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)(PAT)。 --- ## 支持的事件 -opencode 可以由以下 GitHub 事件触发: +OpenCode 可以由以下 GitHub 事件触发: -| 事件类型 | 触发方式 | 详情 | -| ----------------------------- | ---------------------------- | --------------------------------------------------------------------------------------- | -| `issue_comment` | 对问题或 PR 发表评论 | 在评论中提及 `/opencode` 或 `/oc`。 opencode 读取上下文并可以创建分支、打开 PR 或回复。 | -| `pull_request_review_comment` | 对 PR 中的特定代码行进行评论 | 在检查代码时提及 `/opencode` 或 `/oc`。 opencode 接收文件路径、行号和 diff 上下文。 | -| `issues` | 问题已打开或已编辑 | 创建或修改问题时自动触发 opencode。需要 `prompt` 输入。 | -| `pull_request` | PR 已开启或已更新 | 当 PR 打开、同步或重新打开时自动触发 opencode。对于自动评论很有帮助。 | -| `schedule` | 基于 Cron 的计划 | 按计划运行 opencode。需要 `prompt` 输入。输出进入日志和 PR(没有可评论的问题)。 | -| `workflow_dispatch` | 从 GitHub UI 手动触发 | 通过“操作”选项卡触发 opencode。需要 `prompt` 输入。输出进入日志和 PR。 | +| 事件类型 | 触发方式 | 详情 | +| ----------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------- | +| `issue_comment` | 在 Issue 或 PR 上发表评论 | 在评论中提及 `/opencode` 或 `/oc`。OpenCode 会读取上下文,并可创建分支、提交 PR 或回复。 | +| `pull_request_review_comment` | 在 PR 中对特定代码行发表评论 | 在代码审查时提及 `/opencode` 或 `/oc`。OpenCode 会接收文件路径、行号和 diff 上下文。 | +| `issues` | Issue 被创建或编辑 | 在 Issue 创建或修改时自动触发 OpenCode。需要提供 `prompt` 输入。 | +| `pull_request` | PR 被创建或更新 | 在 PR 被打开、同步或重新打开时自动触发 OpenCode。适用于自动化审查场景。 | +| `schedule` | 基于 Cron 的定时任务 | 按计划运行 OpenCode。需要提供 `prompt` 输入。输出会写入日志和 PR(没有 Issue 可供评论)。 | +| `workflow_dispatch` | 从 GitHub UI 手动触发 | 通过 Actions 选项卡按需触发 OpenCode。需要提供 `prompt` 输入。输出会写入日志和 PR。 | -### 计划示例 +### 定时任务示例 -按计划运行 opencode 以执行自动化任务: +按计划运行 OpenCode 以执行自动化任务: ```yaml title=".github/workflows/opencode-scheduled.yml" name: Scheduled OpenCode Task @@ -150,13 +150,13 @@ jobs: If you find issues worth addressing, open an issue to track them. ``` -对于计划事件,`prompt` 输入是 **必需的**,因为没有注释可以从中提取指令。希望计划工作流在没有用户上下文的情况下运行并进行权限检查,因此如果您 opencode 创建分支或 PR,工作流必须支持 `contents: write` 和 `pull-requests: write`。 +对于定时事件,`prompt` 输入为**必填**,因为没有评论可供提取指令。定时工作流在运行时没有用户上下文来进行权限检查,因此如果你希望 OpenCode 创建分支或 PR,工作流必须授予 `contents: write` 和 `pull-requests: write` 权限。 --- ### Pull Request 示例 -或更新 PR 时自动审核: +在 PR 被创建或更新时自动进行审查: ```yaml title=".github/workflows/opencode-review.yml" name: opencode-review @@ -191,13 +191,13 @@ jobs: - Suggest improvements ``` -对于 `pull_request` 事件,如果未提供 `prompt`,opencode 将默认审核拉取请求。 +对于 `pull_request` 事件,如果未提供 `prompt`,OpenCode 将默认对该 Pull Request 进行审查。 --- ### Issue 分类示例 -自动分类新问题。此示例过滤超过 30 天的账户以减少垃圾邮件: +自动分类新建的 Issue。以下示例会过滤掉注册不满 30 天的账户以减少垃圾信息: ```yaml title=".github/workflows/opencode-triage.yml" name: Issue Triage @@ -246,13 +246,13 @@ jobs: Otherwise, do not comment. ``` -对于 `issues` 事件,`prompt` 输入是 **必需的**,因为没有注释可以从中提取指令。 +对于 `issues` 事件,`prompt` 输入为**必填**,因为没有评论可供提取指令。 --- -## 自定义提示 +## 自定义提示词 -覆盖默认提示,为您的工作流程自定义 opencode 的行为。 +覆盖默认提示词,以便为你的工作流自定义 OpenCode 的行为。 ```yaml title=".github/workflows/opencode.yml" - uses: anomalyco/opencode/github@latest @@ -265,57 +265,57 @@ jobs: - Suggest improvements ``` -这对于执行与您的项目相关的特定审查标准、编码标准或重点领域非常有用。 +这对于在项目中实施特定的审查标准、编码规范或关注重点非常有用。 --- ## 示例 -以下是如何在 GitHub 中使用 opencode 的一些示例。 +以下是在 GitHub 中使用 OpenCode 的一些示例。 -- **Explain an issue** +- **解释 Issue** - 在 GitHub 问题中添加此评论。 + 在 GitHub Issue 中添加以下评论: ``` /opencode explain this issue ``` - opencode 将阅读整个线程,包括所有评论,并回复并提供清晰的解释。 + OpenCode 会阅读整个讨论串(包括所有评论),并回复一份清晰的解释。 -- **Fix an issue** +- **修复 Issue** - 在 GitHub 问题中,说: + 在 GitHub Issue 中输入: ``` /opencode fix this ``` - opencode 将创建一个新分支,实施更改,并使用更改打开 PR。 + OpenCode 会创建一个新分支,实现变更,并提交一个包含所有修改的 PR。 -- **Review PRs and make changes** +- **审查 PR 并进行修改** - 在 GitHub PR 上留下以下评论。 + 在 GitHub PR 上留下以下评论: ``` Delete the attachment from S3 when the note is removed /oc ``` - opencode 将实施请求的更改并提交到同一个 PR。 + OpenCode 会实现所请求的变更并将其提交到同一个 PR 中。 -- **Review specific code lines** +- **审查特定代码行** - 直接在 PR 的“文件”选项卡中的代码行上留下评论。opencode 自动检测文件、行号和差异上下文以提供准确的响应。 + 在 PR 的 "Files" 选项卡中直接对代码行留下评论。OpenCode 会自动检测文件、行号和 diff 上下文,从而提供精准的响应。 ``` [Comment on specific lines in Files tab] /oc add error handling here ``` - 当评论特定行时,opencode 接收: - - 正在审查的确切文件 - - 具体代码行 - - 周围的差异上下文 + 当你对特定代码行发表评论时,OpenCode 会接收到: + - 正在审查的具体文件 + - 特定的代码行 + - 周围的 diff 上下文 - 行号信息 - 这允许更有针对性的请求,而无需手动指定文件路径或行号。 + 这样你就可以提出更有针对性的请求,而无需手动指定文件路径或行号。 diff --git a/packages/web/src/content/docs/zh-cn/gitlab.mdx b/packages/web/src/content/docs/zh-cn/gitlab.mdx index e9c474e62fec..c1ffa0be614c 100644 --- a/packages/web/src/content/docs/zh-cn/gitlab.mdx +++ b/packages/web/src/content/docs/zh-cn/gitlab.mdx @@ -1,34 +1,34 @@ --- title: GitLab -description: 在 GitLab 问题和合并请求中使用 opencode。 +description: 在 GitLab issue 和合并请求中使用 OpenCode。 --- -opencode 通过 GitLab CI/CD 管道或与 GitLab Duo 与您的 GitLab 工作流程集成。 +OpenCode 通过 GitLab CI/CD 流水线或 GitLab Duo 与你的 GitLab 工作流集成。 -在这两种情况下,opencode 都会在您的 GitLab 运行器上运行。 +在这两种情况下,OpenCode 都将在你的 GitLab Runner 上运行。 --- ## GitLab CI -opencode 在常规 GitLab 管道中工作。您可以将其构建为管道 [CI 组件](https://docs.gitlab.com/ee/ci/components/) +OpenCode 可以在常规的 GitLab 流水线中运行。你可以将其作为 [CI 组件](https://docs.gitlab.com/ee/ci/components/) 集成到流水线中。 -在这里,我们使用社区创建的 opencode CI/CD 组件 — [nagyv/gitlab-opencode](https://gitlab.com/nagyv/gitlab-opencode)。 +这里我们使用的是社区创建的 OpenCode CI/CD 组件 — [nagyv/gitlab-opencode](https://gitlab.com/nagyv/gitlab-opencode)。 --- -### 功能 +### 功能特性 -- **Use custom configuration per job**: 使用自定义配置目录配置 opencode,例如 `./config/#custom-directory` 以启用或禁用 opencode 调用的功能。 -- **Authentication**: Uses OIDC for secure authentication -- **Flexible**: CI 组件支持多种输入来自定义其行为 +- **按任务自定义配置**:使用自定义配置目录来配置 OpenCode,例如 `./config/#custom-directory`,以便为每次 OpenCode 调用启用或禁用特定功能。 +- **最小化配置**:CI 组件会在后台完成 OpenCode 的设置,你只需创建 OpenCode 配置和初始提示词即可。 +- **灵活可定制**:CI 组件支持多种输入参数来自定义其行为。 --- ### 设置 -1. 将 opencode 身份验证 JSON 作为文件类型 CI 环境变量存储在 **Settings** > **CI/CD** > **Variables** 下。确保将它们标记为“隐藏和隐藏”。 -2. 将以下内容添加到您的 `.gitlab-ci.yml` 文件中。 +1. 将你的 OpenCode 身份验证 JSON 作为文件类型的 CI 环境变量存储在 **Settings** > **CI/CD** > **Variables** 下。请确保将其标记为 "Masked and hidden"。 +2. 将以下内容添加到你的 `.gitlab-ci.yml` 文件中。 ```yaml title=".gitlab-ci.yml" include: @@ -40,40 +40,39 @@ opencode 在常规 GitLab 管道中工作。您可以将其构建为管道 [CI message: "Your prompt here" ``` -有关此组件的更多输入和示例[查看文档](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode)。 +有关更多输入参数和使用场景,请[查看该组件的文档](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode)。 --- ## GitLab Duo -opencode 与您的 GitLab 工作流程集成。 -在评论中提及 `@opencode`,opencode 将在您的 GitLab CI 管道中执行任务。 +OpenCode 与你的 GitLab 工作流集成。 +在评论中提及 `@opencode`,OpenCode 将在你的 GitLab CI 流水线中执行任务。 --- -### 功能 +### 功能特性 -- **Triage issues**: 要求 opencode 调查问题并向您解释。 -- **Fix and implement**: 要求 opencode 修复问题或实施功能。 - 它将创建一个新分支并提出包含更改的合并请求。 -- **Secure**: opencode 在您的 GitLab 运行器上运行。 +- **问题分类**:让 OpenCode 调查某个 issue 并为你解释。 +- **修复与实现**:让 OpenCode 修复 issue 或实现某个功能。它会创建一个新分支,并提交包含更改的合并请求。 +- **安全可靠**:OpenCode 在你的 GitLab Runner 上运行。 --- ### 设置 -opencode 在您的 GitLab CI/CD 管道中运行,您需要进行以下设置: +OpenCode 在你的 GitLab CI/CD 流水线中运行,以下是设置所需的步骤: :::tip -查看 [**GitLab docs**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) 获取最新说明。 +请查看 [**GitLab 文档**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) 获取最新说明。 ::: -1. 配置您的 GitLab 环境 +1. 配置你的 GitLab 环境 2. 设置 CI/CD -3. 获取 AI 模型提供商 API 密钥 +3. 获取 AI 模型提供商的 API 密钥 4. 创建服务账户 5. 配置 CI/CD 变量 -6. 创建一个流配置文件,这是一个示例: +6. 创建流程配置文件,以下是一个示例:
@@ -152,44 +151,44 @@ opencode 在您的 GitLab CI/CD 管道中运行,您需要进行以下设置:
-详细说明可以参考 [GitLab CLI agents docs](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/)。 +详细说明请参考 [GitLab CLI agents 文档](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/)。 --- ### 示例 -以下是如何在 GitLab 中使用 opencode 的一些示例。 +以下是在 GitLab 中使用 OpenCode 的一些示例。 :::tip -您可以配置使用与 `@opencode` 不同的触发词。 +你可以配置使用不同于 `@opencode` 的触发词。 ::: -- **Explain an issue** +- **解释 issue** - 在 GitLab 问题中添加此评论。 + 在 GitLab issue 中添加以下评论。 ``` @opencode explain this issue ``` - opencode 将阅读该问题并回复并提供清晰的解释。 + OpenCode 会阅读该 issue 并回复清晰的解释。 -- **Fix an issue** +- **修复 issue** - 在 GitLab 问题中,说: + 在 GitLab issue 中输入: ``` @opencode fix this ``` - opencode 将创建一个新分支,实施更改,并打开包含更改的合并请求。 + OpenCode 会创建一个新分支,实现更改,并提交包含更改的合并请求。 -- **Review merge requests** +- **审查合并请求** - 对 GitLab 合并请求留下以下评论。 + 在 GitLab 合并请求中留下以下评论。 ``` @opencode review this merge request ``` - opencode 将审核合并请求并提供反馈。 + OpenCode 会审查合并请求并提供反馈。 diff --git a/packages/web/src/content/docs/zh-cn/ide.mdx b/packages/web/src/content/docs/zh-cn/ide.mdx index fb3a92bec7c8..1a759a8e6a04 100644 --- a/packages/web/src/content/docs/zh-cn/ide.mdx +++ b/packages/web/src/content/docs/zh-cn/ide.mdx @@ -1,48 +1,48 @@ --- title: IDE -description: VS Code、Cursor 等 IDE 的 opencode 扩展 +description: 适用于 VS Code、Cursor 及其他 IDE 的 OpenCode 扩展 --- -opencode 与 VS Code、Cursor 或任何支持终端的 IDE 集成。只需在终端中运行 `opencode` 即可开始。 +OpenCode 可与 VS Code、Cursor 或任何支持终端的 IDE 集成。只需在终端中运行 `opencode` 即可开始使用。 --- ## 用法 -- **快速启动**:使用 `Cmd+Esc` (Mac) 或 `Ctrl+Esc` (Windows/Linux) 在分割终端视图中打开 opencode,或者聚焦现有终端会话(如果现有终端会话正在运行)。 -- **新会话**:使用 `Cmd+Shift+Esc` (Mac) 或 `Ctrl+Shift+Esc` (Windows/Linux) 启动新的 opencode 终端会话,即使该会话已打开。您还可以单击 UI 中的 opencode 按钮。 -- **上下文获取**:自动与 opencode 共享您当前的选择或选项卡。 -- **文件引用快捷方式**:使用 `Cmd+Option+K` (Mac) 或 `Alt+Ctrl+K` (Linux/Windows) 插入文件引用。例如,`@File#L37-42`。 +- **快速启动**:使用 `Cmd+Esc`(Mac)或 `Ctrl+Esc`(Windows/Linux)在分屏终端视图中打开 OpenCode,如果已有终端会话正在运行,则会自动聚焦到该会话。 +- **新建会话**:使用 `Cmd+Shift+Esc`(Mac)或 `Ctrl+Shift+Esc`(Windows/Linux)启动新的 OpenCode 终端会话,即使已有会话在运行也会新建。你也可以点击界面中的 OpenCode 按钮。 +- **上下文感知**:自动将当前选中内容或标签页共享给 OpenCode。 +- **文件引用快捷键**:使用 `Cmd+Option+K`(Mac)或 `Alt+Ctrl+K`(Linux/Windows)插入文件引用。例如 `@File#L37-42`。 --- ## 安装 -要在 VS Code 和 Cursor、Windsurf、VSCodium 等流行分支上安装 opencode: +在 VS Code 及其常见分支(如 Cursor、Windsurf、VSCodium)上安装 OpenCode: 1. 打开 VS Code 2. 打开集成终端 -3. 运行 `opencode` - 扩展会自动安装 +3. 运行 `opencode`——扩展将自动安装 -另一方面,如果您想在从 TUI 运行 `/editor` 或 `/export` 时使用自己的 IDE,则需要设置 `export EDITOR="code --wait"`。 [了解更多](/docs/tui/#editor-setup)。 +如果你希望在 TUI 中执行 `/editor` 或 `/export` 时使用自己的 IDE,需要设置 `export EDITOR="code --wait"`。[了解更多](/docs/tui/#editor-setup)。 --- ### 手动安装 -在扩展市场中搜索 **opencode**,然后单击 **Install**。 +在扩展商店中搜索 **OpenCode**,然后点击 **Install**。 --- ### 故障排除 -如果扩展无法自动安装: +如果扩展未能自动安装: -- 确定您在集成终端中运行 `opencode`。 -- 确认您的 IDE 和 CLI 已安装: - - 对于 VS Code:`code` 命令 - - 对于 Cursor:`cursor` 命令 - - 对于 Windsurf:`windsurf` 命令 - - 对于 VSCodium:`codium` 命令 - - 如果没有,请运行 `Cmd+Shift+P` (Mac) 或 `Ctrl+Shift+P` (Windows/Linux) 并搜索 “Shell Command: Install 'code' command in PATH”(或适用于您的 IDE 的对应命令) -- 确保 VS Code 能够安装扩展 +- 确保你是在集成终端中运行的 `opencode`。 +- 确认你的 IDE 对应的 CLI 命令已安装: + - VS Code:`code` 命令 + - Cursor:`cursor` 命令 + - Windsurf:`windsurf` 命令 + - VSCodium:`codium` 命令 + - 如果未安装,请按 `Cmd+Shift+P`(Mac)或 `Ctrl+Shift+P`(Windows/Linux),搜索 "Shell Command: Install 'code' command in PATH"(或你的 IDE 对应的命令) +- 确保 VS Code 有权限安装扩展 diff --git a/packages/web/src/content/docs/zh-cn/index.mdx b/packages/web/src/content/docs/zh-cn/index.mdx index 5bff7628ac3b..ed278e2397dc 100644 --- a/packages/web/src/content/docs/zh-cn/index.mdx +++ b/packages/web/src/content/docs/zh-cn/index.mdx @@ -1,43 +1,43 @@ --- -title: 介绍 -description: 开始使用 opencode。 +title: 简介 +description: 开始使用 OpenCode。 --- import { Tabs, TabItem } from "@astrojs/starlight/components" import config from "../../../../config.mjs" export const console = config.console -[**opencode**](/) 是一个开源人工智能编码代理。它可用于基于终端的界面、桌面应用程序或 IDE 扩展。 +[**OpenCode**](/) 是一个开源的 AI 编码代理。它提供终端界面、桌面应用和 IDE 扩展等多种使用方式。 -![具有 opencode 主题的 opencode TUI](../../../assets/lander/screenshot.png) +![使用 opencode 主题的 OpenCode TUI](../../../assets/lander/screenshot.png) 让我们开始吧。 --- -#### 先决条件 +#### 前提条件 -要在终端中使用 opencode,您需要: +要在终端中使用 OpenCode,你需要: -1. 现代终端模拟器,例如: +1. 一款现代终端模拟器,例如: - [WezTerm](https://wezterm.org),跨平台 - [Alacritty](https://alacritty.org),跨平台 - [Ghostty](https://ghostty.org),Linux 和 macOS - [Kitty](https://sw.kovidgoyal.net/kitty/),Linux 和 macOS -2. 你要使用的 LLM 提供商 API 密钥。 +2. 你想使用的 LLM 提供商的 API 密钥。 --- ## 安装 -安装 opencode 最简单的方法是通过安装脚本。 +安装 OpenCode 最简单的方法是通过安装脚本。 ```bash curl -fsSL https://opencode.ai/install | bash ``` -您还可以使用以下命令安装它: +你也可以使用以下方式安装: - **使用 Node.js** @@ -79,9 +79,9 @@ curl -fsSL https://opencode.ai/install | bash brew install anomalyco/tap/opencode ``` - > 我们使用 opencode Tap 来获取最新版本。官方 `brew install opencode` 公式由 Homebrew 团队建议,维护频率较低。 + > 我们推荐使用 OpenCode tap 以获取最新版本。官方的 `brew install opencode` formula 由 Homebrew 团队维护,更新频率较低。 -- **在 Arch Linux 上使用 Paru** +- **在 Arch Linux 上安装** ```bash sudo pacman -S opencode # Arch Linux (Stable) @@ -91,7 +91,7 @@ curl -fsSL https://opencode.ai/install | bash #### Windows :::tip[推荐:使用 WSL] -为了在 Windows 上获得最佳体验,我们建议使用 [Windows Subsystem for Linux (WSL)](/docs/windows-wsl)。它提供了更好的性能并与 opencode 的功能完全兼容。 +为了在 Windows 上获得最佳体验,我们推荐使用 [Windows Subsystem for Linux (WSL)](/docs/windows-wsl)。它提供更好的性能,并完全兼容 OpenCode 的所有功能。 ::: - **使用 Chocolatey** @@ -106,7 +106,7 @@ curl -fsSL https://opencode.ai/install | bash scoop install opencode ``` -- **使用 npm** +- **使用 NPM** ```bash npm install -g opencode-ai @@ -124,18 +124,17 @@ curl -fsSL https://opencode.ai/install | bash docker run -it --rm ghcr.io/anomalyco/opencode ``` -目前正在支持在 Windows 上安装 opencode 时使用 Bun。 +在 Windows 上通过 Bun 安装 OpenCode 的支持目前正在开发中。 -您还可以从 [Releases](https://github.com/anomalyco/opencode/releases) 获取二进制文件。 +你也可以从 [Releases](https://github.com/anomalyco/opencode/releases) 页面直接下载二进制文件。 --- ## 配置 -借助 opencode,你可以通过配置 API 使用任意 LLM 提供商。 +通过 OpenCode,你可以配置 API 密钥来使用任意 LLM 提供商。 -如果你刚开始使用 LLM 提供商,我们建议使用 [OpenCode Zen](/docs/zen)。 -这是经过 opencode 团队测试和验证的精选模型列表。 +如果你刚开始接触 LLM 提供商,我们推荐使用 [OpenCode Zen](/docs/zen)。这是一组经过 OpenCode 团队测试和验证的精选模型。 1. 在 TUI 中运行 `/connect` 命令,选择 opencode,然后前往 [opencode.ai/auth](https://opencode.ai/auth)。 @@ -143,9 +142,9 @@ curl -fsSL https://opencode.ai/install | bash /connect ``` -2. 登录,添加您的账单信息,然后复制您的详细 API 密钥。 +2. 登录并添加账单信息,然后复制你的 API 密钥。 -3. 粘贴您的 API 密钥。 +3. 粘贴你的 API 密钥。 ```txt ┌ API key @@ -154,82 +153,79 @@ curl -fsSL https://opencode.ai/install | bash └ enter ``` -或者,你也可以选择其他提供商之一。[了解更多](/docs/providers#directory)。 +你也可以选择其他提供商。[了解更多](/docs/providers#directory)。 --- ## 初始化 -现在您已经配置了提供商,您可以导航到一个项目 -你想继续工作。 +配置好提供商后,导航到你想要处理的项目目录。 ```bash cd /path/to/project ``` -并运行 opencode。 +然后运行 OpenCode。 ```bash opencode ``` -接下来,通过运行以下命令来初始化项目的 opencode。 +接下来,运行以下命令为项目初始化 OpenCode。 ```bash frame="none" /init ``` -这涉及 opencode 分析您的项目并在以下位置创建 `AGENTS.md` 文件 -项目根。 +OpenCode 会分析你的项目并在项目根目录创建一个 `AGENTS.md` 文件。 :::tip -您应该将项目的 `AGENTS.md` 文件提交到 Git。 +你应该将项目的 `AGENTS.md` 文件提交到 Git。 ::: -这有助于 opencode 理解项目结构和使用的编码模式。 +这有助于 OpenCode 理解项目结构和编码规范。 --- -## 用法 +## 使用 -您现在已准备好使用 opencode 来处理您的项目。请轻松询问任何事物! +现在你已经准备好使用 OpenCode 来处理项目了,尽管提问吧! -如果您不熟悉使用 AI 编码代理,以下是一些可能会有所帮助的示例。 +如果你是第一次使用 AI 编码代理,以下示例可能会对你有所帮助。 --- ### 提问 -您可以要求 opencode 向您解释代码库。 +你可以让 OpenCode 为你讲解代码库。 :::tip -使用 `@` 键模糊搜索工程中的文件。 +使用 `@` 键可以模糊搜索项目中的文件。 ::: ```txt frame="none" "@packages/functions/src/api/index.ts" How is authentication handled in @packages/functions/src/api/index.ts ``` -如果您没有处理代码库的一部分,这会很有帮助。 +当你遇到不熟悉的代码时,这个功能非常有用。 --- ### 添加功能 -您可以要求 opencode 向您的项目添加新功能。但是我们首先建议要求它制定一个计划。 +你可以让 OpenCode 为项目添加新功能。不过我们建议先让它制定一个计划。 -1. **创建计划** +1. **制定计划** - opencode 有一个 _计划模式_,该模式禁止其进行更改和 - 相反,建议 _如何_ 实现该功能。 + OpenCode 有一个*计划模式*,该模式下它不会进行任何修改,而是建议*如何*实现该功能。 - 使用 **Tab** 键切换到它。您会在右下角有一个指示符。 + 使用 **Tab** 键切换到计划模式。你会在右下角看到模式指示器。 ```bash frame="none" title="Switch to Plan mode" ``` - 现在让我们描述一下我们想要它做什么。 + 接下来描述你希望它做什么。 ```txt frame="none" When a user deletes a note, we'd like to flag it as deleted in the database. @@ -237,17 +233,15 @@ How is authentication handled in @packages/functions/src/api/index.ts From this screen, the user can undelete a note or permanently delete it. ``` - 您需要为 opencode 提供足够的详细信息才能了解您想要的内容。它有帮助 - 就像与团队中的初级开发人员交谈一样与它交谈。 + 你需要提供足够的细节,让 OpenCode 理解你的需求。可以把它当作团队中的一名初级开发者来沟通。 :::tip - 为 opencode 提供大量上下文和示例,以帮助其理解您的内容 - 想。 + 为 OpenCode 提供充足的上下文和示例,帮助它理解你的需求。 ::: 2. **迭代计划** - 一旦它为您提供了计划,您就可以提供反馈或添加更多详细信息。 + 当它给出计划后,你可以提供反馈或补充更多细节。 ```txt frame="none" We'd like to design this new screen using a design I've used before. @@ -255,22 +249,20 @@ How is authentication handled in @packages/functions/src/api/index.ts ``` :::tip - 将图像拖放到终端中以将其添加到提示中。 + 将图片拖放到终端中即可将其添加到提示词中。 ::: - opencode 可以扫描您提供的任何图像并将其添加到提示中。您可以 - 通过将图像拖放到终端中来完成此操作。 + OpenCode 可以扫描你提供的图片并将其添加到提示词中。只需将图片拖放到终端窗口即可。 3. **构建功能** - 一旦您对计划感到满意,请切换回 _构建模式_ - 再次按 **Tab** 键。 + 当你对计划满意后,再次按 **Tab** 键切换回*构建模式*。 ```bash frame="none" ``` - 并要求它做出改变。 + 然后让它开始实施。 ```bash frame="none" Sounds good! Go ahead and make the changes. @@ -278,10 +270,9 @@ How is authentication handled in @packages/functions/src/api/index.ts --- -### 进行更改 +### 直接修改 -对于更直接的更改,您可以要求 opencode 直接构建它 -无需先审查计划。 +对于比较简单的修改,你可以直接让 OpenCode 实施,无需先审查计划。 ```txt frame="none" "@packages/functions/src/settings.ts" "@packages/functions/src/notes.ts" We need to add authentication to the /settings route. Take a look at how this is @@ -289,38 +280,37 @@ handled in the /notes route in @packages/functions/src/notes.ts and implement the same logic in @packages/functions/src/settings.ts ``` -您需要确保提供大量详细信息,以便 opencode 做出正确的决定变化。 +请确保提供足够的细节,以便 OpenCode 做出正确的修改。 --- -### 撤消更改 +### 撤销修改 -假设您要求 opencode 进行一些更改。 +假设你让 OpenCode 做了一些修改。 ```txt frame="none" "@packages/functions/src/api/index.ts" Can you refactor the function in @packages/functions/src/api/index.ts? ``` -但你意识到这不是你想要的。您 **可以撤消** 更改 -使用 `/undo` 命令。 +但你发现结果不是你想要的。你**可以使用** `/undo` 命令来撤销修改。 ```bash frame="none" /undo ``` -opencode 现在将恢复您所做的更改并再次显示您的原始消息。 +OpenCode 会还原所做的修改,并重新显示你之前的消息。 ```txt frame="none" "@packages/functions/src/api/index.ts" Can you refactor the function in @packages/functions/src/api/index.ts? ``` -您可以从这里调整提示并要求 opencode 重试。 +你可以调整提示词,让 OpenCode 重新尝试。 :::tip -您可以多次运行 `/undo` 以撤销多次更改。 +你可以多次运行 `/undo` 来撤销多次修改。 ::: -或者您 **可以使用 `/redo` 命令重做** 更改。 +你也**可以使用** `/redo` 命令来重做修改。 ```bash frame="none" /redo @@ -330,24 +320,24 @@ Can you refactor the function in @packages/functions/src/api/index.ts? ## 分享 -您与 opencode 的对话可以 [与您的团队分享](/docs/share)。 +你与 OpenCode 的对话可以[与团队分享](/docs/share)。 ```bash frame="none" /share ``` -这会创建当前对话的链接并复制到剪贴板。 +这会生成当前对话的链接并复制到剪贴板。 :::note -默认情况下不共享对话。 +对话默认不会被分享。 ::: -这是带有 opencode 的 [示例对话](https://opencode.ai/s/4XP1fce5)。 +这是一个与 OpenCode 的[示例对话](https://opencode.ai/s/4XP1fce5)。 --- -## 定制 +## 个性化 -就是这样!你现在已经是 opencode 高手了。 +以上就是全部内容!你现在已经是 OpenCode 的使用高手了。 -要让您成为自己的,我们建议 [选择一个主题](/docs/themes)、[自定义交互绑定](/docs/keybinds)、[配置代码整理程序](/docs/formatters)、[创建自定义命令](/docs/commands) 或使用 [opencode 配置](/docs/config)。 +要让它更符合你的习惯,我们推荐[选择一个主题](/docs/themes)、[自定义快捷键](/docs/keybinds)、[配置代码格式化工具](/docs/formatters)、[创建自定义命令](/docs/commands),或者探索 [OpenCode 配置](/docs/config)。 diff --git a/packages/web/src/content/docs/zh-cn/keybinds.mdx b/packages/web/src/content/docs/zh-cn/keybinds.mdx index 0ecd7c9bf66e..bb1d2c21a791 100644 --- a/packages/web/src/content/docs/zh-cn/keybinds.mdx +++ b/packages/web/src/content/docs/zh-cn/keybinds.mdx @@ -1,9 +1,9 @@ --- title: 快捷键 -description: 自定义您的按键绑定。 +description: 自定义您的快捷键。 --- -opencode 有一个按键绑定列表,您可以通过 opencode 配置进行自定义。 +OpenCode 提供了一系列快捷键,您可以通过 OpenCode 配置进行自定义。 ```json title="opencode.json" { @@ -105,19 +105,19 @@ opencode 有一个按键绑定列表,您可以通过 opencode 配置进行自 --- -## Leader 键 +## 前导键 -opencode 对大多数按键绑定使用 `leader` 键。这可以避免终端中的冲突。 +OpenCode 的大多数快捷键使用 `leader`(前导键)。这可以避免与终端中的其他快捷键冲突。 -默认情况下,`ctrl+x` 是主键,大多数操作要求您先按主键,再按快捷键。例如,要开始新会话,请先按 `ctrl+x`,然后按 `n`。 +默认情况下,`ctrl+x` 是前导键,大多数操作需要您先按下前导键,然后再按对应的快捷键。例如,要新建一个会话,请先按 `ctrl+x`,然后按 `n`。 -您不需要为键绑定使用主键,但我们建议您这样做。 +您不一定需要使用前导键来设置快捷键,但我们建议您这样做。 --- -## 禁用按键绑定 +## 禁用快捷键 -您可以通过将按键添加到您的配置中并使用值“none”来禁用按键绑定。 +您可以通过在配置中将对应的键值设置为 "none" 来禁用某个快捷键。 ```json title="opencode.json" { @@ -130,41 +130,41 @@ opencode 对大多数按键绑定使用 `leader` 键。这可以避免终端中 --- -## 桌面提示快捷键 - -opencode 桌面应用程序提示输入支持常见的 Readline/Emacs 风格的文本编辑快捷方式。这些是内置的,目前无法通过 `opencode.json` 进行配置。 - -| 快捷键 | 动作 | -| -------- | ------------------------- | -| `ctrl+a` | 移至当前行起点 | -| `ctrl+e` | 移至当前行尾 | -| `ctrl+b` | 将光标向后移动一个字符 | -| `ctrl+f` | 将光标向前移动一个字符 | -| `alt+b` | 将光标向后移动一个单词 | -| `alt+f` | 将光标向前移动一个单词 | -| `ctrl+d` | 删除光标下的字符 | -| `ctrl+k` | 删除到行尾 | -| `ctrl+u` | 删除到行首 | -| `ctrl+w` | 删除前一个单词 | -| `alt+d` | 删除下一个单词 | -| `ctrl+t` | 转置字符 | -| `ctrl+g` | 取消弹出窗口/中止运行响应 | +## 桌面版提示词输入快捷键 + +OpenCode 桌面应用的提示词输入框支持常见的 Readline/Emacs 风格文本编辑快捷键。这些快捷键为内置功能,目前无法通过 `opencode.json` 进行配置。 + +| 快捷键 | 操作 | +| -------- | --------------------------------- | +| `ctrl+a` | 移动到当前行的开头 | +| `ctrl+e` | 移动到当前行的末尾 | +| `ctrl+b` | 光标向后移动一个字符 | +| `ctrl+f` | 光标向前移动一个字符 | +| `alt+b` | 光标向后移动一个单词 | +| `alt+f` | 光标向前移动一个单词 | +| `ctrl+d` | 删除光标所在位置的字符 | +| `ctrl+k` | 删除从光标到行尾的内容 | +| `ctrl+u` | 删除从光标到行首的内容 | +| `ctrl+w` | 删除前一个单词 | +| `alt+d` | 删除后一个单词 | +| `ctrl+t` | 交换光标前后的字符 | +| `ctrl+g` | 取消弹出窗口 / 中止正在运行的响应 | --- ## Shift+Enter -默认情况下,某些终端不发送带有 Enter 的修饰键。您可能需要配置终端发送 `Shift+Enter` 作为转义序列。 +某些终端默认不会发送带修饰键的 Enter 键。您可能需要配置终端将 `Shift+Enter` 作为转义序列发送。 ### Windows Terminal -打开您的 `settings.json`: +打开您的 `settings.json` 文件,路径为: ``` %LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json ``` -将其添加到根级 `actions` 数组: +将以下内容添加到根级 `actions` 数组中: ```json "actions": [ @@ -178,7 +178,7 @@ opencode 桌面应用程序提示输入支持常见的 Readline/Emacs 风格的 ] ``` -将其添加到根级 `keybindings` 数组: +将以下内容添加到根级 `keybindings` 数组中: ```json "keybindings": [ @@ -189,4 +189,4 @@ opencode 桌面应用程序提示输入支持常见的 Readline/Emacs 风格的 ] ``` -保存文件并重新启动 Windows Terminal 或打开新选项卡。 +保存文件并重启 Windows Terminal,或打开一个新标签页。 diff --git a/packages/web/src/content/docs/zh-cn/lsp.mdx b/packages/web/src/content/docs/zh-cn/lsp.mdx index cc81810cc79f..57b812190215 100644 --- a/packages/web/src/content/docs/zh-cn/lsp.mdx +++ b/packages/web/src/content/docs/zh-cn/lsp.mdx @@ -1,71 +1,71 @@ --- title: LSP 服务器 -description: opencode 与您的 LSP 服务器集成。 +description: OpenCode 与你的 LSP 服务器集成。 --- -opencode 与您的语言服务器协议 (LSP) 集成,以帮助 LLM 与您的代码库交互。它使用诊断向 LLM 提供反馈。 +OpenCode 与你的语言服务器协议(LSP)集成,帮助 LLM 与你的代码库进行交互。它利用诊断信息向 LLM 提供反馈。 --- -## 内置 - -opencode 附带了多种适用于流行语言的内置 LSP 服务器: - -| LSP 服务器 | 扩展名 | 要求 | -| ------------------ | ------------------------------------------------------------------- | ------------------------------------------------ | -| astro | .astro | Astro 项目自动安装 | -| bash | .sh, .bash, .zsh, .ksh | 自动安装 bash-language-server | -| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | 自动安装 C/C++ 项目 | -| csharp | .cs | `.NET SDK` 已安装 | -| clojure-lsp | .clj, .cljs, .cljc, .edn | `clojure-lsp` 命令可用 | -| dart | .dart | `dart` 命令可用 | -| deno | .ts, .tsx, .js, .jsx, .mjs | `deno` 命令可用(自动检测 deno.json/deno.jsonc) | -| elixir-ls | .ex, .exs | `elixir` 命令可用 | -| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue | `eslint` 项目中的依赖项 | -| fsharp | .fs, .fsi, .fsx, .fsscript | `.NET SDK` 已安装 | -| gleam | .gleam | `gleam` 命令可用 | -| gopls | .go | `go` 命令可用 | -| hls | .hs, .lhs | `haskell-language-server-wrapper` 命令可用 | -| jdtls | .java | `Java SDK (version 21+)` 已安装 | -| kotlin-ls | .kt, .kts | Kotlin 项目的自动安装 | -| lua-ls | .lua | 自动安装 Lua 项目 | -| nixd | .nix | `nixd` 命令可用 | -| ocaml-lsp | .ml, .mli | `ocamllsp` 命令可用 | -| oxlint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue, .astro, .svelte | `oxlint` 项目中的依赖项 | -| php intelephense | .php | PHP 项目的自动安装 | -| prisma | .prisma | `prisma` 命令可用 | -| pyright | .py, .pyi | `pyright` 依赖项已安装 | -| ruby-lsp (rubocop) | .rb, .rake, .gemspec, .ru | `ruby` 和 `gem` 命令可用 | -| rust | .rs | `rust-analyzer` 命令可用 | -| sourcekit-lsp | .swift, .objc, .objcpp | `swift` 已安装(`xcode` 在 macOS 上) | -| svelte | .svelte | Svelte 项目的自动安装 | -| terraform | .tf, .tfvars | 从 GitHub 版本自动安装 | -| tinymist | .typ, .typc | 从 GitHub 版本自动安装 | -| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` 项目中的依赖项 | -| vue | .vue | Vue 项目自动安装 | -| yaml-ls | .yaml, .yml | 自动安装 Red Hat yaml-language-server | -| zls | .zig, .zon | `zig` 命令可用 | - -当检测到上述文件扩展名之一并且满足要求时,LSP 服务器将自动启用。 +## 内置支持 + +OpenCode 内置了多种适用于主流语言的 LSP 服务器: + +| LSP 服务器 | 扩展名 | 要求 | +| ------------------ | ------------------------------------------------------------------- | ----------------------------------------------------- | +| astro | .astro | 为 Astro 项目自动安装 | +| bash | .sh, .bash, .zsh, .ksh | 自动安装 bash-language-server | +| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | 为 C/C++ 项目自动安装 | +| csharp | .cs | 需要已安装 `.NET SDK` | +| clojure-lsp | .clj, .cljs, .cljc, .edn | 需要 `clojure-lsp` 命令可用 | +| dart | .dart | 需要 `dart` 命令可用 | +| deno | .ts, .tsx, .js, .jsx, .mjs | 需要 `deno` 命令可用(自动检测 deno.json/deno.jsonc) | +| elixir-ls | .ex, .exs | 需要 `elixir` 命令可用 | +| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue | 项目中需要 `eslint` 依赖 | +| fsharp | .fs, .fsi, .fsx, .fsscript | 需要已安装 `.NET SDK` | +| gleam | .gleam | 需要 `gleam` 命令可用 | +| gopls | .go | 需要 `go` 命令可用 | +| hls | .hs, .lhs | 需要 `haskell-language-server-wrapper` 命令可用 | +| jdtls | .java | 需要已安装 `Java SDK (version 21+)` | +| kotlin-ls | .kt, .kts | 为 Kotlin 项目自动安装 | +| lua-ls | .lua | 为 Lua 项目自动安装 | +| nixd | .nix | 需要 `nixd` 命令可用 | +| ocaml-lsp | .ml, .mli | 需要 `ocamllsp` 命令可用 | +| oxlint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue, .astro, .svelte | 项目中需要 `oxlint` 依赖 | +| php intelephense | .php | 为 PHP 项目自动安装 | +| prisma | .prisma | 需要 `prisma` 命令可用 | +| pyright | .py, .pyi | 需要已安装 `pyright` 依赖 | +| ruby-lsp (rubocop) | .rb, .rake, .gemspec, .ru | 需要 `ruby` 和 `gem` 命令可用 | +| rust | .rs | 需要 `rust-analyzer` 命令可用 | +| sourcekit-lsp | .swift, .objc, .objcpp | 需要已安装 `swift`(macOS 上为 `xcode`) | +| svelte | .svelte | 为 Svelte 项目自动安装 | +| terraform | .tf, .tfvars | 从 GitHub releases 自动安装 | +| tinymist | .typ, .typc | 从 GitHub releases 自动安装 | +| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | 项目中需要 `typescript` 依赖 | +| vue | .vue | 为 Vue 项目自动安装 | +| yaml-ls | .yaml, .yml | 自动安装 Red Hat yaml-language-server | +| zls | .zig, .zon | 需要 `zig` 命令可用 | + +当检测到上述文件扩展名且满足相应要求时,LSP 服务器会自动启用。 :::note -您可以通过将 `OPENCODE_DISABLE_LSP_DOWNLOAD` 环境变量设置为 `true` 来取消自动 LSP 服务器下载。 +你可以将 `OPENCODE_DISABLE_LSP_DOWNLOAD` 环境变量设置为 `true` 来禁用 LSP 服务器的自动下载。 ::: --- -## 它是如何工作的 +## 工作原理 -当 opencode 打开一个文件时,它: +当 opencode 打开一个文件时,它会: -1. 根据所有启用的 LSP 服务器检查文件扩展名。 -2. 如果尚未运行,则启动相应的 LSP 服务器。 +1. 将文件扩展名与所有已启用的 LSP 服务器进行匹配。 +2. 如果对应的 LSP 服务器尚未运行,则自动启动它。 --- ## 配置 -您可以通过 opencode 配置中的 `lsp` 部分自定义 LSP 服务器。 +你可以通过 opencode 配置文件中的 `lsp` 部分来自定义 LSP 服务器。 ```json title="opencode.json" { @@ -74,23 +74,23 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: } ``` -每个 LSP 服务器支持以下功能: +每个 LSP 服务器支持以下配置项: -| 属性 | 类型 | 描述 | -| ---------------- | -------- | ----------------------------------- | -| `disabled` | boolean | 将其设置为 `true` 以禁用 LSP 服务器 | -| `command` | string[] | 启动 LSP 服务器的命令 | -| `extensions` | string[] | 此 LSP 服务器应处理的文件扩展名 | -| `env` | object | 启动服务器时设置的环境变量 | -| `initialization` | object | 发送到 LSP 服务器的初始化选项 | +| 属性 | 类型 | 描述 | +| ---------------- | -------- | --------------------------------- | +| `disabled` | boolean | 设置为 `true` 可禁用该 LSP 服务器 | +| `command` | string[] | 启动 LSP 服务器的命令 | +| `extensions` | string[] | 该 LSP 服务器需要处理的文件扩展名 | +| `env` | object | 启动服务器时设置的环境变量 | +| `initialization` | object | 发送给 LSP 服务器的初始化选项 | -让我们看一些例子。 +下面来看一些示例。 --- ### 环境变量 -启动 LSP 服务器时使用 `env` 参数设置环境变量: +使用 `env` 属性在启动 LSP 服务器时设置环境变量: ```json title="opencode.json" {5-7} { @@ -109,7 +109,7 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: ### 初始化选项 -使用 `initialization` 属性将初始化选项传递给 LSP 服务器。这些是在 LSP `initialize` 请求发送期间的服务器特定设置: +使用 `initialization` 属性向 LSP 服务器传递初始化选项。这些是在 LSP `initialize` 请求期间发送的服务器特定设置: ```json title="opencode.json" {5-9} { @@ -127,14 +127,14 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: ``` :::note -初始化选项因 LSP 服务器而异。检查 LSP 服务器的文档以获得可用选项。 +初始化选项因 LSP 服务器而异。请查阅你所使用的 LSP 服务器的文档以了解可用选项。 ::: --- ### 禁用 LSP 服务器 -要全局禁用 **所有** LSP 服务,将 `lsp` 设置为 `false`: +要全局禁用**所有** LSP 服务器,将 `lsp` 设置为 `false`: ```json title="opencode.json" {3} { @@ -143,7 +143,7 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: } ``` -要禁用 **特定** LSP 服务器,将 `disabled` 设置为 `true`: +要禁用**特定的** LSP 服务器,将 `disabled` 设置为 `true`: ```json title="opencode.json" {5} { @@ -160,7 +160,7 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: ### 自定义 LSP 服务器 -您可以通过指定命令和文件扩展名来添加自定义 LSP 服务器: +你可以通过指定命令和文件扩展名来添加自定义 LSP 服务器: ```json title="opencode.json" {4-7} { @@ -176,13 +176,13 @@ opencode 附带了多种适用于流行语言的内置 LSP 服务器: --- -## 其他信息 +## 补充信息 ### PHP Intelephense -PHP Intelephense 通过许可证密钥提供高级功能。您可以通过将(仅)密钥放置在以下位置的文本文件中来提供许可证密钥: +PHP Intelephense 通过许可证密钥提供高级功能。你可以将许可证密钥单独放在以下路径的文本文件中: -- 在 macOS/Linux 上:`$HOME/intelephense/license.txt` -- 在 Windows 上:`%USERPROFILE%/intelephense/license.txt` +- macOS/Linux:`$HOME/intelephense/license.txt` +- Windows:`%USERPROFILE%/intelephense/license.txt` -该文件应仅包含许可证密钥,不包含其他内容。 +该文件应仅包含许可证密钥,不要添加其他任何内容。 diff --git a/packages/web/src/content/docs/zh-cn/mcp-servers.mdx b/packages/web/src/content/docs/zh-cn/mcp-servers.mdx index 6a5fe2075dbc..cac8778d485b 100644 --- a/packages/web/src/content/docs/zh-cn/mcp-servers.mdx +++ b/packages/web/src/content/docs/zh-cn/mcp-servers.mdx @@ -1,29 +1,29 @@ --- title: MCP 服务器 -description: 添加本地和远程MCP工具。 +description: 添加本地和远程 MCP 工具。 --- -您可以使用“模型上下文协议”或MCP将外部工具添加到opencode。opencode支持本地和远程服务器。 +你可以通过 _Model Context Protocol_(MCP)为 OpenCode 添加外部工具。OpenCode 同时支持本地和远程服务器。 -添加使用后,MCP工具将自动与内置工具一起供LLM。 +添加后,MCP 工具会自动与内置工具一起提供给 LLM 使用。 --- #### 注意事项 -当您使用 MCP 服务器时,它会添加到上下文中。如果您有很多工具,这会很快增加。因此,我们建议您选择使用哪些 MCP 服务器。 +使用 MCP 服务器时,它会占用上下文空间。如果你启用了大量工具,上下文消耗会迅速增加。因此,我们建议谨慎选择要使用的 MCP 服务器。 :::tip -MCP服务器会添加到您的上下文中,因此您需要小心启用哪些服务器。 +MCP 服务器会占用你的上下文空间,所以请谨慎选择启用哪些服务器。 ::: -某些MCP服务器(例如GitHub MCP服务器)往往会添加大量代币,并且很容易超出上下文限制。 +某些 MCP 服务器(例如 GitHub MCP 服务器)往往会消耗大量 Token,很容易超出上下文限制。 --- ## 启用 -您可以在`mcp`下的[opencode配置](https://opencode.ai/docs/config/)中定义MCP服务器。为每个MCP添加唯一的名称。当提示LLM时,您可以通过名称引用该MCP。 +你可以在 [OpenCode 配置](https://opencode.ai/docs/config/)的 `mcp` 字段下定义 MCP 服务器。为每个 MCP 指定一个唯一的名称,在提示词中可以通过该名称来引用对应的 MCP。 ```jsonc title="opencode.jsonc" {6} { @@ -40,15 +40,15 @@ MCP服务器会添加到您的上下文中,因此您需要小心启用哪些 } ``` -您还可以通过将`enabled`设置为`false`来取消服务器。如果您想暂时取消服务器而不将其从配置中删除,这非常有用。 +你也可以将 `enabled` 设置为 `false` 来禁用某个服务器。当你想临时禁用某个服务器而不将其从配置中移除时,这个选项非常有用。 --- ### 覆盖远程默认值 -组织可以通过其 `.well-known/opencode` 端点提供默认的 MCP 服务器。这些服务器可能默认被禁用,允许用户选择他们需要的服务器。 +组织可以通过其 `.well-known/opencode` 端点提供默认的 MCP 服务器。这些服务器可能默认处于禁用状态,允许用户按需启用。 -要从组织的远程特定启用服务器,请使用配置 `enabled: true` 将其添加到本地配置: +要启用组织远程配置中的某个服务器,请在本地配置中添加该服务器并设置 `enabled: true`: ```json title="opencode.json" { @@ -63,13 +63,13 @@ MCP服务器会添加到您的上下文中,因此您需要小心启用哪些 } ``` -您的本地配置值会覆盖远程默认值。有关更多详细信息,请参阅[配置优先级](/docs/config#precedence-order)。 +本地配置值会覆盖远程默认值。详情请参阅[配置优先级](/docs/config#precedence-order)。 --- ## 本地 -使用`type`将本地MCP服务器添加到MCP对像中的`"local"`。 +通过在 MCP 对象中将 `type` 设置为 `"local"` 来添加本地 MCP 服务器。 ```jsonc title="opencode.jsonc" {15} { @@ -88,9 +88,9 @@ MCP服务器会添加到您的上下文中,因此您需要小心启用哪些 } ``` -该命令是本地MCP服务器的启动方式。您还可以确定环境变量列表。 +`command` 用于指定本地 MCP 服务器的启动命令。你还可以传入一组环境变量。 -例如,以下是添加测试 [`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) MCP 服务器的方法。 +例如,以下是添加测试用的 [`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) MCP 服务器的方法。 ```jsonc title="opencode.jsonc" { @@ -104,7 +104,7 @@ MCP服务器会添加到您的上下文中,因此您需要小心启用哪些 } ``` -要使用它,我可以将 `use the mcp_everything tool` 添加到我的提示中。 +要使用它,可以在提示词中添加 `use the mcp_everything tool`。 ```txt "mcp_everything" use the mcp_everything tool to add the number 3 and 4 @@ -116,19 +116,19 @@ use the mcp_everything tool to add the number 3 and 4 以下是配置本地 MCP 服务器的所有选项。 -| 选项 | 类型 | 必填 | 描述 | -| ------------- | ------ | ---- | -------------------------------------------------------------- | -| `type` | 字符串 | 是 | MCP 服务器连接类型,必须是`"local"`。 | -| `command` | 数据库 | 是 | 运行 MCP 服务器的命令和参数。 | -| `environment` | 对象 | | 运行服务器时设置的环境变量。 | -| `enabled` | 布尔 | | 在启动时启用或禁用MCP 服务器。 | -| `timeout` | 数量 | | 从MCP服务器获取工具的超时(以毫秒为单位)。默认为5000(5秒)。 | +| 选项 | 类型 | 必填 | 描述 | +| ------------- | ------ | ---- | ----------------------------------------------------------------- | +| `type` | 字符串 | 是 | MCP 服务器连接类型,必须为 `"local"`。 | +| `command` | 数组 | 是 | 运行 MCP 服务器的命令及参数。 | +| `environment` | 对象 | | 运行服务器时设置的环境变量。 | +| `enabled` | 布尔值 | | 启动时启用或禁用该 MCP 服务器。 | +| `timeout` | 数字 | | 从 MCP 服务器获取工具的超时时间(毫秒)。默认为 5000(即 5 秒)。 | --- ## 远程 -通过将`type`设置为ZZPH1Z添加远程MCP服务器。 +通过将 `type` 设置为 `"remote"` 来添加远程 MCP 服务器。 ```json title="opencode.json" { @@ -146,36 +146,36 @@ use the mcp_everything tool to add the number 3 and 4 } ``` -`"remote"` 是远程MCP服务器的URL,使用`url`选项您可以创建标头列表。 +`url` 是远程 MCP 服务器的地址,通过 `headers` 选项可以传入一组请求头。 --- #### 选项 -| 选项 | 类型 | 必填 | 描述 | -| ---------- | ------ | ---- | -------------------------------------------------------------- | -| `headers` | 字符串 | 是 | MCP 服务器连接类型,必须是`type`。 | -| `"remote"` | 字符串 | 是 | 远程 MCP 服务器的 URL。 | -| `url` | 布尔 | | 在启动时启用或禁用MCP 服务器。 | -| `enabled` | 对象 | | 随请求一起发送的标头。 | -| `headers` | 对象 | | OAuth 身份验证。请参阅下面的配置[开放认证](#oauth) 部分。 | -| `oauth` | 数量 | | 从MCP服务器获取工具的超时(以毫秒为单位)。默认为5000(5秒)。 | +| 选项 | 类型 | 必填 | 描述 | +| --------- | ------ | ---- | ----------------------------------------------------------------- | +| `type` | 字符串 | 是 | MCP 服务器连接类型,必须为 `"remote"`。 | +| `url` | 字符串 | 是 | 远程 MCP 服务器的 URL。 | +| `enabled` | 布尔值 | | 启动时启用或禁用该 MCP 服务器。 | +| `headers` | 对象 | | 随请求发送的请求头。 | +| `oauth` | 对象 | | OAuth 身份验证配置。详见下方 [OAuth](#oauth) 部分。 | +| `timeout` | 数字 | | 从 MCP 服务器获取工具的超时时间(毫秒)。默认为 5000(即 5 秒)。 | --- ## OAuth -opencode自动处理远程MCP服务器的OAuth身份验证。当服务器需要身份验证时,opencode将: +OpenCode 会自动处理远程 MCP 服务器的 OAuth 身份验证。当服务器需要身份验证时,OpenCode 将: 1. 检测 401 响应并启动 OAuth 流程 -2. 如果服务器支持,请使用**动态客户端注册 (RFC 7591)** -3. 安全地存储Tokens以供将来的请求 +2. 在服务器支持的情况下使用**动态客户端注册(RFC 7591)** +3. 安全地存储 Token 以供后续请求使用 --- -### 自动 +### 自动认证 -对于大多数支持 OAuth 的 MCP 配置服务器,不需要特殊配置。只需远程服务器: +对于大多数支持 OAuth 的 MCP 服务器,无需特殊配置。只需配置远程服务器即可: ```json title="opencode.json" { @@ -189,13 +189,13 @@ opencode自动处理远程MCP服务器的OAuth身份验证。当服务器需要 } ``` -如果服务器需要身份验证,opencode 将在您第一次尝试使用它时提示您进行身份验证。如果没有,您可以使用 `timeout`[手动触发流量](#authenticating)。 +如果服务器需要身份验证,OpenCode 会在你首次使用时提示你进行认证。你也可以使用 `opencode mcp auth ` [手动触发认证流程](#authenticating)。 --- ### 预注册 -如果您有来自MCP服务器强大的客户端,则可以配置它们: +如果你已经从 MCP 服务器提供商处获得了客户端凭据,可以直接配置: ```json title="opencode.json" {7-11} { @@ -218,33 +218,33 @@ opencode自动处理远程MCP服务器的OAuth身份验证。当服务器需要 ### 身份验证 -您可以手动触发身份验证或管理凭据。 +你可以手动触发身份验证或管理凭据。 -使用特定MCP服务器进行身份验证: +对特定 MCP 服务器进行身份验证: ```bash opencode mcp auth my-oauth-server ``` -列出所有MCP服务器及其身份验证状态: +列出所有 MCP 服务器及其认证状态: ```bash opencode mcp list ``` -删除存储的凭据: +删除已存储的凭据: ```bash opencode mcp logout my-oauth-server ``` -`opencode mcp auth ` 命令将打开您的浏览器进行授权。授权后,opencode Tokens安全地存储在 `mcp auth` 中。 +`mcp auth` 命令会打开浏览器进行授权。授权完成后,OpenCode 会将 Token 安全地存储在 `~/.local/share/opencode/mcp-auth.json` 中。 --- #### 禁用 OAuth -如果要禁用服务器的自动OAuth(例如,对于使用API密钥的服务器),则`~/.local/share/opencode/mcp-auth.json`设置为`oauth`: +如果你想为某个服务器禁用自动 OAuth(例如,该服务器使用 API 密钥而非 OAuth),可以将 `oauth` 设置为 `false`: ```json title="opencode.json" {7} { @@ -266,38 +266,38 @@ opencode mcp logout my-oauth-server #### OAuth 选项 -| 选项 | 类型 | 描述 | -| -------------- | --------------- | --------------------------------------------------- | -| `false` | 对象 \| `oauth` | OAuth 配置对象,或 `false` 以取消 OAuth 自动检测。 | -| `clientId` | 字符串 | OAuth 客户端 ID。如果未提供,将尝试动态客户端注册。 | -| `clientSecret` | 字符串 | OAuth客户端密钥(如果需要授权服务器)。 | -| `scope` | 字符串 | 授权期间请求的 OAuth 范围。 | +| 选项 | 类型 | 描述 | +| -------------- | --------------- | ------------------------------------------------------ | +| `oauth` | 对象 \| `false` | OAuth 配置对象,或设为 `false` 以禁用 OAuth 自动检测。 | +| `clientId` | 字符串 | OAuth 客户端 ID。如果未提供,将尝试动态客户端注册。 | +| `clientSecret` | 字符串 | OAuth 客户端密钥(如果授权服务器要求提供)。 | +| `scope` | 字符串 | 授权时请求的 OAuth 作用域。 | #### 调试 -如果远程MCP服务器无法进行身份验证,您可以通过以下方式诊断问题: +如果远程 MCP 服务器身份验证失败,你可以通过以下方式诊断问题: ```bash -# View auth status for all OAuth-capable servers +# 查看所有支持 OAuth 的服务器的认证状态 opencode mcp auth list -# Debug connection and OAuth flow for a specific server +# 调试特定服务器的连接和 OAuth 流程 opencode mcp debug my-oauth-server ``` -`mcp debug`命令显示当前身份验证状态、测试HTTP连接并尝试OAuth发现流程。 +`mcp debug` 命令会显示当前认证状态、测试 HTTP 连接,并尝试执行 OAuth 发现流程。 --- ## 管理 -您的 MCP 可以作为 opencode 中的工具以及内置工具使用。,您可以像任何其他工具一样通过 opencode 配置来管理它们。 +你的 MCP 在 OpenCode 中作为工具使用,与内置工具并列。因此,你可以像管理其他工具一样,通过 OpenCode 配置来管理它们。 --- ### 全局 -这意味著您可以全局启用或禁用它们。 +你可以全局启用或禁用 MCP 工具。 ```json title="opencode.json" {14} { @@ -318,7 +318,7 @@ opencode mcp debug my-oauth-server } ``` -我们还可以使用 glob 模式来取消所有匹配的 MCP。 +也可以使用 glob 模式来禁用所有匹配的 MCP。 ```json title="opencode.json" {14} { @@ -339,16 +339,16 @@ opencode mcp debug my-oauth-server } ``` -这里我们使用 glob 模式 `my-mcp*` 来取消所有 MCP。 +这里使用 glob 模式 `my-mcp*` 来禁用所有 MCP。 --- -### 每个代理人 +### 按代理配置 -如果您有大量 MCP 服务器,您可以选择为每个代理启用它们并全局取消它们。因此: +如果你有大量 MCP 服务器,可以选择全局禁用它们,然后仅在特定代理中启用。具体做法: -1. 全局禁用它作为工具。 -2. 在您的[代理配置](/docs/agents#tools)中,启用MCP作为服务器工具。 +1. 全局禁用该工具。 +2. 在[代理配置](/docs/agents#tools)中,将 MCP 服务器作为工具启用。 ```json title="opencode.json" {11, 14-18} { @@ -375,16 +375,16 @@ opencode mcp debug my-oauth-server --- -#### 全局模式 +#### Glob 模式 -glob 模式使用简单的正则表达式 globbing 模式: +glob 模式使用简单的正则通配符规则: - `*` 匹配零个或多个任意字符(例如,`"my-mcp*"` 匹配 `my-mcp_search`、`my-mcp_list` 等) -- `?` 恰好匹配一个字符 -- 所有其他字符均按字面意思匹配 +- `?` 匹配恰好一个字符 +- 其他字符按字面值匹配 :::note -MCP服务器工具以名称服务器作为出口进行注册,要因此禁用服务器的所有工具,只需使用: +MCP 服务器工具在注册时以服务器名称作为前缀,因此要禁用某个服务器的所有工具,只需使用: ``` "mymcpservername_*": false @@ -396,13 +396,13 @@ MCP服务器工具以名称服务器作为出口进行注册,要因此禁用 ## 示例 -以下是一些常见的 MCP 服务器的示例。如果您想记录其他服务器,您可以提交 PR。 +以下是一些常见 MCP 服务器的配置示例。如果你想记录其他服务器的用法,欢迎提交 PR。 --- -### 哨兵 +### Sentry -添加[哨兵MCP服务器](https://mcp.sentry.dev)以与您的Sentry项目和问题进行交互。 +添加 [Sentry MCP 服务器](https://mcp.sentry.dev) 以与你的 Sentry 项目和问题进行交互。 ```json title="opencode.json" {4-8} { @@ -417,15 +417,15 @@ MCP服务器工具以名称服务器作为出口进行注册,要因此禁用 } ``` -添加配置后,使用Sentry进行身份验证: +添加配置后,使用 Sentry 进行身份验证: ```bash opencode mcp auth sentry ``` -这将打开一个浏览器窗口以完成 OAuth 流程并将 opencode 连接到您的 Sentry 账户。 +这会打开浏览器窗口完成 OAuth 流程,将 OpenCode 连接到你的 Sentry 账户。 -通过身份验证后,您可以在提示中使用Sentry工具来查询问题、项目和错误数据。 +认证完成后,你可以在提示词中使用 Sentry 工具来查询问题、项目和错误数据。 ```txt "use sentry" Show me the latest unresolved issues in my project. use sentry @@ -433,9 +433,9 @@ Show me the latest unresolved issues in my project. use sentry --- -### 背景7 +### Context7 -添加[Context7 MCP 服务器](https://github.com/upstash/context7) 以搜索文档。 +添加 [Context7 MCP 服务器](https://github.com/upstash/context7) 以搜索文档。 ```json title="opencode.json" {4-7} { @@ -449,7 +449,7 @@ Show me the latest unresolved issues in my project. use sentry } ``` -如果您注册了免费帐户,则可以使用 API 轴并获得更高的速率限制。 +如果你注册了免费账户,可以使用 API 密钥来获得更高的速率限制。 ```json title="opencode.json" {7-9} { @@ -466,15 +466,15 @@ Show me the latest unresolved issues in my project. use sentry } ``` -这里我们假设您设置了 `CONTEXT7_API_KEY` 环境变量。 +这里假设你已经设置了 `CONTEXT7_API_KEY` 环境变量。 -将 `use context7` 添加到提示中以使用 Context7 MCP 服务器。 +在提示词中添加 `use context7` 即可使用 Context7 MCP 服务器。 ```txt "use context7" Configure a Cloudflare Worker script to cache JSON API responses for five minutes. use context7 ``` -或者,您可以将类似的内容添加到您的[代理.md](/docs/rules/)。 +你也可以在 [AGENTS.md](/docs/rules/) 中添加类似的规则。 ```md title="AGENTS.md" When you need to search docs, use `context7` tools. @@ -482,9 +482,9 @@ When you need to search docs, use `context7` tools. --- -### Vercel 的 Grep +### Grep by Vercel -添加 [Vercel 的 Grep](https://grep.app) MCP 服务器正在搜索 GitHub 上的代码片段。 +添加 [Grep by Vercel](https://grep.app) MCP 服务器以搜索 GitHub 上的代码片段。 ```json title="opencode.json" {4-7} { @@ -498,13 +498,13 @@ When you need to search docs, use `context7` tools. } ``` -由于我们将 MCP 服务器命名为 `gh_grep`,因此您可以将 `use the gh_grep tool` 添加到提示中以便代理使用它。 +由于我们将 MCP 服务器命名为 `gh_grep`,你可以在提示词中添加 `use the gh_grep tool` 来让代理使用它。 ```txt "use the gh_grep tool" What's the right way to set a custom domain in an SST Astro component? use the gh_grep tool ``` -或者,您可以将类似的内容添加到您的[代理.md](/docs/rules/)。 +你也可以在 [AGENTS.md](/docs/rules/) 中添加类似的规则。 ```md title="AGENTS.md" If you are unsure how to do something, use `gh_grep` to search code examples from GitHub. diff --git a/packages/web/src/content/docs/zh-cn/models.mdx b/packages/web/src/content/docs/zh-cn/models.mdx index 84d542efddd4..5399a59abd62 100644 --- a/packages/web/src/content/docs/zh-cn/models.mdx +++ b/packages/web/src/content/docs/zh-cn/models.mdx @@ -3,21 +3,21 @@ title: 模型 description: 配置 LLM 提供商和模型。 --- -opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev) 支持 **75+ LLM 提供商**,并支持运行本地模型。 +OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev) 支持 **75+ LLM 提供商**,并支持运行本地模型。 --- ## 提供商 -默认会预加载大多数流行的提供商。如果您已通过 `/connect` 命令添加了提供商的凭据,那么它们将在您启动 opencode 时可用。 +大多数热门提供商已默认预加载。如果你通过 `/connect` 命令添加了提供商的凭据,它们将在你启动 OpenCode 时自动可用。 -了解有关[提供商](/docs/providers) 的更多信息。 +了解更多关于[提供商](/docs/providers)的信息。 --- ## 选择模型 -配置完提供商后,您可以通过输入以下内容来选择您想要的模型: +配置好提供商后,你可以通过输入以下命令来选择想要使用的模型: ```bash frame="none" /models @@ -27,15 +27,15 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de ## 推荐模型 -那里有很多模型,每周都会有新模型问世。 +市面上有非常多的模型,每周都有新模型发布。 :::tip -考虑使用我们推荐的模型之一。 +建议使用我们推荐的模型。 ::: -然而,既擅长生成代码又擅长工具调用的只有少数。 +然而,真正擅长代码生成和工具调用的模型只有少数几个。 -以下是与 opencode 配合良好的几个模型,排名不分前面。(这不是好看的列表,也不一定是最新的): +以下是与 OpenCode 配合良好的几个模型,排名不分先后(此列表并非详尽无遗,也不一定是最新的): - GPT 5.2 - GPT 5.1 Codex @@ -46,10 +46,9 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de --- -## 设置默认值 +## 设置默认模型 -要将其中之一设置为默认模型,您可以在您的 -打开代码配置。 +要将某个模型设为默认模型,可以在 OpenCode 配置中设置 `model` 字段。 ```json title="opencode.json" {3} { @@ -58,15 +57,15 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de } ``` -这里完整的ID是`provider_id/model_id`。例如,如果您使用[OpenCode Zen](/docs/zen),则您将使用`opencode/gpt-5.1-codex`来表示GPT 5.1 Codex。 +这里完整的 ID 格式为 `provider_id/model_id`。例如,如果你使用 [OpenCode Zen](/docs/zen),则 GPT 5.1 Codex 对应的值为 `opencode/gpt-5.1-codex`。 -如果您配置了[定制生产](/docs/providers#custom),则`provider_id` 是配​​置中`provider` 部分的按键,`model_id` 是`provider.models` 中的按键。 +如果你配置了[自定义提供商](/docs/providers#custom),`provider_id` 是配置中 `provider` 部分的键名,`model_id` 是 `provider.models` 中的键名。 --- ## 配置模型 -您可以通过 config.json 全局配置模型的选项。 +你可以通过配置文件全局配置模型的选项。 ```jsonc title="opencode.jsonc" {7-12,19-24} { @@ -100,12 +99,12 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de } ``` -在这里,我们为两个内置模型配置全局设置:`gpt-5`(通过 `openai` 提供商访问)和 `claude-sonnet-4-20250514`(通过 `anthropic` 提供商访问)。 -内置结构和模型名称可以在[Models.dev](https://models.dev) 上找到。 +这里我们为两个内置模型配置了全局设置:通过 `openai` 提供商访问的 `gpt-5`,以及通过 `anthropic` 提供商访问的 `claude-sonnet-4-20250514`。 +内置的提供商和模型名称可以在 [Models.dev](https://models.dev) 上查阅。 -您还可以为您正在使用的任何代理配置这些选项。代理配置会覆盖此处的所有全局选项。 [了解更多](/docs/agents/#additional)。 +你还可以为使用中的任何代理配置这些选项。代理配置会覆盖此处的全局选项。[了解更多](/docs/agents/#additional)。 -你还可以定义扩展内置 variants 的自定义 variants。variants 允许你为同一模型配置不同设置,而无需创建重复条目: +你也可以定义扩展内置变体的自定义变体。变体允许你为同一个模型配置不同的设置,而无需创建重复的条目: ```jsonc title="opencode.jsonc" {6-21} { @@ -137,40 +136,40 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de ## 变体 -许多模型支持具有不同配置的多种变体。opencode附带了流行建设的内置默认变体。 +许多模型支持具有不同配置的多种变体。OpenCode 为热门提供商内置了默认变体。 ### 内置变体 -opencode 附带了许多重大的默认变体: +OpenCode 为许多提供商提供了默认变体: **Anthropic**: -- `high` - 高思维预算(默认) -- `max` - 最大预算规划 +- `high` - 高思考预算(默认) +- `max` - 最大思考预算 **OpenAI**: 因模型而异,但大致如下: -- `none` - 没有推理 -- `minimal` - 最少的推理工作 -- `low` - 推理工作量低 -- `medium` - 中等推理努力 -- `high` - 高推理能力 -- `xhigh` - 极高的推理能力 +- `none` - 无推理 +- `minimal` - 极少推理 +- `low` - 低推理 +- `medium` - 中等推理 +- `high` - 高推理 +- `xhigh` - 超高推理 **Google**: -- `low` - 降低工作量/Tokens预算 -- `high` - 更高的努力/Tokens预算 +- `low` - 较低推理/Token 预算 +- `high` - 较高推理/Token 预算 :::tip -该列表并不全面。许多其他提供商也有内置的默认值。 +此列表并不全面,许多其他提供商也有内置的默认变体。 ::: ### 自定义变体 -您可以覆盖现有变体或添加您自己的变体: +你可以覆盖现有变体或添加自己的变体: ```jsonc title="opencode.jsonc" {7-18} { @@ -197,17 +196,17 @@ opencode 附带了许多重大的默认变体: ### 切换变体 -使用按键绑定`variant_cycle`在变体之间快速切换。 [了解更多](/docs/keybinds)。 +使用快捷键 `variant_cycle` 可以快速在变体之间切换。[了解更多](/docs/keybinds)。 --- ## 加载模型 -当opencode启动时,它会按以下优先顺序检查模型: +OpenCode 启动时,会按以下优先顺序加载模型: -1. `--model` 或 `-m` 配置命令行标志。格式与文件中的相同:`provider_id/model_id`。 +1. `--model` 或 `-m` 命令行标志。格式与配置文件中相同:`provider_id/model_id`。 -2. opencode 配置中的模型列表。 +2. OpenCode 配置中的 model 字段。 ```json title="opencode.json" { @@ -216,8 +215,8 @@ opencode 附带了许多重大的默认变体: } ``` - 这里的格式是`provider/model`。 + 格式为 `provider/model`。 -3. 最后使用的模型。 +3. 上次使用的模型。 -4. 第一个模型使用内部优先级。 +4. 按内部优先级排列的第一个可用模型。 diff --git a/packages/web/src/content/docs/zh-cn/modes.mdx b/packages/web/src/content/docs/zh-cn/modes.mdx index 79c437884f5c..474126c946b8 100644 --- a/packages/web/src/content/docs/zh-cn/modes.mdx +++ b/packages/web/src/content/docs/zh-cn/modes.mdx @@ -1,58 +1,56 @@ --- title: 模式 -description: 不同的模式适用于不同的用例。 +description: 不同模式适用于不同的使用场景。 --- :::caution -现在通过opencode配置中的`agent`选项配置模式。这 -`mode` 选项现已废弃。 [了解更多](/docs/agents)。 +模式现在通过 opencode 配置中的 `agent` 选项进行配置。`mode` 选项已废弃。[了解更多](/docs/agents)。 ::: -opencode 中的模式允许自定义不同的示例行为、工具和提示。 +opencode 中的模式允许你为不同的使用场景自定义行为、工具和提示词。 -它具有两种内置模式:**构建**和**计划**。您可以定制 -这些或通过 opencode 配置配置您自己的。 +opencode 自带两种内置模式:**build** 和 **plan**。你可以自定义这些模式,也可以通过 opencode 配置创建自己的模式。 -您可以在会话期间在模式之间切换或在配置文件中配置它们。 +你可以在会话中切换模式,也可以在配置文件中进行配置。 --- -## 内置 +## 内置模式 -opencode 有两种内置模式。 +opencode 自带两种内置模式。 --- -### 构建 +### Build -构建是启用所有工具的**默认**模式。这是开发工作的标准模式,您需要完全访问文件操作和系统命令。 +Build 是启用了所有工具的**默认**模式。这是进行开发工作的标准模式,你可以完全访问文件操作和系统命令。 --- -### 计划 +### Plan -专为规划和分析而设计的受限模式。在计划模式下,默认情况下禁用以下工具: +Plan 是一种为规划和分析设计的受限模式。在 plan 模式下,以下工具默认被禁用: - `write` - 无法创建新文件 -- `edit` - 无法修改现有文件,位于 `.opencode/plans/*.md` 的用于详细说明计划本身的文件另外 +- `edit` - 无法修改现有文件,但位于 `.opencode/plans/*.md` 的文件除外,用于详细说明计划本身 - `patch` - 无法应用补丁 - `bash` - 无法执行 shell 命令 -当您希望人工智能分析代码、建议更改或创建计划而不对代码库进行任何实际修改时,此模式非常有用。 +当你希望 AI 分析代码、提出修改建议或制定计划,而不对代码库进行任何实际更改时,此模式非常有用。 --- ## 切换 -您可以在会话期间使用 _Tab_ 键在模式之间切换。或者您配置的 `switch_mode` 键绑定。 +你可以在会话中使用 _Tab_ 键切换模式,或者使用你配置的 `switch_mode` 快捷键。 -另请参见:[格式化程序](/docs/formatters)相关代码配置的信息。 +另请参阅:[格式化工具](/docs/formatters)了解代码格式化配置的相关信息。 --- ## 配置 -您可以自定义内置模式或通过配置创建自己的模式。可以通过两种方式配置模式: +你可以自定义内置模式或通过配置创建自己的模式。模式可以通过两种方式进行配置: ### JSON 配置 @@ -85,9 +83,9 @@ opencode 有两种内置模式。 ### Markdown 配置 -您还可以使用 Markdown 文件定义模式。将它们放入: +你还可以使用 Markdown 文件定义模式。将文件放置在以下位置: -- 全球:`~/.config/opencode/modes/` +- 全局:`~/.config/opencode/modes/` - 项目:`.opencode/modes/` ```markdown title="~/.config/opencode/modes/review.md" @@ -110,15 +108,15 @@ You are in code review mode. Focus on: Provide constructive feedback without making direct changes. ``` -Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模式)。 +Markdown 文件名即为模式名称(例如,`review.md` 创建一个名为 `review` 的模式)。 -让我们详细看看这些配置选项。 +下面让我们详细了解这些配置选项。 --- ### 模型 -使用`model`配置覆盖此模式的默认模型。对于使用针对不同任务优化的不同模型很有帮助。例如,更快的规划模型、更强大的实施模型。 +使用 `model` 配置可以覆盖该模式的默认模型。这对于针对不同任务使用不同模型非常有用。例如,规划时使用更快的模型,实现时使用更强大的模型。 ```json title="opencode.json" { @@ -134,7 +132,7 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 ### 温度 -使用`temperature`配置控制AI响应的随机性和创造。较低的值使响应更加集中和确定,而较高的值则增加创造力和可变性。 +使用 `temperature` 配置控制 AI 响应的随机性和创造性。较低的值使响应更加集中和确定性,较高的值则增加创造性和多样性。 ```json title="opencode.json" { @@ -149,11 +147,11 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 } ``` -温度值范围通常为 0.0 到 1.0: +温度值的范围通常为 0.0 到 1.0: -- **0.0-0.2**:响应更集中、确定性更高,适合代码分析和规划 -- **0.3-0.5**:平衡型响应,兼顾稳定性与创造力 -- **0.6-1.0**:响应更有创意和多样性,适合头脑风暴和探索 +- **0.0-0.2**:非常集中且确定性高的响应,适合代码分析和规划 +- **0.3-0.5**:兼顾稳定性与创造力的平衡型响应,适合一般开发任务 +- **0.6-1.0**:更具创造性和多样性的响应,适合头脑风暴和探索性工作 ```json title="opencode.json" { @@ -173,13 +171,13 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 } ``` -如果未指定温度,opencode将使用特定于模型的默认值(大多数模型通常为0,Qwen模型为0.55)。 +如果未指定温度,opencode 将使用模型特定的默认值(大多数模型通常为 0,Qwen 模型为 0.55)。 --- ### 提示词 -使用 `prompt` 配置为模式指定自定义系统提示文件。提示文件应包含特定于该模式用途的指令。 +使用 `prompt` 配置为模式指定自定义系统提示词文件。提示词文件应包含针对该模式用途的具体指令。 ```json title="opencode.json" { @@ -191,14 +189,13 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 } ``` -该路径是相对于配置文件所在位置的。所以这适用于 -全局opencode配置和项目特定配置。 +此路径相对于配置文件所在位置。因此,全局 opencode 配置和项目特定配置均可使用。 --- ### 工具 -使用 `tools` 配置控制模式下可用的工具。您可以通过将特定工具设置为 `true` 或 `false` 来启用或禁用特定工具。 +使用 `tools` 配置控制该模式下可用的工具。你可以将特定工具设置为 `true` 或 `false` 来启用或禁用它们。 ```json { @@ -223,27 +220,27 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 #### 可用工具 -这里是所有可以通过模式配置控制的工具。 +以下是所有可通过模式配置控制的工具。 | 工具 | 描述 | | ----------- | ---------------- | -| `bash` | 执行shell命令 | +| `bash` | 执行 shell 命令 | | `edit` | 修改现有文件 | | `write` | 创建新文件 | | `read` | 读取文件内容 | | `grep` | 搜索文件内容 | | `glob` | 按模式查找文件 | -| `list` | 上市目录内容 | +| `list` | 列出目录内容 | | `patch` | 对文件应用补丁 | | `todowrite` | 管理待办事项列表 | -| `todoread` | 阅读待办事项列表 | +| `todoread` | 读取待办事项列表 | | `webfetch` | 获取网页内容 | --- ## 自定义模式 -您可以通过将自定义模式添加到配置来创建自己的自定义模式。以下是使用这两种方法的示例: +你可以通过在配置中添加自定义模式来创建自己的模式。以下是两种方式的示例: ### 使用 JSON 配置 @@ -268,7 +265,7 @@ Markdown 文件名成为模式名称(例如,`review.md` 创建`review` 模 ### 使用 Markdown 文件 -在`.opencode/modes/`中为项目特定模式创建模式文件,在`~/.config/opencode/modes/`中为全局模式创建模式文件: +在 `.opencode/modes/` 中创建项目特定的模式文件,或在 `~/.config/opencode/modes/` 中创建全局模式文件: ```markdown title=".opencode/modes/debug.md" --- @@ -318,14 +315,14 @@ Priorities: --- -### 使用案例 +### 使用场景 -以下是不同模式的一些常见用例。 +以下是不同模式的一些常见使用场景。 -- **构建模式**:启用所有工具的完整开发工作 -- **计划模式**:分析和计划,无需更改 -- **审阅模式**:使用只读访问权限和文档工具进行代码审阅 -- **调试模式**:专注于启用bash和读取工具的调查 -- **文档模式**:使用文件操作但不使用系统命令的文档编写 +- **Build 模式**:启用所有工具的完整开发工作 +- **Plan 模式**:分析和规划,不做任何更改 +- **Review 模式**:使用只读访问权限加文档工具进行代码审查 +- **Debug 模式**:启用 bash 和读取工具,专注于问题排查 +- **Docs 模式**:支持文件操作但不支持系统命令的文档编写 -您可能还会发现不同的模型适用于不同的用例。 +你可能还会发现不同的模型适用于不同的使用场景。 diff --git a/packages/web/src/content/docs/zh-cn/network.mdx b/packages/web/src/content/docs/zh-cn/network.mdx index 2ba6b2f481eb..8289777a1e10 100644 --- a/packages/web/src/content/docs/zh-cn/network.mdx +++ b/packages/web/src/content/docs/zh-cn/network.mdx @@ -3,13 +3,13 @@ title: 网络 description: 配置代理和自定义证书。 --- -opencode支持企业网络环境的标准代理环境变量和自定义证书。 +OpenCode 支持标准代理环境变量和自定义证书,适用于企业网络环境。 --- ## 代理 -opencode 遵循标准代理环境变量。 +OpenCode 遵循标准代理环境变量。 ```bash # HTTPS proxy (recommended) @@ -23,35 +23,35 @@ export NO_PROXY=localhost,127.0.0.1 ``` :::caution -TUI 与本地 HTTP 服务器通信。您必须绕过此连接的代理以防止路由循环。 +TUI 与本地 HTTP 服务器进行通信。你必须为此连接绕过代理,以防止路由循环。 ::: -您可以使用[CLI 标志](/docs/cli#run)配置服务器的端口和主机名。 +你可以使用 [CLI 标志](/docs/cli#run)来配置服务器的端口和主机名。 --- -### 认证 +### 身份验证 -如果您的代理需要基本身份验证,请在 URL 中包含凭据。 +如果你的代理需要基本身份验证,请在 URL 中包含凭据。 ```bash export HTTPS_PROXY=http://username:password@proxy.example.com:8080 ``` :::caution -避免对密码进行硬编码。使用环境变量或安全凭证存储。 +避免将密码硬编码在代码中。请使用环境变量或安全的凭据存储方式。 ::: -对于需要高级身份验证(例如 NTLM 或 Kerberos)的代理,请考虑使用支持您的身份验证方法的 LLM 网关。 +对于需要高级身份验证(如 NTLM 或 Kerberos)的代理,建议使用支持相应身份验证方式的 LLM 网关。 --- ## 自定义证书 -如果您的企业使用自定义 CA 进行 HTTPS 连接,请配置 opencode 以信任它们。 +如果你的企业使用自定义 CA 进行 HTTPS 连接,请配置 OpenCode 以信任这些证书。 ```bash export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem ``` -这适用于代理连接和直接 API 访问。 +此配置同时适用于代理连接和直接 API 访问。 diff --git a/packages/web/src/content/docs/zh-cn/permissions.mdx b/packages/web/src/content/docs/zh-cn/permissions.mdx index 87bcb62ed0dd..0f608976aa99 100644 --- a/packages/web/src/content/docs/zh-cn/permissions.mdx +++ b/packages/web/src/content/docs/zh-cn/permissions.mdx @@ -1,27 +1,27 @@ --- title: 权限 -description: 控制哪些操作需要批准才能运行。 +description: 控制哪些操作需要审批才能运行。 --- -opencode 使用`permission` 配置来决定给定的操作是否应自动运行、提示您或被阻止。 +OpenCode 使用 `permission` 配置来决定某个操作是否应自动运行、提示你审批,还是被阻止。 -从 `v1.1.1` 开始,旧版配置 `tools` 布尔已被废弃,并已合并到 `permission` 中。仍支持旧版的 `tools` 配置以实现平滑兼容。 +从 `v1.1.1` 开始,旧版 `tools` 布尔配置已被弃用,并已合并到 `permission` 中。旧版 `tools` 配置仍然支持,以保持向后兼容。 --- -## 动作 +## 操作 -每个权限规则解析为以下之一: +每条权限规则解析为以下之一: -- `"allow"` — 尚未批准运行 -- `"ask"` — 提示批准 +- `"allow"` — 无需审批直接运行 +- `"ask"` — 提示审批 - `"deny"` — 阻止该操作 --- ## 配置 -您可以全局设置权限(使用`*`),并覆盖特定工具。 +你可以全局设置权限(使用 `*`),并覆盖特定工具的权限。 ```json title="opencode.json" { @@ -34,7 +34,7 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 } ``` -您还可以一次设置所有权限: +你还可以一次性设置所有权限: ```json title="opencode.json" { @@ -45,9 +45,9 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 --- -## 粒度规则(对象语法) +## 细粒度规则(对象语法) -对于大多数权限,您可以使用对象根据工具输入应用不同的操作。 +对于大多数权限,你可以使用对象来根据工具输入应用不同的操作。 ```json title="opencode.json" { @@ -68,19 +68,19 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 } ``` -规则通过模式匹配进行评估,**最后匹配的规则获胜**。常见的模式是将包罗万象的 `"*"` 规则放在前面,然后再放置更具体的规则。 +规则通过模式匹配进行评估,**最后匹配的规则优先**。常见做法是将通配的 `"*"` 规则放在最前面,更具体的规则放在后面。 ### 通配符 权限模式使用简单的通配符匹配: - `*` 匹配零个或多个任意字符 -- `?` 恰好匹配一个字符 -- 所有其他字符均按字面意思匹配 +- `?` 精确匹配一个字符 +- 所有其他字符按字面值匹配 -### 主目录扩展 +### 主目录展开 -您可以在模式目录中使用 `~` 或 `$HOME` 来引用您的主目录。这对于 [`外部目录`](#external_directory) 规则特别有用。 +你可以在模式开头使用 `~` 或 `$HOME` 来引用你的主目录。这对于 [`external_directory`](#外部目录) 规则特别有用。 - `~/projects/*` -> `/Users/username/projects/*` - `$HOME/projects/*` -> `/Users/username/projects/*` @@ -88,11 +88,11 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 ### 外部目录 -使用 `external_directory` 允许工具调用启动 opencode 的工作目录之外的路径。这适用于任何采用路径作为输入的工具(例如 `read`、`edit`、`list`、`glob`、`grep` 和许多Z`bash` 命令)。 +使用 `external_directory` 允许工具调用访问 OpenCode 启动时工作目录之外的路径。这适用于任何接受路径作为输入的工具(例如 `read`、`edit`、`list`、`glob`、`grep` 以及许多 `bash` 命令)。 -主扩展(如`~/...`)仅影响模式的编写方式。它不会使外部路径成为当前工作空间的一部分,因此仍然必须通过 `external_directory` 允许工作目录之外的路径。 +主目录展开(如 `~/...`)仅影响模式的书写方式。它不会将外部路径纳入当前工作空间,因此工作目录之外的路径仍然必须通过 `external_directory` 来允许。 -例如,这允许访问`~/projects/personal/`下的所有内容: +例如,以下配置允许访问 `~/projects/personal/` 下的所有内容: ```json title="opencode.json" { @@ -105,7 +105,7 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 } ``` -这里允许的任何目录都会继承与当前工作空间默认相同的值。自[`read`默认为`allow`](#defaults)起,也允许读取`external_directory`下面的边界,除非被覆盖。当工具应在这些路径中时添加显式规则,例如在保留读取的同时阻止编辑: +此处允许的任何目录都会继承与当前工作空间相同的默认值。由于 [`read` 默认为 `allow`](#默认值),`external_directory` 下的条目也允许读取,除非另行覆盖。当需要在这些路径中限制某个工具时,请添加显式规则,例如在保留读取的同时阻止编辑: ```json title="opencode.json" { @@ -121,38 +121,38 @@ opencode 使用`permission` 配置来决定给定的操作是否应自动运行 } ``` -将列表重点放在受信任的路径上,并根据其他工具的需要分层额外的允许或拒绝规则(例如`bash`)。 +请将列表限定在受信任的路径上,并根据需要为其他工具(例如 `bash`)叠加额外的允许或拒绝规则。 --- ## 可用权限 -opencode权限由工具名称和一些安全防护措施决定: +OpenCode 的权限以工具名称为键,外加几个安全防护项: -- `read` — 读取文件(与文件路径匹配) -- `edit` — 所有文件修改(头部`edit`、`write`、`patch`、`multiedit`) -- `glob` — 文件通配符(匹配通配符模式) +- `read` — 读取文件(匹配文件路径) +- `edit` — 所有文件修改(涵盖 `edit`、`write`、`patch`、`multiedit`) +- `glob` — 文件通配(匹配通配模式) - `grep` — 内容搜索(匹配正则表达式模式) -- `list` — 列出目录中的文件(与目录路径匹配) -- `bash` — 运行 shell 命令(匹配 `git status --porcelain` 等解析命令) -- `task` — 启动子代理(与子代理类型匹配) -- `skill` — 加载技能(与技能名称匹配) -- `lsp` — 运行 LSP 查询(当前非粒度) +- `list` — 列出目录中的文件(匹配目录路径) +- `bash` — 运行 shell 命令(匹配解析后的命令,如 `git status --porcelain`) +- `task` — 启动子代理(匹配子代理类型) +- `skill` — 加载技能(匹配技能名称) +- `lsp` — 运行 LSP 查询(当前不支持细粒度配置) - `todoread`、`todowrite` — 读取/更新待办事项列表 -- `webfetch` — 获取 URL(与 URL 匹配) -- `websearch`、`codesearch` — 网页/代码搜索(与查询匹配) -- `external_directory` — 当工具访问项目工作目录外部的路径时触发 -- `doom_loop` — 当相同的工具调用相同的输入重复 3 次时触发 +- `webfetch` — 获取 URL(匹配 URL) +- `websearch`、`codesearch` — 网页/代码搜索(匹配查询内容) +- `external_directory` — 当工具访问项目工作目录之外的路径时触发 +- `doom_loop` — 当同一工具调用以相同输入重复 3 次时触发 --- ## 默认值 -如果您未指定任何内容,opencode将从宽松的默认值开始: +如果你未指定任何配置,OpenCode 将使用宽松的默认值: -- 大部分权限默认为`"allow"`。 -- `doom_loop`和`external_directory`默认为`"ask"`。 -- `read` 是 `"allow"`,但 `.env` 文件默认被拒绝: +- 大多数权限默认为 `"allow"`。 +- `doom_loop` 和 `external_directory` 默认为 `"ask"`。 +- `read` 为 `"allow"`,但 `.env` 文件默认被拒绝: ```json title="opencode.json" { @@ -169,24 +169,24 @@ opencode权限由工具名称和一些安全防护措施决定: --- -## “询问”的作用是什么 +## "Ask"的作用 -当 opencode 提示批准时,UI 会提供三种结果: +当 OpenCode 提示审批时,界面提供三种选择: -- `once` — 仅批准此请求 -- `always` — 批准与建议模式匹配的未来请求(对于当前 opencode 会话的其余部分) +- `once` — 仅批准本次请求 +- `always` — 批准与建议模式匹配的后续请求(在当前 OpenCode 会话的剩余时间内有效) - `reject` — 拒绝请求 -`always` 将批准的模式集由该工具提供(例如,bash 批准通常将安全端口(如 `git status*`)列入白名单)。 +`always` 所批准的模式集合由工具提供(例如,bash 审批通常会将安全的命令前缀如 `git status*` 加入白名单)。 --- ## 代理 -您可以覆盖每个代理的权限。代理权限与全局​​配置合并,代理规则优先。 [了解更多](/docs/agents#permissions)关于代理权限。 +你可以为每个代理单独覆盖权限。代理权限会与全局配置合并,且代理规则优先。[了解更多](/docs/agents#permissions)关于代理权限的内容。 :::note -有关更详细的模式匹配示例,请参见上面的 [粒度规则(对象语法)](#granular-rules-object-syntax) 部分。 +有关更详细的模式匹配示例,请参阅上方的[细粒度规则(对象语法)](#细粒度规则对象语法)部分。 ::: ```json title="opencode.json" @@ -217,7 +217,7 @@ opencode权限由工具名称和一些安全防护措施决定: } ``` -您还可以在 Markdown 中配置代理权限: +你还可以在 Markdown 中配置代理权限: ```markdown title="~/.config/opencode/agents/review.md" --- @@ -233,5 +233,5 @@ Only analyze code and suggest changes. ``` :::tip -对参数的命令使用模式匹配。 `"grep *"` 允许 `grep pattern file.txt`,而 `"grep"` 单独会阻止它。像 `git status` 这样的命令适用于默认行为,但在传递参数时需要显式许可(如 `"git status *"`)。 +对带参数的命令使用模式匹配。`"grep *"` 允许执行 `grep pattern file.txt`,而单独的 `"grep"` 则会阻止它。像 `git status` 这样的命令适用于默认行为,但在传递参数时需要显式权限(如 `"git status *"`)。 ::: diff --git a/packages/web/src/content/docs/zh-cn/plugins.mdx b/packages/web/src/content/docs/zh-cn/plugins.mdx index 69810e67c781..0df6d1ee6591 100644 --- a/packages/web/src/content/docs/zh-cn/plugins.mdx +++ b/packages/web/src/content/docs/zh-cn/plugins.mdx @@ -1,21 +1,21 @@ --- title: 插件 -description: 编写您自己的插件来扩展 opencode。 +description: 编写自己的插件来扩展 OpenCode。 --- -插件允许您通过挂钩各种事件和自定义行为来扩展 opencode。您可以创建插件来添加新功能、与外部服务集成或修改 opencode 的默认行为。 +插件允许你通过挂钩各种事件和自定义行为来扩展 OpenCode。你可以创建插件来添加新功能、集成外部服务,或修改 OpenCode 的默认行为。 -例如,查看社区创建的[插件](/docs/ecosystem#plugins)。 +如需了解示例,请查看社区创建的[插件](/docs/ecosystem#plugins)。 --- ## 使用插件 -有两种加载插件的方法。 +有两种方式加载插件。 --- -### 从本地文件 +### 从本地文件加载 将 JavaScript 或 TypeScript 文件放置在插件目录中。 @@ -26,7 +26,7 @@ description: 编写您自己的插件来扩展 opencode。 --- -### 来自 npm +### 从 npm 加载 在配置文件中指定 npm 包。 @@ -37,43 +37,42 @@ description: 编写您自己的插件来扩展 opencode。 } ``` -支持常规和范围的 npm 包。 +支持常规和带作用域的 npm 包。 浏览[生态系统](/docs/ecosystem#plugins)中的可用插件。 --- -### 插件是如何安装的 +### 插件的安装方式 -**npm 插件** 在启动时使用 Bun 自动安装。包及其依赖项缓存在 `~/.cache/opencode/node_modules/` 中。 +**npm 插件**在启动时使用 Bun 自动安装。包及其依赖项会缓存在 `~/.cache/opencode/node_modules/` 中。 -**本地插件**直接从插件目录加载。要使用外部包,您必须在配置目录中创建`package.json`(请参阅[依赖关系](#dependencies)),或将插件发布到npm和[将其添加到您的配置中](/docs/config#plugins)。 +**本地插件**直接从插件目录加载。如果需要使用外部包,你必须在配置目录中创建 `package.json`(参见[依赖项](#dependencies)),或者将插件发布到 npm 并[将其添加到配置中](/docs/config#plugins)。 --- ### 加载顺序 -插件从所有源加载,所有挂钩按顺序运行。加载顺序为: +插件从所有来源加载,所有钩子按顺序执行。加载顺序为: 1. 全局配置 (`~/.config/opencode/opencode.json`) -2. 项目配置(`opencode.json`) -3. 插件全局目录 (`~/.config/opencode/plugins/`) -4. 项目插件目录(`.opencode/plugins/`) +2. 项目配置 (`opencode.json`) +3. 全局插件目录 (`~/.config/opencode/plugins/`) +4. 项目插件目录 (`.opencode/plugins/`) -具有相同的名称和版本,但是重复的 npm 包将被加载一次。,本地插件和名称相似的 npm 插件都是分开加载的。 +名称和版本相同的重复 npm 包只会加载一次。但本地插件和名称相似的 npm 插件会分别独立加载。 --- -## 创建一个插件 +## 创建插件 -插件是一个 **JavaScript/TypeScript 模块多个**,它导出一个或插件 -功能。每个函数接收一个上下文对象并返回一个钩子对象。 +插件是一个 **JavaScript/TypeScript 模块**,它导出一个或多个插件函数。每个函数接收一个上下文对象,并返回一个钩子对象。 --- -### 依赖关系 +### 依赖项 -本地插件和自定义工具可以使用外部 npm 包。将 `package.json` 添加到您的配置目录,其中包含您需要的依赖项。 +本地插件和自定义工具可以使用外部 npm 包。在配置目录中添加一个 `package.json`,列出所需的依赖项。 ```json title=".opencode/package.json" { @@ -83,7 +82,7 @@ description: 编写您自己的插件来扩展 opencode。 } ``` -opencode 在启动时运行 `bun install` 来安装这些。然后你的插件和工具就可以导入它们了。 +OpenCode 会在启动时运行 `bun install` 来安装这些依赖项。之后你的插件和工具就可以导入它们了。 ```ts title=".opencode/plugins/my-plugin.ts" import { escape } from "shescape" @@ -113,19 +112,19 @@ export const MyPlugin = async ({ project, client, $, directory, worktree }) => { } ``` -插件函数接收: +插件函数接收以下参数: - `project`:当前项目信息。 - `directory`:当前工作目录。 - `worktree`:git 工作树路径。 -- `client`:用于与AI交互的opencodeSDK客户端。 -- `$`:Bun的[外壳API](https://bun.com/docs/runtime/shell)用于执行命令。 +- `client`:用于与 AI 交互的 OpenCode SDK 客户端。 +- `$`:Bun 的 [Shell API](https://bun.com/docs/runtime/shell),用于执行命令。 --- ### TypeScript 支持 -对于 TypeScript 插件,您可以从插件包中导入类型: +对于 TypeScript 插件,你可以从插件包中导入类型: ```ts title="my-plugin.ts" {1} import type { Plugin } from "@opencode-ai/plugin" @@ -141,7 +140,7 @@ export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree ### 事件 -插件可以订阅事件,如下面的示例部分所示。以下是可用的不同事件的列表。 +插件可以订阅事件,如下方示例部分所示。以下是所有可用事件的列表。 #### 命令事件 @@ -211,13 +210,13 @@ export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree ## 示例 -以下是一些可用于扩展 opencode 的插件示例。 +以下是一些可用于扩展 OpenCode 的插件示例。 --- ### 发送通知 -当某些事件发生时发送通知: +在特定事件发生时发送通知: ```js title=".opencode/plugins/notification.js" export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => { @@ -232,17 +231,17 @@ export const NotificationPlugin = async ({ project, client, $, directory, worktr } ``` -我们在 macOS 上使用 `osascript` AppleScript。这里我们用它运行来发送通知。 +这里使用 `osascript` 在 macOS 上运行 AppleScript 来发送通知。 :::note -如果您使用 opencode 桌面应用程序,它可以在响应准备就绪或会话错误时自动发送系统通知。 +如果你使用 OpenCode 桌面应用,它可以在响应就绪或会话出错时自动发送系统通知。 ::: --- ### .env 保护 -阻止opencode读取`.env`文件: +阻止 OpenCode 读取 `.env` 文件: ```javascript title=".opencode/plugins/env-protection.js" export const EnvProtection = async ({ project, client, $, directory, worktree }) => { @@ -260,7 +259,7 @@ export const EnvProtection = async ({ project, client, $, directory, worktree }) ### 注入环境变量 -将环境变量注入所有shell执行(AI工具和用户终端): +将环境变量注入所有 Shell 执行(AI 工具和用户终端): ```javascript title=".opencode/plugins/inject-env.js" export const InjectEnvPlugin = async () => { @@ -277,7 +276,7 @@ export const InjectEnvPlugin = async () => { ### 自定义工具 -插件还可以向 opencode 添加自定义工具: +插件还可以为 OpenCode 添加自定义工具: ```ts title=".opencode/plugins/custom-tools.ts" import { type Plugin, tool } from "@opencode-ai/plugin" @@ -300,19 +299,19 @@ export const CustomToolsPlugin: Plugin = async (ctx) => { } ``` -`tool` 帮助器创建一个可以调用的自定义工具的opencode。它采用 Zod 模式函数并返回一个工具定义: +`tool` 辅助函数用于创建 OpenCode 可调用的自定义工具。它接受一个 Zod schema 函数,并返回一个工具定义,包含: -- `description`:该工具的作用 -- `args`:Zod 模式的工具参数 -- `execute`:调用工具时运行的函数 +- `description`:工具的功能描述 +- `args`:工具参数的 Zod schema +- `execute`:工具被调用时执行的函数 -您的自定义工具将可与内置工具一起用于opencode。 +你的自定义工具将与内置工具一起在 OpenCode 中可用。 --- -### 日志 +### 日志记录 -使用 `client.app.log()` 而不是 `console.log` 进行成型日志记录: +使用 `client.app.log()` 代替 `console.log` 进行结构化日志记录: ```ts title=".opencode/plugins/my-plugin.ts" export const MyPlugin = async ({ client }) => { @@ -327,13 +326,13 @@ export const MyPlugin = async ({ client }) => { } ``` -级别:`debug`、`info`、`warn`、`error`。详情请参见【SDK文档](https://opencode.ai/docs/sdk)。 +日志级别:`debug`、`info`、`warn`、`error`。详情请参阅 [SDK 文档](https://opencode.ai/docs/sdk)。 --- ### 压缩钩子 -自定义压缩会话时包含的上下文: +自定义会话压缩时包含的上下文: ```ts title=".opencode/plugins/compaction.ts" import type { Plugin } from "@opencode-ai/plugin" @@ -355,9 +354,9 @@ Include any state that should persist across compaction: } ``` -`experimental.session.compacting`钩子在LLM生成驱动机之前触发。使用它来填充默认压缩提示会丢失的特定于域的上下文。 +`experimental.session.compacting` 钩子在 LLM 生成续接摘要之前触发。使用它来注入默认压缩提示词可能遗漏的领域特定上下文。 -您还可以通过设置`output.prompt`来完全替换压缩提示: +你还可以通过设置 `output.prompt` 来完全替换压缩提示词: ```ts title=".opencode/plugins/custom-compaction.ts" import type { Plugin } from "@opencode-ai/plugin" @@ -382,4 +381,4 @@ Format as a structured prompt that a new agent can use to resume work. } ``` -当设置`output.prompt`时,它会取代完全默认的压缩提示。在这种情况下,`output.context` 内存将被忽略。 +当设置了 `output.prompt` 时,它会完全替换默认的压缩提示词。在这种情况下,`output.context` 数组将被忽略。 diff --git a/packages/web/src/content/docs/zh-cn/providers.mdx b/packages/web/src/content/docs/zh-cn/providers.mdx index 5b064e4ac0ed..ccc2bf7d406b 100644 --- a/packages/web/src/content/docs/zh-cn/providers.mdx +++ b/packages/web/src/content/docs/zh-cn/providers.mdx @@ -1,36 +1,36 @@ --- title: 提供商 -description: 在 opencode 中使用任意 LLM 提供商。 +description: 在 OpenCode 中使用任意 LLM 提供商。 --- import config from "../../../../config.mjs" export const console = config.console -opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev),支持 **75+ LLM 提供商**,也支持运行本地模型。 +OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev),支持 **75+ LLM 提供商**,同时也支持运行本地模型。 要添加提供商,你需要: 1. 使用 `/connect` 命令添加提供商的 API 密钥。 -2. 在 opencode 配置中设置该提供商。 +2. 在 OpenCode 配置中设置该提供商。 --- ### 凭据 -当你使用 `/connect` 命令添加提供商 API 后,凭据会存储在 -`~/.local/share/opencode/auth.json`。 +使用 `/connect` 命令添加提供商的 API 密钥后,凭据会存储在 +`~/.local/share/opencode/auth.json` 中。 --- ### 配置 -你可以使用 opencode 配置中的 `provider` 部分自定义提供商配置。 +你可以通过 OpenCode 配置中的 `provider` 部分来自定义提供商。 --- -#### 基本网址 +#### 自定义 Base URL -您可以通过设置 `baseURL` 选项来自定义任何提供商的基本 URL。这在使用代理服务或自定义端点时非常有用。 +你可以通过设置 `baseURL` 选项来自定义任何提供商的 Base URL。这在使用代理服务或自定义端点时非常有用。 ```json title="opencode.json" {6} { @@ -49,22 +49,21 @@ opencode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de ## OpenCode Zen -OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 -经测试和验证可与opencode良好配合。 [了解更多](/docs/zen)。 +OpenCode Zen 是由 OpenCode 团队提供的模型列表,这些模型已经过测试和验证,能够与 OpenCode 良好配合使用。[了解更多](/docs/zen)。 :::tip -如果您是新手,我们建议您从 OpenCode Zen 开始。 +如果你是新用户,我们建议从 OpenCode Zen 开始。 ::: -1. Run the `/connect` command in the TUI, select opencode, and head to [opencode.ai/auth](https://opencode.ai/auth). +1. 在 TUI 中执行 `/connect` 命令,选择 opencode,然后前往 [opencode.ai/auth](https://opencode.ai/auth)。 ```txt /connect ``` -2. 登录,添加您的账单详细信息,然后复制您的 API 密钥。 +2. 登录后添加账单信息,然后复制你的 API 密钥。 -3. 贴上您的 API 密钥。 +3. 粘贴你的 API 密钥。 ```txt ┌ API key @@ -73,38 +72,37 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 └ enter ``` -4. 在 TUI 中执行 `/models` 以查看我们推荐的模型列表。 +4. 在 TUI 中执行 `/models` 查看我们推荐的模型列表。 ```txt /models ``` -它的工作方式与 opencode 中的任何其他提供的程序相同,并且完全可以选择使用。 +它的使用方式与 OpenCode 中的其他提供商完全相同,且完全可选。 --- ## 目录 -让我们详细了解一些提供商。如果您想将提供商添加到 -列表,请随时开启PR。 +下面我们来详细了解一些提供商。如果你想将某个提供商添加到列表中,欢迎提交 PR。 :::note -没看到你要的提供商?欢迎提交 PR。 +没有看到你想要的提供商?欢迎提交 PR。 ::: --- ### 302.AI -1. Head over to the [302.AI console](https://302.ai/), create an account, and generate an API key. +1. 前往 [302.AI 控制台](https://302.ai/),创建账户并生成 API 密钥。 -2. 执行`/connect`命令并搜索**302.AI**。 +2. 执行 `/connect` 命令并搜索 **302.AI**。 ```txt /connect ``` -3. 输入您的 302.AI API 密钥。 +3. 输入你的 302.AI API 密钥。 ```txt ┌ API key @@ -113,7 +111,7 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models @@ -123,20 +121,19 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 ### Amazon Bedrock -相当于 Amazon Bedrock 与 opencode 结合使用: +要在 OpenCode 中使用 Amazon Bedrock: -1. 前往 Amazon Bedrock 控制台中的 **模型目录** 并请求 - 访问您想要的模型。 +1. 前往 Amazon Bedrock 控制台中的**模型目录**,申请访问你想要使用的模型。 - :::提示 - 您需要能够在 Amazon Bedrock 中访问所需的模型。 + :::tip + 你需要先在 Amazon Bedrock 中获得对目标模型的访问权限。 ::: -2. **使用以下方法之一配置身份验证**: +2. 使用以下方法之一**配置身份验证**: - #### 环境变量(快速启动) + #### 环境变量(快速上手) - 执行 opencode 时设置以下环境变量之一: + 运行 opencode 时设置以下环境变量之一: ```bash # Option 1: Using AWS access keys @@ -149,7 +146,7 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 AWS_BEARER_TOKEN_BEDROCK=XXX opencode ``` - 或者将它们添加到您的 bash 配置文件中: + 或者将它们添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export AWS_PROFILE=my-dev-profile @@ -158,7 +155,7 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 #### 配置文件(推荐) - For project-specific or persistent configuration, use `opencode.json`: + 如需项目级别或持久化的配置,请使用 `opencode.json`: ```json title="opencode.json" { @@ -176,16 +173,16 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 **可用选项:** - `region` - AWS 区域(例如 `us-east-1`、`eu-west-1`) - - `profile` - 来自 `~/.aws/credentials` 的 AWS 命名配置档案 - - `endpoint` - VPC 节点节点的自定义节点 URL(通用 `baseURL` 选项的别名) + - `profile` - `~/.aws/credentials` 中的 AWS 命名配置文件 + - `endpoint` - VPC 端点的自定义端点 URL(通用 `baseURL` 选项的别名) - :::提示 - 配置文件选项优先于环境变量。 + :::tip + 配置文件中的选项优先级高于环境变量。 ::: - #### 高阶:VPC 端点 + #### 进阶:VPC 端点 - 如果您使用 Bedrock 的 VPC 终端节点: + 如果你使用 Bedrock 的 VPC 端点: ```json title="opencode.json" { @@ -202,34 +199,34 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 } ``` - :::笔记 - `endpoint` 选项是通用 `baseURL` 选项的别名,使用 AWS 术语特定。如果同时指定了 `endpoint` 和 `baseURL`,则 `endpoint` 优先。 + :::note + `endpoint` 选项是通用 `baseURL` 选项的别名,使用了 AWS 特有的术语。如果同时指定了 `endpoint` 和 `baseURL`,则 `endpoint` 优先。 ::: #### 认证方式 - - **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**:创建IAM用户并在AWS控制台中生成访问金币。 - - **`AWS_PROFILE`**:使用 `~/.aws/credentials` 中的命名配置文件。首先配置 `aws configure --profile my-profile` 或 `aws sso login` + - **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**:在 AWS 控制台中创建 IAM 用户并生成访问密钥 + - **`AWS_PROFILE`**:使用 `~/.aws/credentials` 中的命名配置文件。需要先通过 `aws configure --profile my-profile` 或 `aws sso login` 进行配置 - **`AWS_BEARER_TOKEN_BEDROCK`**:从 Amazon Bedrock 控制台生成长期 API 密钥 - - **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**:适用于 EKS IRSA(服务账户的 IAM 角色)或具有 OIDC 联合的其他 Kubernetes 环境。使用服务账户注释时,这些环境变量由 Kubernetes 自动注入。 + - **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**:适用于 EKS IRSA(服务账户的 IAM 角色)或其他支持 OIDC 联合的 Kubernetes 环境。使用服务账户注解时,Kubernetes 会自动注入这些环境变量。 - #### 认证优先顺序 + #### 认证优先级 - Amazon Bedrock 使用以下身份验证优先顺序: - 1. **不记名Tokens** - `AWS_BEARER_TOKEN_BEDROCK`环境变化数据或来自`/connect`Tokens的Tokens - 2. **AWS 凭证链** - 配置档案、访问密钥、共享凭证、IAM 角色、Web 身份Tokens (EKS IRSA)、实例项后设置资料 + Amazon Bedrock 使用以下认证优先级: + 1. **Bearer Token** - `AWS_BEARER_TOKEN_BEDROCK` 环境变量或通过 `/connect` 命令获取的 Token + 2. **AWS 凭证链** - 配置文件、访问密钥、共享凭证、IAM 角色、Web Identity Token(EKS IRSA)、实例元数据 - :::笔记 - 设置不记名Tokens(使用 `/connect` 或 `AWS_BEARER_TOKEN_BEDROCK`)时,其优先于所有 AWS 凭证方法(包括配置的配置文件)。 + :::note + 当设置了 Bearer Token(通过 `/connect` 或 `AWS_BEARER_TOKEN_BEDROCK`)时,它的优先级高于所有 AWS 凭证方式,包括已配置的配置文件。 ::: -3. 执行`/models`命令选择所需的模型。 +3. 执行 `/models` 命令选择你想要的模型。 ```txt /models ``` :::note -对于自定义推理配置文件,请在按键中使用模型并提供商名称,并将 `id` 属性设置为 arn。这确保了正确的缓存: +对于自定义推理配置文件,请在 key 中使用模型名称和提供商名称,并将 `id` 属性设置为 ARN。这可以确保正确的缓存行为: ```json title="opencode.json" { @@ -253,14 +250,13 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 ### Anthropic -1. 注册后,执行`/connect`命令并选择Anthropic。 +1. 注册完成后,执行 `/connect` 命令并选择 Anthropic。 ```txt /connect ``` -2. 您可以在此处选择 **Claude Pro/Max** 选项,就会打开您的浏览器 - 并要求您进行身份验证。 +2. 你可以选择 **Claude Pro/Max** 选项,浏览器会自动打开并要求你进行身份验证。 ```txt ┌ Select auth method @@ -271,38 +267,38 @@ OpenCode Zen 是opencode团队提供的模型列表,这些模型已被 └ ``` -3. 现在,当您使用 `/models` 命令时,所有人类模型都应该可用。 +3. 现在使用 `/models` 命令即可看到所有 Anthropic 模型。 ```txt /models ``` :::info -Using your Claude Pro/Max subscription in opencode is not officially supported by [Anthropic](https://anthropic.com). +在 OpenCode 中使用 Claude Pro/Max 订阅不是 [Anthropic](https://anthropic.com) 官方支持的用法。 ::: -##### 使用 API 键 +##### 使用 API 密钥 -如果您没有 Pro/Max 订阅,您还可以选择 **创建 API 密钥**。它还会开启您的浏览器并要求您登录 Anthropic 并为您提供一个可以粘贴到终端中的代码。 +如果你没有 Pro/Max 订阅,也可以选择 **Create an API Key**。浏览器会自动打开并要求你登录 Anthropic,然后会提供一个代码供你粘贴到终端中。 -或者,如果您已安装 API 密钥,则可以选择 **手动输入 API 密钥** 将其贴到终端中。 +如果你已经有 API 密钥,可以选择 **Manually enter API Key** 并将其粘贴到终端中。 --- ### Azure OpenAI :::note -如果遇到“抱歉,但我无法协助该请求”错误,请尝试将 Azure 资源中的内容筛选器从 **DefaultV2** 更改为 **Default**。 +如果遇到 "I'm sorry, but I cannot assist with that request" 错误,请尝试将 Azure 资源中的内容过滤器从 **DefaultV2** 更改为 **Default**。 ::: -1. Head over to the [Azure portal](https://portal.azure.com/) and create an **Azure OpenAI** resource. You'll need: - - **资源名称**:这将成为您的 API 端点 (`https://RESOURCE_NAME.openai.azure.com/`) 的一部分 - - **API 密钥**:来自您资源的 `KEY 1` 或 `KEY 2` +1. 前往 [Azure 门户](https://portal.azure.com/)并创建 **Azure OpenAI** 资源。你需要: + - **资源名称**:这会成为你的 API 端点的一部分(`https://RESOURCE_NAME.openai.azure.com/`) + - **API 密钥**:资源中的 `KEY 1` 或 `KEY 2` -2. Go to [Azure AI Foundry](https://ai.azure.com/) and deploy a model. +2. 前往 [Azure AI Foundry](https://ai.azure.com/) 并部署一个模型。 - :::笔记 - 部署名称必须与模型名称匹配,opencode才能正常工作。 + :::note + 部署名称必须与模型名称一致,OpenCode 才能正常工作。 ::: 3. 执行 `/connect` 命令并搜索 **Azure**。 @@ -311,7 +307,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b /connect ``` -4. 输入您的 API 密钥。 +4. 输入你的 API 密钥。 ```txt ┌ API key @@ -320,19 +316,19 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b └ enter ``` -5. 将您的资源名称设置为环境变量: +5. 将资源名称设置为环境变量: ```bash AZURE_RESOURCE_NAME=XXX opencode ``` - 或者将其添加内容添加到您的 bash 配置文件中: + 或者添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export AZURE_RESOURCE_NAME=XXX ``` -6. 执行 `/models` 命令以选择您部署的模型。 +6. 执行 `/models` 命令选择你已部署的模型。 ```txt /models @@ -340,25 +336,25 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b --- -### Azure 认知服务 +### Azure Cognitive Services -1. Head over to the [Azure portal](https://portal.azure.com/) and create an **Azure OpenAI** resource. You'll need: - - **资源名称**:这将成为您的 API 端点 (`https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/`) 的一部分 - - **API 密钥**:来自您资源的 `KEY 1` 或 `KEY 2` +1. 前往 [Azure 门户](https://portal.azure.com/)并创建 **Azure OpenAI** 资源。你需要: + - **资源名称**:这会成为你的 API 端点的一部分(`https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/`) + - **API 密钥**:资源中的 `KEY 1` 或 `KEY 2` -2. Go to [Azure AI Foundry](https://ai.azure.com/) and deploy a model. +2. 前往 [Azure AI Foundry](https://ai.azure.com/) 并部署一个模型。 - :::笔记 - 部署名称必须与模型名称匹配,opencode才能正常工作。 + :::note + 部署名称必须与模型名称一致,OpenCode 才能正常工作。 ::: -3. 执行 `/connect` 命令并搜索 **Azure 认知服务**。 +3. 执行 `/connect` 命令并搜索 **Azure Cognitive Services**。 ```txt /connect ``` -4. 输入您的 API 密钥。 +4. 输入你的 API 密钥。 ```txt ┌ API key @@ -367,19 +363,19 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b └ enter ``` -5. 将您的资源名称设置为环境变量: +5. 将资源名称设置为环境变量: ```bash AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX opencode ``` - 或者将其添加内容添加到您的 bash 配置文件中: + 或者添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX ``` -6. 执行 `/models` 命令以选择您部署的模型。 +6. 执行 `/models` 命令选择你已部署的模型。 ```txt /models @@ -389,7 +385,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b ### Baseten -1. Head over to the [Baseten](https://app.baseten.co/), create an account, and generate an API key. +1. 前往 [Baseten](https://app.baseten.co/),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Baseten**。 @@ -397,7 +393,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b /connect ``` -3. 输入您的 Baseten API 密钥。 +3. 输入你的 Baseten API 密钥。 ```txt ┌ API key @@ -406,7 +402,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models @@ -416,7 +412,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b ### Cerebras -1. Head over to the [Cerebras console](https://inference.cerebras.ai/), create an account, and generate an API key. +1. 前往 [Cerebras 控制台](https://inference.cerebras.ai/),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Cerebras**。 @@ -424,7 +420,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b /connect ``` -3. 输入您的 Cerebras API 密钥。 +3. 输入你的 Cerebras API 密钥。 ```txt ┌ API key @@ -433,7 +429,7 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b └ enter ``` -4. 执行`/models`命令选择*Qwen 3 Coder 480B*等模型。 +4. 执行 `/models` 命令选择模型,例如 _Qwen 3 Coder 480B_。 ```txt /models @@ -443,11 +439,11 @@ Using your Claude Pro/Max subscription in opencode is not officially supported b ### Cloudflare AI Gateway -Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, and more through a unified endpoint. With [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/) you don't need separate API keys for each provider. +Cloudflare AI Gateway 允许你通过统一端点访问来自 OpenAI、Anthropic、Workers AI 等提供商的模型。通过 [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/),你无需为每个提供商单独准备 API 密钥。 -1. Head over to the [Cloudflare dashboard](https://dash.cloudflare.com/), navigate to **AI** > **AI Gateway**, and create a new gateway. +1. 前往 [Cloudflare 仪表盘](https://dash.cloudflare.com/),导航到 **AI** > **AI Gateway**,创建一个新的网关。 -2. 将您的账户ID和闸道器ID设定为环境变量。 +2. 将你的 Account ID 和 Gateway ID 设置为环境变量。 ```bash title="~/.bash_profile" export CLOUDFLARE_ACCOUNT_ID=your-32-character-account-id @@ -460,7 +456,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, /connect ``` -4. 输入您的 Cloudflare API Tokens。 +4. 输入你的 Cloudflare API Token。 ```txt ┌ API key @@ -475,13 +471,13 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, export CLOUDFLARE_API_TOKEN=your-api-token ``` -5. 执行`/models`命令选择模型。 +5. 执行 `/models` 命令选择模型。 ```txt /models ``` - 您还可以使用opencode配置添加模型。 + 你也可以通过 OpenCode 配置添加模型。 ```json title="opencode.json" { @@ -501,7 +497,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### Cortecs -1. Head over to the [Cortecs console](https://cortecs.ai/), create an account, and generate an API key. +1. 前往 [Cortecs 控制台](https://cortecs.ai/),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Cortecs**。 @@ -509,7 +505,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, /connect ``` -3. 输入您的 Cortecs API 密钥。 +3. 输入你的 Cortecs API 密钥。 ```txt ┌ API key @@ -518,7 +514,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, └ enter ``` -4. 执行 `/models` 命令以选择类似 _Kimi K2 Instruct_ 的模型。 +4. 执行 `/models` 命令选择模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -528,7 +524,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### DeepSeek -1. Head over to the [DeepSeek console](https://platform.deepseek.com/), create an account, and click **Create new API key**. +1. 前往 [DeepSeek 控制台](https://platform.deepseek.com/),创建账户并点击 **Create new API key**。 2. 执行 `/connect` 命令并搜索 **DeepSeek**。 @@ -536,7 +532,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, /connect ``` -3. 输入您的 DeepSeek API 密钥。 +3. 输入你的 DeepSeek API 密钥。 ```txt ┌ API key @@ -545,7 +541,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, └ enter ``` -4. 执行`/models`命令以选择DeepSeek模型,例如*DeepSeek Reasoner*。 +4. 执行 `/models` 命令选择 DeepSeek 模型,例如 _DeepSeek Reasoner_。 ```txt /models @@ -555,7 +551,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### Deep Infra -1. Head over to the [Deep Infra dashboard](https://deepinfra.com/dash), create an account, and generate an API key. +1. 前往 [Deep Infra 仪表盘](https://deepinfra.com/dash),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Deep Infra**。 @@ -563,7 +559,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, /connect ``` -3. 输入您的深层基础设施 API 密钥。 +3. 输入你的 Deep Infra API 密钥。 ```txt ┌ API key @@ -572,7 +568,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models @@ -582,15 +578,15 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### Firmware -1. Head over to the [Firmware dashboard](https://app.firmware.ai/signup), create an account, and generate an API key. +1. 前往 [Firmware 仪表盘](https://app.firmware.ai/signup),创建账户并生成 API 密钥。 -2. 执行`/connect`命令并搜索**韧体**。 +2. 执行 `/connect` 命令并搜索 **Firmware**。 ```txt /connect ``` -3. 输入您的韧体API 密钥。 +3. 输入你的 Firmware API 密钥。 ```txt ┌ API key @@ -599,7 +595,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models @@ -609,7 +605,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### Fireworks AI -1. Head over to the [Fireworks AI console](https://app.fireworks.ai/), create an account, and click **Create API Key**. +1. 前往 [Fireworks AI 控制台](https://app.fireworks.ai/),创建账户并点击 **Create API Key**。 2. 执行 `/connect` 命令并搜索 **Fireworks AI**。 @@ -617,7 +613,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, /connect ``` -3. 输入您的 Fireworks AI API 密钥。 +3. 输入你的 Fireworks AI API 密钥。 ```txt ┌ API key @@ -626,7 +622,7 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, └ enter ``` -4. 执行 `/models` 命令以选择类似 _Kimi K2 Instruct_ 的模型。 +4. 执行 `/models` 命令选择模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -636,15 +632,15 @@ Cloudflare AI Gateway lets you access models from OpenAI, Anthropic, Workers AI, ### GitLab Duo -GitLab Duo 通过 GitLab 的人工代理提供具有本机工具呼叫功能的人工智慧代理聊天。 +GitLab Duo 通过 GitLab 的 Anthropic 代理提供具有原生工具调用能力的 AI 驱动的代理聊天。 -1. 执行`/connect`命令并选择GitLab。 +1. 执行 `/connect` 命令并选择 GitLab。 ```txt /connect ``` -2. 选择您的身份验证方法: +2. 选择你的身份验证方式: ```txt ┌ Select auth method @@ -654,16 +650,16 @@ GitLab Duo 通过 GitLab 的人工代理提供具有本机工具呼叫功能的 └ ``` - #### 使用OAuth(推荐) + #### 使用 OAuth(推荐) - 选择**OAuth**,您的浏览器将开启并进行授权。 + 选择 **OAuth**,浏览器会自动打开进行授权。 - #### 使用个人访问Tokens - 1. Go to [GitLab User Settings > Access Tokens](https://gitlab.com/-/user_settings/personal_access_tokens) - 2. 单击**添加新Tokens** - 3. Name: `OpenCode`, Scopes: `api` - 4. 复制Tokens(以 `glpat-` 发起人) - 5. 在终端中输入 + #### 使用个人访问令牌 + 1. 前往 [GitLab 用户设置 > Access Tokens](https://gitlab.com/-/user_settings/personal_access_tokens) + 2. 点击 **Add new token** + 3. 名称填写 `OpenCode`,范围选择 `api` + 4. 复制令牌(以 `glpat-` 开头) + 5. 在终端中输入该令牌 3. 执行 `/models` 命令查看可用模型。 @@ -671,23 +667,19 @@ GitLab Duo 通过 GitLab 的人工代理提供具有本机工具呼叫功能的 /models ``` - 提供基于 Claude 的模型: - - **duo-chat-haiku-4-5**(默认)- 快速任务的快速响应 - - **duo-chat-sonnet-4-5** - 大多数工作流程的平衡失败 - - **duo-chat-opus-4-5** - 最有能力进行复杂分析 + 提供三个基于 Claude 的模型: + - **duo-chat-haiku-4-5**(默认)- 快速响应,适合简单任务 + - **duo-chat-sonnet-4-5** - 性能均衡,适合大多数工作流 + - **duo-chat-opus-4-5** - 最强大,适合复杂分析 :::note -如果您不愿意,也可以指定“GITLAB_TOKEN”环境变量 -将Tokens存储在opencode身份验证存储中。 +你也可以通过指定 `GITLAB_TOKEN` 环境变量来避免将令牌存储在 OpenCode 的认证存储中。 ::: ##### 自托管 GitLab -:::note[合规笔记] -opencode 使用一个小模型来执行一些 AI 任务,例如生成会话标题。 -情况下,其配置为使用 gpt-5-nano,由 Zen 托管。默认 opencode -只需使用您自己的 GitLab 托管示例项,即可将以下内容添加到您的 -`opencode.json` file. It is also recommended to disable session sharing. +:::note[合规说明] +OpenCode 会使用一个小模型来执行部分 AI 任务,例如生成会话标题。默认情况下使用由 Zen 托管的 gpt-5-nano。如果你需要让 OpenCode 仅使用你自己的 GitLab 托管实例,请在 `opencode.json` 文件中添加以下内容。同时建议禁用会话共享。 ```json { @@ -699,20 +691,20 @@ opencode 使用一个小模型来执行一些 AI 任务,例如生成会话标 ::: -对于自托管 GitLab 示例项目: +对于自托管 GitLab 实例: ```bash export GITLAB_INSTANCE_URL=https://gitlab.company.com export GITLAB_TOKEN=glpat-... ``` -如果您的示例项执行自定义AI闸道器: +如果你的实例运行了自定义 AI Gateway: ```bash GITLAB_AI_GATEWAY_URL=https://ai-gateway.company.com ``` -或者添加到您的 bash 配置文件中: +或者添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export GITLAB_INSTANCE_URL=https://gitlab.company.com @@ -721,35 +713,33 @@ export GITLAB_TOKEN=glpat-... ``` :::note -您的 GitLab 管理员必须启用以下功能: +你的 GitLab 管理员必须启用以下功能: -1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) for the user, group, or instance -2. 功能标志(通过Rails控制台): +1. 为用户、群组或实例启用 [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) +2. 功能标志(通过 Rails 控制台): - `agent_platform_claude_code` - `third_party_agents_enabled` ::: -##### 用于自托管项目的 OAuth +##### 自托管实例的 OAuth -为了使 Oauth 适用于您的自托管项目,您需要建立 -一个新的应用程序(设置→应用程序) -回调 URL `http://127.0.0.1:8080/callback` 和以下范围: +要在自托管实例上使用 OAuth,你需要创建一个新应用(设置 → 应用),回调 URL 设置为 `http://127.0.0.1:8080/callback`,并选择以下范围: -- api(您代表访问API) -- read_user(读取您的个人信息) -- read_repository(允许对存储库进行只读访问) +- api(代表你访问 API) +- read_user(读取你的个人信息) +- read_repository(允许对仓库进行只读访问) -然后将应用程序ID公开为环境变量: +然后将应用 ID 导出为环境变量: ```bash export GITLAB_OAUTH_CLIENT_ID=your_application_id_here ``` -More documentation on [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) homepage. +更多文档请参阅 [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 主页。 ##### 配置 -Customize through `opencode.json`: +通过 `opencode.json` 进行自定义配置: ```json title="opencode.json" { @@ -770,7 +760,7 @@ Customize through `opencode.json`: ##### GitLab API 工具(可选,但强烈推荐) -要访问GitLab工具(合并请求、问题、管道、CI/CD等): +要访问 GitLab 工具(合并请求、Issue、流水线、CI/CD 等): ```json title="opencode.json" { @@ -779,19 +769,16 @@ Customize through `opencode.json`: } ``` -该外挂提供全面的GitLab存储库管理功能,包括MR审查、问题跟踪、管道监控等。 +该插件提供全面的 GitLab 仓库管理功能,包括 MR 审查、Issue 跟踪、流水线监控等。 --- ### GitHub Copilot -相当于您的 GitHub Copilot 订阅与 opencode 一起使用: +要在 OpenCode 中使用你的 GitHub Copilot 订阅: :::note -某些模型可能需要 [Pro+ -订阅](https://github.com/features/copilot/plans)使用。 - -Some models need to be manually enabled in your [GitHub Copilot settings](https://docs.github.com/en/copilot/how-tos/use-ai-models/configure-access-to-ai-models#setup-for-individual-use). +部分模型可能需要 [Pro+ 订阅](https://github.com/features/copilot/plans)才能使用。 ::: 1. 执行 `/connect` 命令并搜索 GitHub Copilot。 @@ -800,7 +787,7 @@ Some models need to be manually enabled in your [GitHub Copilot settings](https: /connect ``` -2. Navigate to [github.com/login/device](https://github.com/login/device) and enter the code. +2. 前往 [github.com/login/device](https://github.com/login/device) 并输入验证码。 ```txt ┌ Login with GitHub Copilot @@ -812,7 +799,7 @@ Some models need to be manually enabled in your [GitHub Copilot settings](https: └ Waiting for authorization... ``` -3. 现在执行 `/models` 命令来选择您想要的模型。 +3. 现在执行 `/models` 命令选择你想要的模型。 ```txt /models @@ -822,29 +809,28 @@ Some models need to be manually enabled in your [GitHub Copilot settings](https: ### Google Vertex AI -Google Vertex AI 与 opencode 结合使用: +要在 OpenCode 中使用 Google Vertex AI: -1. 前往 Google Cloud Console 中的 **模型花园** 并检查 - 您所在地区提供的模型。 +1. 前往 Google Cloud Console 中的**模型花园**,查看你所在区域可用的模型。 - :::笔记 - 您需要有一个启用了 Vertex AI API 的 Google Cloud 专案。 + :::note + 你需要一个启用了 Vertex AI API 的 Google Cloud 项目。 ::: 2. 设置所需的环境变量: - - `GOOGLE_CLOUD_PROJECT`:您的Google云专案ID - - `VERTEX_LOCATION`(可选):Vertex AI的区域(默认为`global`) - - 身份验证(选择一项): - - `GOOGLE_APPLICATION_CREDENTIALS`:服务帐户 JSON 密钥文件的路径 + - `GOOGLE_CLOUD_PROJECT`:你的 Google Cloud 项目 ID + - `VERTEX_LOCATION`(可选):Vertex AI 的区域(默认为 `global`) + - 身份验证(选择其一): + - `GOOGLE_APPLICATION_CREDENTIALS`:服务账户 JSON 密钥文件的路径 - 使用 gcloud CLI 进行身份验证:`gcloud auth application-default login` - 在执行 opencode 时设置它们。 + 在运行 opencode 时设置: ```bash GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json GOOGLE_CLOUD_PROJECT=your-project-id opencode ``` - 或者将它们添加到您的 bash 配置文件中。 + 或者添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json @@ -853,10 +839,10 @@ Google Vertex AI 与 opencode 结合使用: ``` :::tip -The `global` region improves availability and reduces errors at no extra cost. Use regional endpoints (e.g., `us-central1`) for data residency requirements. [Learn more](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models#regional_and_global_endpoints) +`global` 区域可以提高可用性并减少错误,且不会产生额外费用。如果有数据驻留需求,请使用区域端点(例如 `us-central1`)。[了解更多](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models#regional_and_global_endpoints) ::: -3. 执行`/models`命令选择所需的模型。 +3. 执行 `/models` 命令选择你想要的模型。 ```txt /models @@ -866,7 +852,7 @@ The `global` region improves availability and reduces errors at no extra cost. U ### Groq -1. Head over to the [Groq console](https://console.groq.com/), click **Create API Key**, and copy the key. +1. 前往 [Groq 控制台](https://console.groq.com/),点击 **Create API Key** 并复制密钥。 2. 执行 `/connect` 命令并搜索 Groq。 @@ -874,7 +860,7 @@ The `global` region improves availability and reduces errors at no extra cost. U /connect ``` -3. 输入结构的API 密钥。 +3. 输入该提供商的 API 密钥。 ```txt ┌ API key @@ -883,7 +869,7 @@ The `global` region improves availability and reduces errors at no extra cost. U └ enter ``` -4. 执行`/models`命令来选择您想要的。 +4. 执行 `/models` 命令选择你想要的模型。 ```txt /models @@ -893,17 +879,17 @@ The `global` region improves availability and reduces errors at no extra cost. U ### Hugging Face -[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers) provides access to open models supported by 17+ providers. +[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers) 提供对由 17+ 提供商支持的开放模型的访问。 -1. Head over to [Hugging Face settings](https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained) to create a token with permission to make calls to Inference Providers. +1. 前往 [Hugging Face 设置](https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained),创建一个具有调用 Inference Providers 权限的令牌。 -2. 执行 `/connect` 命令并搜索 **拥抱脸**。 +2. 执行 `/connect` 命令并搜索 **Hugging Face**。 ```txt /connect ``` -3. 输入您的 Hugging Face API 密钥。 +3. 输入你的 Hugging Face 令牌。 ```txt ┌ API key @@ -912,7 +898,7 @@ The `global` region improves availability and reduces errors at no extra cost. U └ enter ``` -4. 执行`/models`命令选择*Kimi-K2-Instruct* 或 _GLM-4.6_ 等模型。 +4. 执行 `/models` 命令选择模型,例如 _Kimi-K2-Instruct_ 或 _GLM-4.6_。 ```txt /models @@ -922,9 +908,9 @@ The `global` region improves availability and reduces errors at no extra cost. U ### Helicone -[Helicone](https://helicone.ai) is an LLM observability platform that provides logging, monitoring, and analytics for your AI applications. The Helicone AI Gateway routes your requests to the appropriate provider automatically based on the model. +[Helicone](https://helicone.ai) 是一个 LLM 可观测性平台,为你的 AI 应用提供日志记录、监控和分析功能。Helicone AI Gateway 会根据模型自动将请求路由到对应的提供商。 -1. Head over to [Helicone](https://helicone.ai), create an account, and generate an API key from your dashboard. +1. 前往 [Helicone](https://helicone.ai),创建账户并在仪表盘中生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Helicone**。 @@ -932,7 +918,7 @@ The `global` region improves availability and reduces errors at no extra cost. U /connect ``` -3. 输入您的 Helicone API 密钥。 +3. 输入你的 Helicone API 密钥。 ```txt ┌ API key @@ -941,19 +927,19 @@ The `global` region improves availability and reduces errors at no extra cost. U └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models ``` -For more providers and advanced features like caching and rate limiting, check the [Helicone documentation](https://docs.helicone.ai). +如需了解更多提供商以及缓存、速率限制等高级功能,请查阅 [Helicone 文档](https://docs.helicone.ai)。 #### 可选配置 -如果您发现Helicone的某些功能或模型未通过opencode自动配置,您始终可以自行配置。 +如果 Helicone 的某些功能或模型未通过 OpenCode 自动配置,你随时可以手动配置。 -Here's [Helicone's Model Directory](https://helicone.ai/models), you'll need this to grab the IDs of the models you want to add. +[Helicone 模型目录](https://helicone.ai/models)中可以找到你需要添加的模型 ID。 ```jsonc title="~/.config/opencode/opencode.jsonc" { @@ -979,9 +965,9 @@ Here's [Helicone's Model Directory](https://helicone.ai/models), you'll need thi } ``` -#### 自定义标头 +#### 自定义请求头 -Helicone 支持快速获取、用户跟踪和会话管理等功能的自定义标头。使用 `options.headers` 将它们添加到您提供的方案配置中: +Helicone 支持用于缓存、用户跟踪和会话管理等功能的自定义请求头。使用 `options.headers` 将它们添加到提供商配置中: ```jsonc title="~/.config/opencode/opencode.jsonc" { @@ -1004,13 +990,13 @@ Helicone 支持快速获取、用户跟踪和会话管理等功能的自定义 ##### 会话跟踪 -Helicone's [Sessions](https://docs.helicone.ai/features/sessions) feature lets you group related LLM requests together. Use the [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) plugin to automatically log each opencode conversation as a session in Helicone. +Helicone 的 [Sessions](https://docs.helicone.ai/features/sessions) 功能允许你将相关的 LLM 请求归为一组。使用 [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) 插件可以自动将每个 OpenCode 对话记录为 Helicone 中的一个会话。 ```bash npm install -g opencode-helicone-session ``` -将其添加到您的配置中。 +将其添加到配置中。 ```json title="opencode.json" { @@ -1018,24 +1004,24 @@ npm install -g opencode-helicone-session } ``` -该外挂将 `Helicone-Session-Id` 和 `Helicone-Session-Name` 标头注入您的请求中。在 Helicone 的会话页面中,您将看到每个 opencode 对话都是单独的会话。 +该插件会在你的请求中注入 `Helicone-Session-Id` 和 `Helicone-Session-Name` 请求头。在 Helicone 的 Sessions 页面中,你可以看到每个 OpenCode 对话都作为独立的会话列出。 -##### 常见螺旋接头 +##### 常用 Helicone 请求头 -| 标题 | 描述 | -| -------------------------- | ----------------------------------------------------- | -| `Helicone-Cache-Enabled` | Enable response caching (`true`/`false`) | -| `Helicone-User-Id` | 点击用户跟踪指标 | -| `Helicone-Property-[Name]` | 添加自定义属性(例如`Helicone-Property-Environment`) | -| `Helicone-Prompt-Id` | 将请求与提示版本相关联 | +| 请求头 | 描述 | +| -------------------------- | ------------------------------------------------------ | +| `Helicone-Cache-Enabled` | 启用响应缓存(`true`/`false`) | +| `Helicone-User-Id` | 按用户跟踪指标 | +| `Helicone-Property-[Name]` | 添加自定义属性(例如 `Helicone-Property-Environment`) | +| `Helicone-Prompt-Id` | 将请求与提示词版本关联 | -See the [Helicone Header Directory](https://docs.helicone.ai/helicone-headers/header-directory) for all available headers. +有关所有可用请求头,请参阅 [Helicone Header Directory](https://docs.helicone.ai/helicone-headers/header-directory)。 --- ### llama.cpp -You can configure opencode to use local models through [llama.cpp's](https://github.com/ggml-org/llama.cpp) llama-server utility +你可以通过 [llama.cpp](https://github.com/ggml-org/llama.cpp) 的 llama-server 工具配置 OpenCode 使用本地模型。 ```json title="opencode.json" "llama.cpp" {5, 6, 8, 10-15} { @@ -1061,29 +1047,29 @@ You can configure opencode to use local models through [llama.cpp's](https://git } ``` -在这个例子中: +在这个示例中: -- `llama.cpp` 是自定义创建 ID。这可以是您想要的任何字符串。 -- `npm` specifies the package to use for this provider. Here, `@ai-sdk/openai-compatible` is used for any OpenAI-compatible API. -- `name` 是 UI 中提供商的显示名称。 -- `options.baseURL` 是本地服务器器的端点。 -- `models` 是模型 ID 以及配置的对应映射。模型名称将显示在模型选择列表中。 +- `llama.cpp` 是自定义的提供商 ID,可以是任意字符串。 +- `npm` 指定该提供商使用的包。这里使用 `@ai-sdk/openai-compatible` 来兼容任何 OpenAI 兼容的 API。 +- `name` 是该提供商在 UI 中显示的名称。 +- `options.baseURL` 是本地服务器的端点地址。 +- `models` 是模型 ID 到其配置的映射。模型名称会显示在模型选择列表中。 --- ### IO.NET -IO.NET提供了17种针对各种例子进行优化的模型: +IO.NET 提供 17 个针对不同用例优化的模型: -1. Head over to the [IO.NET console](https://ai.io.net/), create an account, and generate an API key. +1. 前往 [IO.NET 控制台](https://ai.io.net/),创建账户并生成 API 密钥。 -2. 执行`/connect`命令并搜索**IO.NET**。 +2. 执行 `/connect` 命令并搜索 **IO.NET**。 ```txt /connect ``` -3. 输入您的 IO.NET API 密钥。 +3. 输入你的 IO.NET API 密钥。 ```txt ┌ API key @@ -1092,7 +1078,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models @@ -1102,7 +1088,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: ### LM Studio -您可以通过使用本地模型来使用 LM Studio 配置opencode。 +你可以通过 LM Studio 配置 OpenCode 使用本地模型。 ```json title="opencode.json" "lmstudio" {5, 6, 8, 10-14} { @@ -1124,21 +1110,21 @@ IO.NET提供了17种针对各种例子进行优化的模型: } ``` -在这个例子中: +在这个示例中: -- `lmstudio` 是自定义创建 ID。这可以是您想要的任何字符串。 -- `npm` specifies the package to use for this provider. Here, `@ai-sdk/openai-compatible` is used for any OpenAI-compatible API. -- `name` 是 UI 中提供商的显示名称。 -- `options.baseURL` 是本地服务器器的端点。 -- `models` 是模型 ID 以及配置的对应映射。模型名称将显示在模型选择列表中。 +- `lmstudio` 是自定义的提供商 ID,可以是任意字符串。 +- `npm` 指定该提供商使用的包。这里使用 `@ai-sdk/openai-compatible` 来兼容任何 OpenAI 兼容的 API。 +- `name` 是该提供商在 UI 中显示的名称。 +- `options.baseURL` 是本地服务器的端点地址。 +- `models` 是模型 ID 到其配置的映射。模型名称会显示在模型选择列表中。 --- ### Moonshot AI -要使用 Moonshot AI 中的 Kimi K2: +要使用 Moonshot AI 的 Kimi K2: -1. Head over to the [Moonshot AI console](https://platform.moonshot.ai/console), create an account, and click **Create API key**. +1. 前往 [Moonshot AI 控制台](https://platform.moonshot.ai/console),创建账户并点击 **Create API key**。 2. 执行 `/connect` 命令并搜索 **Moonshot AI**。 @@ -1146,7 +1132,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: /connect ``` -3. 输入您的 Moonshot API 密钥。 +3. 输入你的 Moonshot API 密钥。 ```txt ┌ API key @@ -1155,7 +1141,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: └ enter ``` -4. 执行`/models`命令以选择*Kimi K2*。 +4. 执行 `/models` 命令选择 _Kimi K2_。 ```txt /models @@ -1165,7 +1151,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: ### MiniMax -1. Head over to the [MiniMax API Console](https://platform.minimax.io/login), create an account, and generate an API key. +1. 前往 [MiniMax API 控制台](https://platform.minimax.io/login),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **MiniMax**。 @@ -1173,7 +1159,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: /connect ``` -3. 输入您的 MiniMax API 密钥。 +3. 输入你的 MiniMax API 密钥。 ```txt ┌ API key @@ -1182,7 +1168,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: └ enter ``` -4. 执行`/models`命令选择*M2.1*等模型。 +4. 执行 `/models` 命令选择模型,例如 _M2.1_。 ```txt /models @@ -1192,15 +1178,15 @@ IO.NET提供了17种针对各种例子进行优化的模型: ### Nebius Token Factory -1. Head over to the [Nebius Token Factory console](https://tokenfactory.nebius.com/), create an account, and click **Add Key**. +1. 前往 [Nebius Token Factory 控制台](https://tokenfactory.nebius.com/),创建账户并点击 **Add Key**。 -2. 执行`/connect`命令并搜索**NebiusTokens工厂**。 +2. 执行 `/connect` 命令并搜索 **Nebius Token Factory**。 ```txt /connect ``` -3. 输入您的 Nebius Tokens工厂 API 密钥。 +3. 输入你的 Nebius Token Factory API 密钥。 ```txt ┌ API key @@ -1209,7 +1195,7 @@ IO.NET提供了17种针对各种例子进行优化的模型: └ enter ``` -4. 执行 `/models` 命令以选择类似 _Kimi K2 Instruct_ 的模型。 +4. 执行 `/models` 命令选择模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -1219,10 +1205,10 @@ IO.NET提供了17种针对各种例子进行优化的模型: ### Ollama -您可以使用 Ollama 配置 opencode 本地模型。 +你可以通过 Ollama 配置 OpenCode 使用本地模型。 :::tip -Ollama can automatically configure itself for opencode. See the [Ollama integration docs](https://docs.ollama.com/integrations/opencode) for details. +Ollama 可以自动为 OpenCode 进行配置。详见 [Ollama 集成文档](https://docs.ollama.com/integrations/opencode)。 ::: ```json title="opencode.json" "ollama" {5, 6, 8, 10-14} @@ -1245,29 +1231,29 @@ Ollama can automatically configure itself for opencode. See the [Ollama integrat } ``` -在这个例子中: +在这个示例中: -- `ollama` 是自定义创建 ID。这可以是您想要的任何字符串。 -- `npm` specifies the package to use for this provider. Here, `@ai-sdk/openai-compatible` is used for any OpenAI-compatible API. -- `name` 是 UI 中提供商的显示名称。 -- `options.baseURL` 是本地服务器器的端点。 -- `models` 是模型 ID 以及配置的对应映射。模型名称将显示在模型选择列表中。 +- `ollama` 是自定义的提供商 ID,可以是任意字符串。 +- `npm` 指定该提供商使用的包。这里使用 `@ai-sdk/openai-compatible` 来兼容任何 OpenAI 兼容的 API。 +- `name` 是该提供商在 UI 中显示的名称。 +- `options.baseURL` 是本地服务器的端点地址。 +- `models` 是模型 ID 到其配置的映射。模型名称会显示在模型选择列表中。 :::tip -如果工具暂停,请尝试增加 Ollama 中的 `num_ctx`。从 16k - 32k 左右开始。 +如果工具调用不工作,请尝试增大 Ollama 中的 `num_ctx` 值。建议从 16k - 32k 左右开始。 ::: --- ### Ollama Cloud -相当于 Ollama Cloud 与 opencode 一起使用: +要在 OpenCode 中使用 Ollama Cloud: -1. 前往 [https://ollama.com/](https://ollama.com/) 并登录或建立账户。 +1. 前往 [https://ollama.com/](https://ollama.com/) 登录或创建账户。 -2. 导航至**设置** > **API 密钥**,然后单击**添加API 密钥**以生成新的API 密钥。 +2. 导航到 **Settings** > **Keys**,点击 **Add API Key** 生成新的 API 密钥。 -3. 复制 API 密钥以在 opencode 中使用。 +3. 复制 API 密钥以便在 OpenCode 中使用。 4. 执行 `/connect` 命令并搜索 **Ollama Cloud**。 @@ -1275,7 +1261,7 @@ Ollama can automatically configure itself for opencode. See the [Ollama integrat /connect ``` -5. 输入您的 Ollama Cloud API 密钥。 +5. 输入你的 Ollama Cloud API 密钥。 ```txt ┌ API key @@ -1284,13 +1270,13 @@ Ollama can automatically configure itself for opencode. See the [Ollama integrat └ enter ``` -6. **重要**:在opencode中使用云模型之前,必须将模型信息拉取到本地: +6. **重要**:在 OpenCode 中使用云端模型之前,必须先将模型信息拉取到本地: ```bash ollama pull gpt-oss:20b-cloud ``` -7. 执行 `/models` 命令以选择您的 Ollama Cloud 模型。 +7. 执行 `/models` 命令选择你的 Ollama Cloud 模型。 ```txt /models @@ -1300,16 +1286,15 @@ Ollama can automatically configure itself for opencode. See the [Ollama integrat ### OpenAI -We recommend signing up for [ChatGPT Plus or Pro](https://chatgpt.com/pricing). +我们建议注册 [ChatGPT Plus 或 Pro](https://chatgpt.com/pricing)。 -1. 注册后,执行`/connect`命令并选择OpenAI。 +1. 注册完成后,执行 `/connect` 命令并选择 OpenAI。 ```txt /connect ``` -2. 您可以选择 **ChatGPT Plus 或 Pro** 选项,就会在这里开启您的浏览器 - 并要求您进行身份验证。 +2. 你可以选择 **ChatGPT Plus/Pro** 选项,浏览器会自动打开并要求你进行身份验证。 ```txt ┌ Select auth method @@ -1319,23 +1304,23 @@ We recommend signing up for [ChatGPT Plus or Pro](https://chatgpt.com/pricing). └ ``` -3. 现在,当您使用 `/models` 命令时,所有 OpenAI 模型都应该可用。 +3. 现在使用 `/models` 命令即可看到所有 OpenAI 模型。 ```txt /models ``` -##### 使用 API 键 +##### 使用 API 密钥 -如果您已安装 API 密钥,则可以选择 **手动输入 API 密钥** 将其贴到终端中。 +如果你已经有 API 密钥,可以选择 **Manually enter API Key** 并将其粘贴到终端中。 --- ### OpenCode Zen -OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 [了解更多](/docs/zen)。 +OpenCode Zen 是由 OpenCode 团队提供的经过测试和验证的模型列表。[了解更多](/docs/zen)。 -1. 登录 **OpenCode Zen** 并单击 **创建 API 密钥**。 +1. 登录 **OpenCode Zen** 并点击 **Create API Key**。 2. 执行 `/connect` 命令并搜索 **OpenCode Zen**。 @@ -1343,7 +1328,7 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 /connect ``` -3. 输入您的 opencode API 密钥。 +3. 输入你的 OpenCode API 密钥。 ```txt ┌ API key @@ -1352,7 +1337,7 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 └ enter ``` -4. 执行`/models`命令选择*Qwen 3 Coder 480B*等模型。 +4. 执行 `/models` 命令选择模型,例如 _Qwen 3 Coder 480B_。 ```txt /models @@ -1362,15 +1347,15 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 ### OpenRouter -1. Head over to the [OpenRouter dashboard](https://openrouter.ai/settings/keys), click **Create API Key**, and copy the key. +1. 前往 [OpenRouter 仪表盘](https://openrouter.ai/settings/keys),点击 **Create API Key** 并复制密钥。 -2. 执行`/connect`命令并搜索OpenRouter。 +2. 执行 `/connect` 命令并搜索 OpenRouter。 ```txt /connect ``` -3. 输入结构的API 密钥。 +3. 输入该提供商的 API 密钥。 ```txt ┌ API key @@ -1379,13 +1364,13 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 └ enter ``` -4. 默认情况下预加载了多个OpenRouter模型,执行`/models`命令选择您想要的模型。 +4. 默认已预加载了许多 OpenRouter 模型,执行 `/models` 命令选择你想要的模型。 ```txt /models ``` - 您还可以通过opencode配置添加其他模型。 + 你也可以通过 OpenCode 配置添加更多模型。 ```json title="opencode.json" {6} { @@ -1400,7 +1385,7 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 } ``` -5. 您还可以使用opencode配置自定义它们。这是指定的示例 +5. 你还可以通过 OpenCode 配置自定义模型。以下是指定提供商的示例: ```json title="opencode.json" { @@ -1426,21 +1411,21 @@ OpenCode Zen 是 opencode 团队提供的经过测试和验证的模型列表。 ### SAP AI Core -SAP AI Core跨统一平台提供对OpenAI、Anthropic、Google、Amazon、Meta、Mistral和AI21的40多个模型的访问。 +SAP AI Core 通过统一平台提供对来自 OpenAI、Anthropic、Google、Amazon、Meta、Mistral 和 AI21 的 40+ 模型的访问。 -1. Go to your [SAP BTP Cockpit](https://account.hana.ondemand.com/), navigate to your SAP AI Core service instance, and create a service key. +1. 前往 [SAP BTP Cockpit](https://account.hana.ondemand.com/),导航到你的 SAP AI Core 服务实例,并创建服务密钥。 - :::提示 - The service key is a JSON object containing `clientid`, `clientsecret`, `url`, and `serviceurls.AI_API_URL`. You can find your AI Core instance under **Services** > **Instances and Subscriptions** in the BTP Cockpit. + :::tip + 服务密钥是一个包含 `clientid`、`clientsecret`、`url` 和 `serviceurls.AI_API_URL` 的 JSON 对象。你可以在 BTP Cockpit 的 **Services** > **Instances and Subscriptions** 下找到你的 AI Core 实例。 ::: -2. 执行`/connect`命令并搜索**SAP AI Core**。 +2. 执行 `/connect` 命令并搜索 **SAP AI Core**。 ```txt /connect ``` -3. 输入您的服务金号JSON。 +3. 输入你的服务密钥 JSON。 ```txt ┌ Service key @@ -1449,29 +1434,62 @@ SAP AI Core跨统一平台提供对OpenAI、Anthropic、Google、Amazon、Meta └ enter ``` - 或者设置`AICORE_SERVICE_KEY`环境变量: + 或者设置 `AICORE_SERVICE_KEY` 环境变量: ```bash AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"...","serviceurls":{"AI_API_URL":"..."}}' opencode ``` - 或者将其添加内容添加到您的 bash 配置文件中: + 或者添加到你的 bash 配置文件中: ```bash title="~/.bash_profile" export AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"...","serviceurls":{"AI_API_URL":"..."}}' ``` -4. (可选)设置部署ID和资源组: +4. 可选:设置部署 ID 和资源组: ```bash AICORE_DEPLOYMENT_ID=your-deployment-id AICORE_RESOURCE_GROUP=your-resource-group opencode ``` - :::笔记 - 这些设置是可选的,应根据 SAP AI Core 设置进行配置。 + :::note + 这些设置是可选的,应根据你的 SAP AI Core 配置进行设置。 + ::: + +5. 执行 `/models` 命令从 40+ 个可用模型中进行选择。 + + ```txt + /models + ``` + +--- + +### STACKIT + +STACKIT AI Model Serving 提供完全托管的主权托管环境,专注于 Llama、Mistral 和 Qwen 等大语言模型,在欧洲基础设施上实现最大程度的数据主权。 + +1. 前往 [STACKIT Portal](https://portal.stackit.cloud),导航到 **AI Model Serving**,为你的项目创建认证令牌。 + + :::tip + 你需要先拥有 STACKIT 客户账户、用户账户和项目,才能创建认证令牌。 ::: -5. 执行 `/models` 命令从 40 个多个可用模型中进行选择。 +2. 执行 `/connect` 命令并搜索 **STACKIT**。 + + ```txt + /connect + ``` + +3. 输入你的 STACKIT AI Model Serving 认证令牌。 + + ```txt + ┌ API key + │ + │ + └ enter + ``` + +4. 执行 `/models` 命令选择模型,例如 _Qwen3-VL 235B_ 或 _Llama 3.3 70B_。 ```txt /models @@ -1481,15 +1499,15 @@ SAP AI Core跨统一平台提供对OpenAI、Anthropic、Google、Amazon、Meta ### OVHcloud AI Endpoints -1. Head over to the [OVHcloud panel](https://ovh.com/manager). Navigate to the `Public Cloud` section, `AI & Machine Learning` > `AI Endpoints` and in `API Keys` tab, click **Create a new API key**. +1. 前往 [OVHcloud 管理面板](https://ovh.com/manager)。导航到 `Public Cloud` 部分,`AI & Machine Learning` > `AI Endpoints`,在 `API Keys` 标签页中点击 **Create a new API key**。 -2. 执行 `/connect` 命令并搜索 **OVHcloud AI 端点**。 +2. 执行 `/connect` 命令并搜索 **OVHcloud AI Endpoints**。 ```txt /connect ``` -3. 输入您的 OVHcloud AI 端点 API 密钥。 +3. 输入你的 OVHcloud AI Endpoints API 密钥。 ```txt ┌ API key @@ -1498,7 +1516,7 @@ SAP AI Core跨统一平台提供对OpenAI、Anthropic、Google、Amazon、Meta └ enter ``` -4. 执行`/models`命令选择*gpt-oss-120b*等模型。 +4. 执行 `/models` 命令选择模型,例如 _gpt-oss-120b_。 ```txt /models @@ -1508,9 +1526,9 @@ SAP AI Core跨统一平台提供对OpenAI、Anthropic、Google、Amazon、Meta ### Scaleway -To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-apis/) with Opencode: +要在 OpenCode 中使用 [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-apis/): -1. Head over to the [Scaleway Console IAM settings](https://console.scaleway.com/iam/api-keys) to generate a new API key. +1. 前往 [Scaleway Console IAM 设置](https://console.scaleway.com/iam/api-keys)生成新的 API 密钥。 2. 执行 `/connect` 命令并搜索 **Scaleway**。 @@ -1518,7 +1536,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap /connect ``` -3. 输入您的Scaleway API 密钥。 +3. 输入你的 Scaleway API 密钥。 ```txt ┌ API key @@ -1527,7 +1545,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap └ enter ``` -4. 执行 `/models` 命令选择 _devstral-2-123b-instruct-2512_ 或 _gpt-oss-120b_ 等模型。 +4. 执行 `/models` 命令选择模型,例如 _devstral-2-123b-instruct-2512_ 或 _gpt-oss-120b_。 ```txt /models @@ -1537,7 +1555,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap ### Together AI -1. Head over to the [Together AI console](https://api.together.ai), create an account, and click **Add Key**. +1. 前往 [Together AI 控制台](https://api.together.ai),创建账户并点击 **Add Key**。 2. 执行 `/connect` 命令并搜索 **Together AI**。 @@ -1545,7 +1563,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap /connect ``` -3. 输入您的Together AI API 密钥。 +3. 输入你的 Together AI API 密钥。 ```txt ┌ API key @@ -1554,7 +1572,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap └ enter ``` -4. 执行 `/models` 命令以选择类似 _Kimi K2 Instruct_ 的模型。 +4. 执行 `/models` 命令选择模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -1564,7 +1582,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap ### Venice AI -1. Head over to the [Venice AI console](https://venice.ai), create an account, and generate an API key. +1. 前往 [Venice AI 控制台](https://venice.ai),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **Venice AI**。 @@ -1572,7 +1590,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap /connect ``` -3. 输入您的威尼斯 AI API 密钥。 +3. 输入你的 Venice AI API 密钥。 ```txt ┌ API key @@ -1581,7 +1599,7 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap └ enter ``` -4. 执行`/models`命令选择*Llama 3.3 70B*等模型。 +4. 执行 `/models` 命令选择模型,例如 _Llama 3.3 70B_。 ```txt /models @@ -1591,9 +1609,9 @@ To use [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-ap ### Vercel AI Gateway -Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、Google、xAI 等的模型。模型按标价提供,不加价。 +Vercel AI Gateway 允许你通过统一端点访问来自 OpenAI、Anthropic、Google、xAI 等提供商的模型。模型按原价提供,不额外加价。 -1. Head over to the [Vercel dashboard](https://vercel.com/), navigate to the **AI Gateway** tab, and click **API keys** to create a new API key. +1. 前往 [Vercel 仪表盘](https://vercel.com/),导航到 **AI Gateway** 标签页,点击 **API keys** 创建新的 API 密钥。 2. 执行 `/connect` 命令并搜索 **Vercel AI Gateway**。 @@ -1601,7 +1619,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G /connect ``` -3. 输入您的 Vercel AI 网关 API 密钥。 +3. 输入你的 Vercel AI Gateway API 密钥。 ```txt ┌ API key @@ -1610,13 +1628,13 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ enter ``` -4. 执行`/models`命令选择模型。 +4. 执行 `/models` 命令选择模型。 ```txt /models ``` -您还可以穿透 opencode 配置自定义模型。以下是指定提供商路由顺序的示例。 +你也可以通过 OpenCode 配置自定义模型。以下是指定提供商路由顺序的示例。 ```json title="opencode.json" { @@ -1635,19 +1653,19 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G } ``` -一些有用的路由选项: +一些常用的路由选项: -| 选项 | 描述 | -| ------------------- | ---------------------- | -| `order` | 提供商尝试顺序 | -| `only` | 限制特定提供商 | -| `zeroDataRetention` | 仅使用零资料保留的政策 | +| 选项 | 描述 | +| ------------------- | -------------------------------- | +| `order` | 提供商尝试顺序 | +| `only` | 限制为特定提供商 | +| `zeroDataRetention` | 仅使用具有零数据留存策略的提供商 | --- ### xAI -1. Head over to the [xAI console](https://console.x.ai/), create an account, and generate an API key. +1. 前往 [xAI 控制台](https://console.x.ai/),创建账户并生成 API 密钥。 2. 执行 `/connect` 命令并搜索 **xAI**。 @@ -1655,7 +1673,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G /connect ``` -3. 输入您的 xAI API 密钥。 +3. 输入你的 xAI API 密钥。 ```txt ┌ API key @@ -1664,7 +1682,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ enter ``` -4. 执行 `/models` 命令来选择类似 _Grok Beta_ 的模型。 +4. 执行 `/models` 命令选择模型,例如 _Grok Beta_。 ```txt /models @@ -1674,7 +1692,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G ### Z.AI -1. Head over to the [Z.AI API console](https://z.ai/manage-apikey/apikey-list), create an account, and click **Create a new API key**. +1. 前往 [Z.AI API 控制台](https://z.ai/manage-apikey/apikey-list),创建账户并点击 **Create a new API key**。 2. 执行 `/connect` 命令并搜索 **Z.AI**。 @@ -1682,9 +1700,9 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G /connect ``` - 如果您订阅了**GLM编码计划**,请选择**Z.AI编码计划**。 + 如果你订阅了 **GLM Coding Plan**,请选择 **Z.AI Coding Plan**。 -3. 输入您的 Z.AI API 密钥。 +3. 输入你的 Z.AI API 密钥。 ```txt ┌ API key @@ -1693,7 +1711,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ enter ``` -4. 执行`/models`命令选择*GLM-4.7*等模型。 +4. 执行 `/models` 命令选择模型,例如 _GLM-4.7_。 ```txt /models @@ -1703,7 +1721,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G ### ZenMux -1. Head over to the [ZenMux dashboard](https://zenmux.ai/settings/keys), click **Create API Key**, and copy the key. +1. 前往 [ZenMux 仪表盘](https://zenmux.ai/settings/keys),点击 **Create API Key** 并复制密钥。 2. 执行 `/connect` 命令并搜索 ZenMux。 @@ -1711,7 +1729,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G /connect ``` -3. 输入结构的API 密钥。 +3. 输入该提供商的 API 密钥。 ```txt ┌ API key @@ -1720,13 +1738,13 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ enter ``` -4. 默认情况下预加载了多个 ZenMux 模型,执行 `/models` 命令选择您想要的模型。 +4. 默认已预加载了许多 ZenMux 模型,执行 `/models` 命令选择你想要的模型。 ```txt /models ``` - 您还可以通过opencode配置添加其他模型。 + 你也可以通过 OpenCode 配置添加更多模型。 ```json title="opencode.json" {6} { @@ -1748,10 +1766,10 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G 要添加 `/connect` 命令中未列出的任何 **OpenAI 兼容**提供商: :::tip -您可以将任何 OpenAI 兼容的提供商与 opencode 一起使用。大多数 AI 提供商都提供 OpenAI 兼容 API。 +你可以在 OpenCode 中使用任何 OpenAI 兼容的提供商。大多数现代 AI 提供商都提供 OpenAI 兼容的 API。 ::: -1. 执行`/connect`命令并逐步升级到**其他**。 +1. 执行 `/connect` 命令,向下滚动到 **Other**。 ```bash $ /connect @@ -1764,7 +1782,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ ``` -2. 输入企业的唯一ID。 +2. 输入该提供商的唯一 ID。 ```bash $ /connect @@ -1776,11 +1794,11 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ ``` - :::笔记 - 选择一个容易记住的 ID,您将在配置文件中使用它。 + :::note + 请选择一个容易记住的 ID,你将在配置文件中使用它。 ::: -3. 输入您的事业的 API 密钥。 +3. 输入该提供商的 API 密钥。 ```bash $ /connect @@ -1794,7 +1812,7 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G └ ``` -4. Create or update your `opencode.json` file in your project directory: +4. 在项目目录中创建或更新 `opencode.json` 文件: ```json title="opencode.json" ""myprovider"" {5-15} { @@ -1816,21 +1834,21 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G } ``` - 以下是配置选项: - - **npm**:要使用AI的SDK包,`@ai-sdk/openai-compatible`用于OpenAI兼容的事业 - - **名称**:UI中的显示名称。 - - **模型**:可用模型。 + 以下是配置选项说明: + - **npm**:要使用的 AI SDK 包,对于 OpenAI 兼容的提供商使用 `@ai-sdk/openai-compatible` + - **name**:在 UI 中显示的名称。 + - **models**:可用模型。 - **options.baseURL**:API 端点 URL。 - - **options.apiKey**:如果不使用身份验证,可以选择设置API 密钥。 - - **options.headers**:可选择设置自定义标头。 + - **options.apiKey**:可选,如果不使用 auth 认证,可直接设置 API 密钥。 + - **options.headers**:可选,设置自定义请求头。 - 有关高阶选项的更多资讯,请参见下面的示例。 + 更多高级选项请参见下面的示例。 -5. 执行 `/models` 命令,您提供的自定义程序和模型将出现在选择列表中。 +5. 执行 `/models` 命令,你自定义的提供商和模型将出现在选择列表中。 --- -##### 例子 +##### 示例 以下是设置 `apiKey`、`headers` 和模型 `limit` 选项的示例。 @@ -1864,25 +1882,24 @@ Vercel AI Gateway 可以让您跨统一端点访问来自 OpenAI、Anthropic、G 配置详情: -- **apiKey**:使用`env`变数语法[了解更多](/docs/config#env-vars)设置。 -- ** headers **:随每个请求传送的自定义标头。 -- **limit.context**:模型接受的最大输入标记。 -- **limit.output**:模型可以生成的最大Tokens。 +- **apiKey**:使用 `env` 变量语法设置,[了解更多](/docs/config#env-vars)。 +- **headers**:随每个请求发送的自定义请求头。 +- **limit.context**:模型接受的最大输入 Token 数。 +- **limit.output**:模型可生成的最大 Token 数。 -`limit` 栏位允许 opencode 了解您还剩下多少上下文。标准提供商会自动从 models.dev 中提取这些内容。 +`limit` 字段让 OpenCode 了解你还剩余多少上下文空间。标准提供商会自动从 models.dev 拉取这些信息。 --- ## 故障排除 -如果您在配置提供商时遇到问题,请检查以下内容: +如果你在配置提供商时遇到问题,请检查以下几点: -1. **Check the auth setup**: Run `opencode auth list` to see if the credentials - 提供商的配置已添加到您的配置中。 +1. **检查认证设置**:运行 `opencode auth list` 查看该提供商的凭据是否已添加到配置中。 - 这并不利于 Amazon Bedrock 等依赖环境变数进行身份验证的工作。 + 这不适用于 Amazon Bedrock 等依赖环境变量进行认证的提供商。 -2. 对于自定义提供的程序,请检查 opencode 配置并: - - 确保 `/connect` 命令中使用的提供商 ID 与 opencode 配置中的 ID 匹配。 - - 正确的 npm 包用于提供商。例如,对 Cerebras 使用 `@ai-sdk/cerebras`。对于所有其他 OpenAI 相内容的提供商,请使用 `@ai-sdk/openai-compatible`。 - - 检查 `options.baseURL` 栏位中使用的 API 端点是否正确。 +2. 对于自定义提供商,请检查 OpenCode 配置并确认: + - `/connect` 命令中使用的提供商 ID 与 OpenCode 配置中的 ID 一致。 + - 使用了正确的 npm 包。例如,Cerebras 应使用 `@ai-sdk/cerebras`。对于其他所有 OpenAI 兼容的提供商,使用 `@ai-sdk/openai-compatible`。 + - `options.baseURL` 字段中的 API 端点地址正确。 diff --git a/packages/web/src/content/docs/zh-cn/rules.mdx b/packages/web/src/content/docs/zh-cn/rules.mdx index 0ad73b33ff1f..9ce5c53de8bf 100644 --- a/packages/web/src/content/docs/zh-cn/rules.mdx +++ b/packages/web/src/content/docs/zh-cn/rules.mdx @@ -1,29 +1,29 @@ --- title: 规则 -description: 设置opencode的自定义指令。 +description: 为 opencode 设置自定义指令。 --- -您可以通过 `AGENTS.md` 文件创建 opencode 的自定义指令。这和 Cursor 的规则类似。它包含将包含在 LLM 上下文中的说明,方便您的特定项目自定义其行为。 +您可以通过创建 `AGENTS.md` 文件来为 opencode 提供自定义指令。这类似于 Cursor 的规则功能。该文件包含的指令会被纳入 LLM 的上下文中,以便针对您的特定项目自定义其行为。 --- ## 初始化 -要创建新的`AGENTS.md`文件,您可以在opencode中运行`/init`命令。 +要创建新的 `AGENTS.md` 文件,您可以在 opencode 中运行 `/init` 命令。 :::tip 您应该将项目的 `AGENTS.md` 文件提交到 Git。 ::: -这将扫描您的项目及其所有内容,以了解该项目的内容并生成一个 `AGENTS.md` 文件。这有助于更好地打开代码导航项目。 +该命令会扫描您的项目及其所有内容,了解项目的用途,并据此生成一个 `AGENTS.md` 文件。这有助于 opencode 更好地导航您的项目。 -如果您已有现有的 `AGENTS.md` 文件,将尝试添加到其中。 +如果您已有 `AGENTS.md` 文件,该命令会尝试在其基础上进行补充。 --- -## 例子 +## 示例 -您也可以手动创建此文件。以下是您可以导入 `AGENTS.md` 文件中的一些内容的示例。 +您也可以手动创建此文件。以下是一些可以放入 `AGENTS.md` 文件中的内容示例。 ```markdown title="AGENTS.md" # SST v3 Monorepo Project @@ -48,33 +48,33 @@ This is an SST v3 monorepo with TypeScript. The project uses bun workspaces for - Import shared modules using workspace names: `@my-app/core/example` ``` -我们在此处添加特定于项目的说明,这将在您的团队中共享。 +我们在这里添加了项目特定的指令,这些指令会在您的团队中共享。 --- ## 类型 -opencode 还支持从多个位置读取 `AGENTS.md` 文件。这有不同的目的。 +opencode 还支持从多个位置读取 `AGENTS.md` 文件,不同的位置有不同的用途。 -### 项目 +### 项目级 -将 `AGENTS.md` 放置在项目根目录中以获取特定于项目的规则。这些仅适用于您在此目录或子目录中工作时。 +在项目根目录放置一个 `AGENTS.md` 文件,用于定义项目特定的规则。这些规则仅在您在该目录或其子目录中工作时生效。 -### 全局 +### 全局级 -您还可以在 `~/.config/opencode/AGENTS.md` 文件中包含全局规则。这适用于所有opencode会话。 +您还可以在 `~/.config/opencode/AGENTS.md` 文件中设置全局规则。这些规则会应用于所有 opencode 会话。 -由于此未提交给 Git 或与您的团队共享,因此我们建议使用它来指定 LLM 应遵循的任何个人规则。 +由于该文件不会被提交到 Git 或与团队共享,我们建议用它来指定 LLM 应遵循的个人规则。 -### 克劳德代码兼容性 +### Claude Code 兼容性 -Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know. +对于从 Claude Code 迁移过来的用户,OpenCode 支持 Claude Code 的文件约定作为回退方案: -- **项目规则**:项目目录中的`CLAUDE.md`(如果`AGENTS.md`不存在则使用) -- **全局规则**:`~/.claude/CLAUDE.md`(如果不存在`~/.config/opencode/AGENTS.md`则使用) -- **技能**:`~/.claude/skills/` — 详情请参见[代理技巧](/docs/skills/) +- **项目规则**:项目目录中的 `CLAUDE.md`(在没有 `AGENTS.md` 的情况下使用) +- **全局规则**:`~/.claude/CLAUDE.md`(在没有 `~/.config/opencode/AGENTS.md` 的情况下使用) +- **技能**:`~/.claude/skills/` — 详情请参阅[代理技能](/docs/skills/) -要取消Claude Code兼容性,请设置以下环境变量之一: +要禁用 Claude Code 兼容性,请设置以下环境变量之一: ```bash export OPENCODE_DISABLE_CLAUDE_CODE=1 # Disable all .claude support @@ -86,21 +86,21 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills ## 优先级 -当opencode启动时,它会按以下顺序查找规则文件: +当 opencode 启动时,它会按以下顺序查找规则文件: -1. **本地文件**,从当前目录向上浏览(`AGENTS.md`,`CLAUDE.md`) -2. **全局文件** `~/.config/opencode/AGENTS.md` -3. **克劳德代码文件**位于`~/.claude/CLAUDE.md`(禁用禁用) +1. **本地文件**,从当前目录向上遍历(`AGENTS.md`、`CLAUDE.md`) +2. **全局文件**,位于 `~/.config/opencode/AGENTS.md` +3. **Claude Code 文件**,位于 `~/.claude/CLAUDE.md`(除非已禁用) -第一个匹配的文件每个在类别中触发。例如,如果您同时拥有`AGENTS.md`和`CLAUDE.md`,则仅使用`AGENTS.md`。同样,`~/.config/opencode/AGENTS.md`优先于`~/.claude/CLAUDE.md`。 +在每个类别中,第一个匹配的文件优先。例如,如果您同时拥有 `AGENTS.md` 和 `CLAUDE.md`,则只会使用 `AGENTS.md`。同样,`~/.config/opencode/AGENTS.md` 优先于 `~/.claude/CLAUDE.md`。 --- ## 自定义指令 -您可以在 `opencode.json` 或全局 `~/.config/opencode/opencode.json` 中指定自定义指令文件。这允许您和您的团队重用现有规则,而不必将它们复制到 AGENTS.md。 +您可以在 `opencode.json` 或全局配置文件 `~/.config/opencode/opencode.json` 中指定自定义指令文件。这允许您和团队复用现有规则,而无需将它们复制到 AGENTS.md 中。 -例子: +示例: ```json title="opencode.json" { @@ -109,7 +109,7 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -您还可以使用远程URL从Web加载说明。 +您还可以使用远程 URL 从网络加载指令。 ```json title="opencode.json" { @@ -118,19 +118,19 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -远程指令的获取有 5 秒的超时时间。 +远程指令的获取超时时间为 5 秒。 -所有说明文件均与您的`AGENTS.md`文件合并。 +所有指令文件都会与您的 `AGENTS.md` 文件合并。 --- ## 引用外部文件 -虽然opencode不会自动解析`AGENTS.md`中的文件引用,但您可以通过两种方式实现类似的功能: +虽然 opencode 不会自动解析 `AGENTS.md` 中的文件引用,但您可以通过以下两种方式实现类似的功能: ### 使用 opencode.json -推荐的方法是在`instructions`中使用`opencode.json`字段: +推荐的方式是使用 `opencode.json` 中的 `instructions` 字段: ```json title="opencode.json" { @@ -139,9 +139,9 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -### AGENTS.md 中的手册说明 +### 在 AGENTS.md 中手动指定 -您可以通过在 `AGENTS.md` 中明确提供的指令来教 opencode 读取外部文件。这是一个实际的示例: +您可以在 `AGENTS.md` 中提供明确的指令,教 opencode 读取外部文件。以下是一个实际示例: ```markdown title="AGENTS.md" # TypeScript Project Rules @@ -168,13 +168,13 @@ For testing strategies and coverage requirements: @test/testing-guidelines.md Read the following file immediately as it's relevant to all workflows: @rules/general-guidelines.md. ``` -这种方法允许您: +这种方式允许您: -- 创建模块化、可重用的规则文件 -- 通过符号链接或git子模块在项目之间共享规则 -- 保持 AGENTS.md 简洁,同时参考详细指南 -- 确保opencode仅在特定任务需要时加载文件 +- 创建模块化、可复用的规则文件 +- 通过符号链接或 Git 子模块在项目之间共享规则 +- 保持 AGENTS.md 简洁,同时引用详细的指南 +- 确保 opencode 仅在特定任务需要时才加载文件 :::tip -对于 monorepos 或具有共享标准的项目,使用 `opencode.json` 和 glob 模式(如 `packages/*/AGENTS.md`)比手动指令更易于维护。 +对于 monorepo 或具有共享标准的项目,使用 `opencode.json` 配合 glob 模式(如 `packages/*/AGENTS.md`)比手动指定指令更易于维护。 ::: diff --git a/packages/web/src/content/docs/zh-cn/sdk.mdx b/packages/web/src/content/docs/zh-cn/sdk.mdx index 4b6929b627f1..121e423d10d9 100644 --- a/packages/web/src/content/docs/zh-cn/sdk.mdx +++ b/packages/web/src/content/docs/zh-cn/sdk.mdx @@ -1,21 +1,21 @@ --- title: SDK -description: opencode 服务器的类型不同于安全 JS 客户端。 +description: opencode 服务器的类型安全 JS 客户端。 --- import config from "../../../../config.mjs" export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts` -opencode JS/TS SDK 提供类型其他安全的客户端用于与服务器交互。 -使用它以程序设计方式构建集成和控制opencode。 +opencode JS/TS SDK 提供了一个类型安全的客户端,用于与服务器进行交互。 +你可以用它来构建集成方案,并以编程方式控制 opencode。 -[了解更多关于服务器如何工作的](/docs/server)。例如,查看社区构建的[projects](/docs/ecosystem#projects)。 +[了解更多](/docs/server)关于服务器的工作原理。如需示例,请查看社区构建的[项目](/docs/ecosystem#projects)。 --- ## 安装 -从npm安装SDK: +从 npm 安装 SDK: ```bash npm install @opencode-ai/sdk @@ -25,7 +25,7 @@ npm install @opencode-ai/sdk ## 创建客户端 -创建opencode的示例项: +创建一个 opencode 实例: ```javascript import { createOpencode } from "@opencode-ai/sdk" @@ -33,23 +33,23 @@ import { createOpencode } from "@opencode-ai/sdk" const { client } = await createOpencode() ``` -这会同时启动服务器和客户端 +这会同时启动服务器和客户端。 #### 选项 -| 选项 | 型别 | 描述 | 默认 | -| ---------- | ------------- | ------------------------------ | ----------- | -| `hostname` | `string` | 服务器主机名 | `127.0.0.1` | -| `port` | `number` | 服务器埠 | `4096` | -| `signal` | `AbortSignal` | 取消的中止讯号 | `undefined` | -| `timeout` | `number` | 服务器启动超时(以毫秒为单位) | `5000` | -| `config` | `Config` | 放置的财产 | `{}` | +| 选项 | 类型 | 描述 | 默认值 | +| ---------- | ------------- | -------------------------- | ----------- | +| `hostname` | `string` | 服务器主机名 | `127.0.0.1` | +| `port` | `number` | 服务器端口 | `4096` | +| `signal` | `AbortSignal` | 用于取消操作的中止信号 | `undefined` | +| `timeout` | `number` | 服务器启动超时时间(毫秒) | `5000` | +| `config` | `Config` | 配置对象 | `{}` | --- ## 配置 -You can pass a configuration object to customize behavior. The instance still picks up your `opencode.json`, but you can override or add configuration inline: +你可以传入一个配置对象来自定义行为。实例仍然会读取你的 `opencode.json`,但你可以通过内联方式覆盖或添加配置: ```javascript import { createOpencode } from "@opencode-ai/sdk" @@ -67,9 +67,9 @@ console.log(`Server running at ${opencode.server.url}`) opencode.server.close() ``` -## 仅限客户端 +## 仅客户端模式 -如果您已经有 opencode 的正在执行示例项,则可以创建一个客户端示例项来连线到它: +如果你已经有一个正在运行的 opencode 实例,可以创建一个客户端实例来连接它: ```javascript import { createOpencodeClient } from "@opencode-ai/sdk" @@ -81,31 +81,31 @@ const client = createOpencodeClient({ #### 选项 -| 选项 | 型别 | 描述 | 默认 | +| 选项 | 类型 | 描述 | 默认值 | | --------------- | ---------- | ---------------------------- | ----------------------- | -| `baseUrl` | `string` | 服务器的 URL | `http://localhost:4096` | -| `fetch` | `function` | 习俗获取实现 | `globalThis.fetch` | -| `parseAs` | `string` | 响应解析方法 | `auto` | -| `responseStyle` | `string` | 返回样式:`data` 或 `fields` | `fields` | -| `throwOnError` | `boolean` | 掷骰错误而不是返回 | `false` | +| `baseUrl` | `string` | 服务器 URL | `http://localhost:4096` | +| `fetch` | `function` | 自定义 fetch 实现 | `globalThis.fetch` | +| `parseAs` | `string` | 响应解析方式 | `auto` | +| `responseStyle` | `string` | 返回风格:`data` 或 `fields` | `fields` | +| `throwOnError` | `boolean` | 抛出错误而非返回错误 | `false` | --- ## 类型 -SDK 包括所有 API 型以外的 TypeScript 定义。直接汇入其中: +SDK 包含所有 API 类型的 TypeScript 定义。你可以直接导入它们: ```typescript import type { Session, Message, Part } from "@opencode-ai/sdk" ``` -所有型别均根据服务器的 OpenAPI 规范生成,并可在 型别文件 中找到。 +所有类型均根据服务器的 OpenAPI 规范生成,可在类型文件中查看。 --- -## 错误 +## 错误处理 -SDK 可能会丢掷错误,您可以捕获并处理这些错误: +SDK 可能会抛出错误,你可以捕获并处理这些错误: ```typescript try { @@ -117,17 +117,89 @@ try { --- +## 结构化输出 + +你可以通过指定带有 JSON Schema 的 `format` 来请求模型返回结构化的 JSON 输出。模型会使用 `StructuredOutput` 工具返回符合你 Schema 的经过验证的 JSON。 + +### 基本用法 + +```typescript +const result = await client.session.prompt({ + path: { id: sessionId }, + body: { + parts: [{ type: "text", text: "Research Anthropic and provide company info" }], + format: { + type: "json_schema", + schema: { + type: "object", + properties: { + company: { type: "string", description: "Company name" }, + founded: { type: "number", description: "Year founded" }, + products: { + type: "array", + items: { type: "string" }, + description: "Main products", + }, + }, + required: ["company", "founded"], + }, + }, + }, +}) + +// Access the structured output +console.log(result.data.info.structured_output) +// { company: "Anthropic", founded: 2021, products: ["Claude", "Claude API"] } +``` + +### 输出格式类型 + +| 类型 | 描述 | +| ------------- | --------------------------------------- | +| `text` | 默认值。标准文本响应(无结构化输出) | +| `json_schema` | 返回符合所提供 Schema 的经过验证的 JSON | + +### JSON Schema 格式 + +使用 `type: 'json_schema'` 时,需提供以下字段: + +| 字段 | 类型 | 描述 | +| ------------ | --------------- | ------------------------------------- | +| `type` | `'json_schema'` | 必填。指定 JSON Schema 模式 | +| `schema` | `object` | 必填。定义输出结构的 JSON Schema 对象 | +| `retryCount` | `number` | 可选。验证重试次数(默认值:2) | + +### 错误处理 + +如果模型在所有重试后仍无法生成有效的结构化输出,响应中会包含 `StructuredOutputError`: + +```typescript +if (result.data.info.error?.name === "StructuredOutputError") { + console.error("Failed to produce structured output:", result.data.info.error.message) + console.error("Attempts:", result.data.info.error.retries) +} +``` + +### 最佳实践 + +1. **在 Schema 属性中提供清晰的描述**,帮助模型理解需要提取的数据 +2. **使用 `required`** 指定哪些字段必须存在 +3. **保持 Schema 简洁** — 复杂的嵌套 Schema 可能会让模型更难正确填充 +4. **设置合适的 `retryCount`** — 对于复杂 Schema 可增加重试次数,对于简单 Schema 可减少 + +--- + ## API -SDK跨越型别安全客户端公开所有服务器API。 +SDK 通过类型安全的客户端暴露所有服务器 API。 --- -### 全局 +### Global -| 方法 | 描述 | 回应 | +| 方法 | 描述 | 响应 | | ----------------- | ------------------------ | ------------------------------------ | -| `global.health()` | 检查服务器健康状况和版本 | `{ healthy: true, version: string }` | +| `global.health()` | 检查服务器健康状态和版本 | `{ healthy: true, version: string }` | --- @@ -140,12 +212,12 @@ console.log(health.data.version) --- -### 应用程序 +### App -| 方法 | 描述 | 回应 | -| -------------- | ------------------ | ------------------------------------------ | -| `app.log()` | 登录日志 | `boolean` | -| `app.agents()` | 列出所有可用的代理 | 代理[] | +| 方法 | 描述 | 响应 | +| -------------- | ------------------ | ------------------------------------------- | +| `app.log()` | 写入一条日志 | `boolean` | +| `app.agents()` | 列出所有可用的代理 | Agent[] | --- @@ -167,12 +239,12 @@ const agents = await client.app.agents() --- -### 项目 +### Project -| 方法 | 描述 | 回应 | -| ------------------- | ------------ | ------------------------------------------ | -| `project.list()` | 列出所有专案 | 专案[] | -| `project.current()` | 获取当前专案 | 专案 | +| 方法 | 描述 | 响应 | +| ------------------- | ------------ | --------------------------------------------- | +| `project.list()` | 列出所有项目 | Project[] | +| `project.current()` | 获取当前项目 | Project | --- @@ -188,11 +260,11 @@ const currentProject = await client.project.current() --- -### 路径 +### Path -| 方法 | 描述 | 回应 | +| 方法 | 描述 | 响应 | | ------------ | ------------ | ---------------------------------------- | -| `path.get()` | 获取当前路径 | 路径 | +| `path.get()` | 获取当前路径 | Path | --- @@ -205,12 +277,12 @@ const pathInfo = await client.path.get() --- -### 配置 +### Config -| 方法 | 描述 | 回应 | -| -------------------- | -------------------- | --------------------------------------------------------------------------------------------------- | -| `config.get()` | 获取配置资讯 | 配置 | -| `config.providers()` | 列出提供商和默认模型 | `{ providers: `提供商[]`, default: { [key: string]: string } }` | +| 方法 | 描述 | 响应 | +| -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | +| `config.get()` | 获取配置信息 | Config | +| `config.providers()` | 列出提供商和默认模型 | `{ providers: `Provider[]`, default: { [key: string]: string } }` | --- @@ -224,29 +296,29 @@ const { providers, default: defaults } = await client.config.providers() --- -### 会话 - -| 方法 | 描述 | 备注 | -| ---------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| `session.list()` | 列出会话 | 返回 Session[] | -| `session.get({ path })` | 获取会话 | 返回 会话 | -| `session.children({ path })` | 列出子会话 | 返回 Session[] | -| `session.create({ body })` | 建立会话 | 返回 会话 | -| `session.delete({ path })` | 离开会话 | 返回`boolean` | -| `session.update({ path, body })` | 更新会话属性 | 返回 会话 | -| `session.init({ path, body })` | Analyze app and create `AGENTS.md` | Returns `boolean` | -| `session.abort({ path })` | 中止正在执行的会话 | 返回`boolean` | -| `session.share({ path })` | 分享会 | 返回 会话 | -| `session.unshare({ path })` | 取消共享会话 | 返回 会话 | -| `session.summarize({ path, body })` | 会议总结 | 返回`boolean` | -| `session.messages({ path })` | 列出会话中的消息 | 返回 `{ info: `消息`, parts: `部分[]`}[]` | -| `session.message({ path })` | 获取消息详情 | 返回 `{ info: `消息`, parts: `部分[]`}` | -| `session.prompt({ path, body })` | 发送提示资讯 | `body.noReply: true` 返回 UserMessage(仅限上下文)。默认返回 AssistantMessage 以及 AI 响应 | -| `session.command({ path, body })` | 向会话发送命令 | 返回 `{ info: `AssistantMessage`, parts: `部分[]`}` | -| `session.shell({ path, body })` | 执行 shell 命令 | 返回 AssistantMessage | -| `session.revert({ path, body })` | 回复消息 | 返回 会话 | -| `session.unrevert({ path })` | 恢复已恢复的消息 | 返回 会话 | -| `postSessionByIdPermissionsByPermissionId({ path, body })` | 回复许可权限请求 | 返回`boolean` | +### Sessions + +| 方法 | 描述 | 备注 | +| ---------------------------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `session.list()` | 列出会话 | 返回 Session[] | +| `session.get({ path })` | 获取会话 | 返回 Session | +| `session.children({ path })` | 列出子会话 | 返回 Session[] | +| `session.create({ body })` | 创建会话 | 返回 Session | +| `session.delete({ path })` | 删除会话 | 返回 `boolean` | +| `session.update({ path, body })` | 更新会话属性 | 返回 Session | +| `session.init({ path, body })` | 分析应用并创建 `AGENTS.md` | 返回 `boolean` | +| `session.abort({ path })` | 中止正在运行的会话 | 返回 `boolean` | +| `session.share({ path })` | 分享会话 | 返回 Session | +| `session.unshare({ path })` | 取消分享会话 | 返回 Session | +| `session.summarize({ path, body })` | 总结会话 | 返回 `boolean` | +| `session.messages({ path })` | 列出会话中的消息 | 返回 `{ info: `Message`, parts: `Part[]`}[]` | +| `session.message({ path })` | 获取消息详情 | 返回 `{ info: `Message`, parts: `Part[]`}` | +| `session.prompt({ path, body })` | 发送提示词消息 | `body.noReply: true` 返回 UserMessage(仅注入上下文)。默认返回带有 AI 响应的 AssistantMessage。支持通过 `body.outputFormat` 使用[结构化输出](#结构化输出) | +| `session.command({ path, body })` | 向会话发送命令 | 返回 `{ info: `AssistantMessage`, parts: `Part[]`}` | +| `session.shell({ path, body })` | 执行 shell 命令 | 返回 AssistantMessage | +| `session.revert({ path, body })` | 撤回消息 | 返回 Session | +| `session.unrevert({ path })` | 恢复已撤回的消息 | 返回 Session | +| `postSessionByIdPermissionsByPermissionId({ path, body })` | 响应权限请求 | 返回 `boolean` | --- @@ -281,21 +353,21 @@ await client.session.prompt({ --- -### 文件 +### Files -| 方法 | 描述 | 回应 | +| 方法 | 描述 | 响应 | | ------------------------- | -------------------- | ----------------------------------------------------------------------------------- | -| `find.text({ query })` | 搜索档案中文字 | 具有 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的匹配对象数组 | -| `find.files({ query })` | 按名称查询档案和目录 | `string[]`(路径) | -| `find.symbols({ query })` | 查询工作区符号 | 符号[] | -| `file.read({ query })` | 读取档案 | `{ type: "raw" \| "patch", content: string }` | -| `file.status({ query? })` | 获取跟踪文件的状态 | 文件[] | +| `find.text({ query })` | 搜索文件中的文本 | 包含 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的匹配对象数组 | +| `find.files({ query })` | 按名称查找文件和目录 | `string[]`(路径) | +| `find.symbols({ query })` | 查找工作区符号 | Symbol[] | +| `file.read({ query })` | 读取文件 | `{ type: "raw" \| "patch", content: string }` | +| `file.status({ query? })` | 获取已跟踪文件的状态 | File[] | -`find.files` 支持一些可选的查询栏位: +`find.files` 支持以下可选查询字段: - `type`:`"file"` 或 `"directory"` -- `directory`:覆盖搜索的专案根目录 -- `limit`:最大结果 (1–200) +- `directory`:覆盖搜索的项目根目录 +- `limit`:最大结果数(1–200) --- @@ -324,17 +396,17 @@ const content = await client.file.read({ ### TUI -| 方法 | 描述 | 回应 | +| 方法 | 描述 | 响应 | | ------------------------------ | ---------------- | --------- | -| `tui.appendPrompt({ body })` | 将文字附加到提示 | `boolean` | -| `tui.openHelp()` | 开启帮助对话方块 | `boolean` | -| `tui.openSessions()` | 开启会话选择器 | `boolean` | -| `tui.openThemes()` | 开启主题选择器 | `boolean` | -| `tui.openModels()` | 开启模型选择器 | `boolean` | -| `tui.submitPrompt()` | 提交当前提示 | `boolean` | -| `tui.clearPrompt()` | 清除提示 | `boolean` | +| `tui.appendPrompt({ body })` | 向提示词追加文本 | `boolean` | +| `tui.openHelp()` | 打开帮助对话框 | `boolean` | +| `tui.openSessions()` | 打开会话选择器 | `boolean` | +| `tui.openThemes()` | 打开主题选择器 | `boolean` | +| `tui.openModels()` | 打开模型选择器 | `boolean` | +| `tui.submitPrompt()` | 提交当前提示词 | `boolean` | +| `tui.clearPrompt()` | 清除提示词 | `boolean` | | `tui.executeCommand({ body })` | 执行命令 | `boolean` | -| `tui.showToast({ body })` | 显示吐司通知 | `boolean` | +| `tui.showToast({ body })` | 显示 Toast 通知 | `boolean` | --- @@ -353,11 +425,11 @@ await client.tui.showToast({ --- -### 授权 +### Auth -| 方法 | 描述 | 回应 | -| ------------------- | ---------------- | --------- | -| `auth.set({ ... })` | 设定身份验证凭据 | `boolean` | +| 方法 | 描述 | 响应 | +| ------------------- | ------------ | --------- | +| `auth.set({ ... })` | 设置认证凭据 | `boolean` | --- @@ -372,11 +444,11 @@ await client.auth.set({ --- -### 事件 +### Events -| 方法 | 描述 | 回应 | +| 方法 | 描述 | 响应 | | ------------------- | ------------------ | ------------------ | -| `event.subscribe()` | 服务器传送的事件流 | 服务器传送的事件流 | +| `event.subscribe()` | 服务器发送的事件流 | 服务器发送的事件流 | --- diff --git a/packages/web/src/content/docs/zh-cn/server.mdx b/packages/web/src/content/docs/zh-cn/server.mdx index fafff87d7c3a..d28342ecc69a 100644 --- a/packages/web/src/content/docs/zh-cn/server.mdx +++ b/packages/web/src/content/docs/zh-cn/server.mdx @@ -6,7 +6,7 @@ description: 通过 HTTP 与 opencode 服务器交互。 import config from "../../../../config.mjs" export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts` -The `opencode serve` command runs a headless HTTP server that exposes an OpenAPI endpoint that an opencode client can use. +`opencode serve` 命令运行一个无界面的 HTTP 服务器,暴露一个 OpenAPI 端点供 opencode 客户端使用。 --- @@ -18,15 +18,15 @@ opencode serve [--port ] [--hostname ] [--cors ] #### 选项 -| 标志 | 描述 | 默认 | -| --------------- | ----------------------------------- | ---------------- | -| `--port` | 监听音频 | `4096` | -| `--hostname` | 监听的主机名 | `127.0.0.1` | -| `--mdns` | 启用 mDNS 发现 | `false` | -| `--mdns-domain` | Custom domain name for mDNS service | `opencode.local` | -| `--cors` | 允许的其他浏览器来源 | `[]` | +| 标志 | 描述 | 默认值 | +| --------------- | --------------------- | ---------------- | +| `--port` | 监听端口 | `4096` | +| `--hostname` | 监听的主机名 | `127.0.0.1` | +| `--mdns` | 启用 mDNS 发现 | `false` | +| `--mdns-domain` | mDNS 服务的自定义域名 | `opencode.local` | +| `--cors` | 额外允许的浏览器来源 | `[]` | -`--cors` 可以多次交付: +`--cors` 可以多次传递: ```bash opencode serve --cors http://localhost:5173 --cors https://app.example.com @@ -34,9 +34,9 @@ opencode serve --cors http://localhost:5173 --cors https://app.example.com --- -### 验证 +### 认证 -Set `OPENCODE_SERVER_PASSWORD` to protect the server with HTTP basic auth. The username defaults to `opencode`, or set `OPENCODE_SERVER_USERNAME` to override it. This applies to both `opencode serve` and `opencode web`. +设置 `OPENCODE_SERVER_PASSWORD` 以使用 HTTP 基本认证保护服务器。用户名默认为 `opencode`,也可以设置 `OPENCODE_SERVER_USERNAME` 来覆盖它。这适用于 `opencode serve` 和 `opencode web`。 ```bash OPENCODE_SERVER_PASSWORD=your-password opencode serve @@ -44,244 +44,241 @@ OPENCODE_SERVER_PASSWORD=your-password opencode serve --- -### 它是如何运作的 +### 工作原理 -When you run `opencode` it starts a TUI and a server. Where the TUI is the -与服务器器对话的客户端。服务器器公开 OpenAPI 3.1 规范 -该端点还用于生成 [SDK](/docs/sdk)。 +当你运行 `opencode` 时,它会启动一个 TUI 和一个服务器。TUI 是与服务器通信的客户端。服务器暴露一个 OpenAPI 3.1 规范端点。该端点也用于生成 [SDK](/docs/sdk)。 :::tip -使用opencode服务器以程序设计方式与opencode交互。 +使用 opencode 服务器以编程方式与 opencode 交互。 ::: -该架构让 opencode 支持客户端,并允许您以多种设计方式与 opencode 交互。 +这种架构让 opencode 支持多个客户端,并允许你以编程方式与 opencode 交互。 -You can run `opencode serve` to start a standalone server. If you have the -opencode TUI running, `opencode serve` will start a new server. +你可以运行 `opencode serve` 来启动一个独立的服务器。如果你已经在运行 opencode TUI,`opencode serve` 会启动一个新的服务器。 --- #### 连接到现有服务器 -当您启动 TUI 时,它会随机分配端口和主机名。您可以重新设置 `--hostname` 和 `--port` [flags](/docs/cli)。使用它连线到其服务器然后器。 +当你启动 TUI 时,它会随机分配端口和主机名。你也可以传入 `--hostname` 和 `--port` [标志](/docs/cli),然后用它来连接对应的服务器。 -[**_T2_**](#tui) 端点可用于跨境服务器驱动 TUI。例如,您可以预填充或执行提示。此设置由 opencode [IDE](/docs/ide) 外挂使用。 +[`/tui`](#tui) 端点可用于通过服务器驱动 TUI。例如,你可以预填充或运行一个提示词。此方式被 OpenCode [IDE](/docs/ide) 插件所使用。 --- -## 规格 +## 规范 -服务器发布了OpenAPI 3.1规范,可以在以下位置查看: +服务器发布了一个 OpenAPI 3.1 规范,可在以下地址查看: ``` http://:/doc ``` -例如,`http://localhost:4096/doc`。使用规范生成客户端或检查请求和响应类型其他。或者在 Swagger 浏览器中查看它。 +例如,`http://localhost:4096/doc`。使用该规范可以生成客户端或检查请求和响应类型,也可以在 Swagger 浏览器中查看。 --- ## API -opencode服务器公开以下API。 +opencode 服务器暴露以下 API。 --- ### 全局 -| 方法 | 路径 | 描述 | 回应 | +| 方法 | 路径 | 描述 | 响应 | | ----- | ---------------- | ------------------------ | ------------------------------------ | -| `GET` | `/global/health` | 获取服务器运行状况和版本 | `{ healthy: true, version: string }` | -| `GET` | `/global/event` | 获取全域性事件(SSE 流) | 事件流 | +| `GET` | `/global/health` | 获取服务器健康状态和版本 | `{ healthy: true, version: string }` | +| `GET` | `/global/event` | 获取全局事件(SSE 流) | 事件流 | --- ### 项目 -| 方法 | 路径 | 描述 | 回应 | -| ----- | ------------------ | ------------ | ------------------------------------------ | -| `GET` | `/project` | 列出所有专案 | 专案[] | -| `GET` | `/project/current` | 获取当前专案 | 专案 | +| 方法 | 路径 | 描述 | 响应 | +| ----- | ------------------ | ------------ | --------------------------------------------- | +| `GET` | `/project` | 列出所有项目 | Project[] | +| `GET` | `/project/current` | 获取当前项目 | Project | --- -### 路径和VCS +### 路径和 VCS -| 方法 | 路径 | 描述 | 回应 | +| 方法 | 路径 | 描述 | 响应 | | ----- | ------- | ----------------------- | ------------------------------------------- | -| `GET` | `/path` | 获取当前路径 | 路径 | -| `GET` | `/vcs` | 获取当前专案的 VCS 资讯 | VcsInfo | +| `GET` | `/path` | 获取当前路径 | Path | +| `GET` | `/vcs` | 获取当前项目的 VCS 信息 | VcsInfo | --- -### 例项 +### 实例 -| 方法 | 路径 | 描述 | 回应 | -| ------ | ------------------- | -------------- | --------- | -| `POST` | `/instance/dispose` | 执行当前实例项 | `boolean` | +| 方法 | 路径 | 描述 | 响应 | +| ------ | ------------------- | ------------ | --------- | +| `POST` | `/instance/dispose` | 销毁当前实例 | `boolean` | --- ### 配置 -| 方法 | 路径 | 描述 | 回应 | -| ------- | ------------------- | -------------------- | -------------------------------------------------------------------------------------- | -| `GET` | `/config` | 获取配置资讯 | 配置 | -| `PATCH` | `/config` | 更新配置 | 配置 | -| `GET` | `/config/providers` | 列出提供商和默认模型 | `{ providers: `提供商[]`, default: { [key: string]: string } }` | +| 方法 | 路径 | 描述 | 响应 | +| ------- | ------------------- | -------------------- | ---------------------------------------------------------------------------------------- | +| `GET` | `/config` | 获取配置信息 | Config | +| `PATCH` | `/config` | 更新配置 | Config | +| `GET` | `/config/providers` | 列出提供商和默认模型 | `{ providers: `Provider[]`, default: { [key: string]: string } }` | --- ### 提供商 -| 方法 | 路径 | 描述 | 回应 | -| ------ | -------------------------------- | ----------------------- | --------------------------------------------------------------------------------- | -| `GET` | `/provider` | 列出所有提供商 | `{ all: `提供商[]`, default: {...}, connected: string[] }` | -| `GET` | `/provider/auth` | 获取提供商身份验证方法 | `{ [providerID: string]: `ProviderAuthMethod[]` }` | -| `POST` | `/provider/{id}/oauth/authorize` | 使用 OAuth 授权提供商 | ProviderAuthAuthorization | -| `POST` | `/provider/{id}/oauth/callback` | 处理提供商的 OAuth 回调 | `boolean` | +| 方法 | 路径 | 描述 | 响应 | +| ------ | -------------------------------- | ----------------------- | ----------------------------------------------------------------------------------- | +| `GET` | `/provider` | 列出所有提供商 | `{ all: `Provider[]`, default: {...}, connected: string[] }` | +| `GET` | `/provider/auth` | 获取提供商认证方式 | `{ [providerID: string]: `ProviderAuthMethod[]` }` | +| `POST` | `/provider/{id}/oauth/authorize` | 使用 OAuth 授权提供商 | ProviderAuthAuthorization | +| `POST` | `/provider/{id}/oauth/callback` | 处理提供商的 OAuth 回调 | `boolean` | --- ### 会话 -| 方法 | 路径 | 描述 | 笔记 | -| -------- | ---------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------- | -| `GET` | `/session` | 列出所有会话 | 返回 Session[] | -| `POST` | `/session` | 建立新会话 | 正文: `{ parentID?, title? }`,返回 Session | -| `GET` | `/session/status` | 获取所有会话的会话状态 | 返回 `{ [sessionID: string]: `SessionStatus` }` | -| `GET` | `/session/:id` | 获取会话详细信息 | 返回会话 | -| `DELETE` | `/session/:id` | 删除会话及所有资料 | 返回`boolean` | -| `PATCH` | `/session/:id` | 更新会话属性 | 正文: `{ title? }`,返回 Session | -| `GET` | `/session/:id/children` | 获取会话的子会话 | 返回 Session[] | -| `GET` | `/session/:id/todo` | 获取会话的待办事项列表 | 返回 Todo[] | -| `POST` | `/session/:id/init` | Analyze app and create `AGENTS.md` | body: `{ messageID, providerID, modelID }`, returns `boolean` | -| `POST` | `/session/:id/fork` | 在消息中分叉现有会话 | 正文: `{ messageID? }`,返回 Session | -| `POST` | `/session/:id/abort` | 中止正在执行的会话 | 返回`boolean` | -| `POST` | `/session/:id/share` | 分享会话 | 返回会话 | -| `DELETE` | `/session/:id/share` | 取消共享会话 | 返回会话 | -| `GET` | `/session/:id/diff` | 获取本次会话的差异 | 查询:`messageID?`,返回 FileDiff[] | -| `POST` | `/session/:id/summarize` | 会议总结 | 正文:`{ providerID, modelID }`,返回 `boolean` | -| `POST` | `/session/:id/revert` | 回复讯息 | 正文:`{ messageID, partID? }`,返回 `boolean` | -| `POST` | `/session/:id/unrevert` | 恢复所有已恢复的消息 | 返回`boolean` | -| `POST` | `/session/:id/permissions/:permissionID` | 回复许可权限请求 | 正文:`{ response, remember? }`,返回 `boolean` | +| 方法 | 路径 | 描述 | 说明 | +| -------- | ---------------------------------------- | -------------------------- | --------------------------------------------------------------------------------- | +| `GET` | `/session` | 列出所有会话 | 返回 Session[] | +| `POST` | `/session` | 创建新会话 | 请求体:`{ parentID?, title? }`,返回 Session | +| `GET` | `/session/status` | 获取所有会话的状态 | 返回 `{ [sessionID: string]: `SessionStatus` }` | +| `GET` | `/session/:id` | 获取会话详情 | 返回 Session | +| `DELETE` | `/session/:id` | 删除会话及其所有数据 | 返回 `boolean` | +| `PATCH` | `/session/:id` | 更新会话属性 | 请求体:`{ title? }`,返回 Session | +| `GET` | `/session/:id/children` | 获取会话的子会话 | 返回 Session[] | +| `GET` | `/session/:id/todo` | 获取会话的待办事项列表 | 返回 Todo[] | +| `POST` | `/session/:id/init` | 分析应用并创建 `AGENTS.md` | 请求体:`{ messageID, providerID, modelID }`,返回 `boolean` | +| `POST` | `/session/:id/fork` | 在某条消息处分叉现有会话 | 请求体:`{ messageID? }`,返回 Session | +| `POST` | `/session/:id/abort` | 中止正在运行的会话 | 返回 `boolean` | +| `POST` | `/session/:id/share` | 分享会话 | 返回 Session | +| `DELETE` | `/session/:id/share` | 取消分享会话 | 返回 Session | +| `GET` | `/session/:id/diff` | 获取本次会话的差异 | 查询参数:`messageID?`,返回 FileDiff[] | +| `POST` | `/session/:id/summarize` | 总结会话 | 请求体:`{ providerID, modelID }`,返回 `boolean` | +| `POST` | `/session/:id/revert` | 回退消息 | 请求体:`{ messageID, partID? }`,返回 `boolean` | +| `POST` | `/session/:id/unrevert` | 恢复所有已回退的消息 | 返回 `boolean` | +| `POST` | `/session/:id/permissions/:permissionID` | 响应权限请求 | 请求体:`{ response, remember? }`,返回 `boolean` | --- -### 留言 +### 消息 -| 方法 | 路径 | 描述 | 笔记 | -| ------ | --------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `GET` | `/session/:id/message` | 列出会话中的消息 | 查询: `limit?`,返回 `{ info: `消息`, parts: `Part[]`}[]` | -| `POST` | `/session/:id/message` | 发送消息并等待回复 | 正文: `{ messageID?, model?, agent?, noReply?, system?, tools?, parts }`,返回 `{ info: `消息`, parts: `部分[]`}` | -| `GET` | `/session/:id/message/:messageID` | 获取消息详情 | 返回 `{ info: `消息`, parts: `部分[]`}` | -| `POST` | `/session/:id/prompt_async` | 非同步传送消息(休眠等待) | 主体:与 `/session/:id/message` 相同,返回 `204 No Content` | -| `POST` | `/session/:id/command` | 执行斜杠命令 | 正文: `{ messageID?, agent?, model?, command, arguments }`,返回 `{ info: `消息`, parts: `部分[]`}` | -| `POST` | `/session/:id/shell` | 执行 shell 命令 | 正文: `{ agent, model?, command }`,返回 `{ info: `消息`, parts: `部分[]`}` | +| 方法 | 路径 | 描述 | 说明 | +| ------ | --------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GET` | `/session/:id/message` | 列出会话中的消息 | 查询参数:`limit?`,返回 `{ info: `Message`, parts: `Part[]`}[]` | +| `POST` | `/session/:id/message` | 发送消息并等待响应 | 请求体:`{ messageID?, model?, agent?, noReply?, system?, tools?, parts }`,返回 `{ info: `Message`, parts: `Part[]`}` | +| `GET` | `/session/:id/message/:messageID` | 获取消息详情 | 返回 `{ info: `Message`, parts: `Part[]`}` | +| `POST` | `/session/:id/prompt_async` | 异步发送消息(不等待响应) | 请求体:与 `/session/:id/message` 相同,返回 `204 No Content` | +| `POST` | `/session/:id/command` | 执行斜杠命令 | 请求体:`{ messageID?, agent?, model?, command, arguments }`,返回 `{ info: `Message`, parts: `Part[]`}` | +| `POST` | `/session/:id/shell` | 运行 shell 命令 | 请求体:`{ agent, model?, command }`,返回 `{ info: `Message`, parts: `Part[]`}` | --- ### 命令 -| 方法 | 路径 | 描述 | 回应 | -| ----- | ---------- | ------------ | ------------------------------------------ | -| `GET` | `/command` | 列出所有命令 | 命令[] | +| 方法 | 路径 | 描述 | 响应 | +| ----- | ---------- | ------------ | --------------------------------------------- | +| `GET` | `/command` | 列出所有命令 | Command[] | --- ### 文件 -| 方法 | 路径 | 描述 | 回应 | +| 方法 | 路径 | 描述 | 响应 | | ----- | ------------------------ | -------------------- | ----------------------------------------------------------------------------------- | -| `GET` | `/find?pattern=` | 搜索文件中的文字 | 具有 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的匹配对像数组 | -| `GET` | `/find/file?query=` | 按名称查询文件和目录 | `string[]`(路径) | -| `GET` | `/find/symbol?query=` | 查询工作区符号 | 符号[] | +| `GET` | `/find?pattern=` | 在文件中搜索文本 | 包含 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的匹配对象数组 | +| `GET` | `/find/file?query=` | 按名称查找文件和目录 | `string[]`(路径) | +| `GET` | `/find/symbol?query=` | 查找工作区符号 | Symbol[] | | `GET` | `/file?path=` | 列出文件和目录 | FileNode[] | -| `GET` | `/file/content?path=

` | 读取文件 | 文件内容 | -| `GET` | `/file/status` | 获取跟踪文件的状态 | 文件[] | +| `GET` | `/file/content?path=

` | 读取文件 | FileContent | +| `GET` | `/file/status` | 获取已跟踪文件的状态 | File[] | #### `/find/file` 查询参数 -- `query`(必需)—搜寻字符串(模糊匹配) +- `query`(必需)— 搜索字符串(模糊匹配) - `type`(可选)— 将结果限制为 `"file"` 或 `"directory"` -- `directory` (任选) — 覆盖搜索的专案根目录 -- `limit`(任选)— 最大结果 (1–200) -- `dirs`(任选)— 旧标志(`"false"`仅返回档案) +- `directory`(可选)— 覆盖搜索的项目根目录 +- `limit`(可选)— 最大结果数(1–200) +- `dirs`(可选)— 旧版标志(`"false"` 仅返回文件) --- -### 工具(实验) +### 工具(实验性) -| 方法 | 路径 | 描述 | 回应 | -| ----- | ------------------------------------------- | ---------------------------- | -------------------------------------------- | -| `GET` | `/experimental/tool/ids` | 列出所有工具 ID | 工具ID | -| `GET` | `/experimental/tool?provider=

&model=` | 列出具有模型 JSON 模式的工具 | 工具列表 | +| 方法 | 路径 | 描述 | 响应 | +| ----- | ------------------------------------------- | ---------------------------------- | -------------------------------------------- | +| `GET` | `/experimental/tool/ids` | 列出所有工具 ID | ToolIDs | +| `GET` | `/experimental/tool?provider=

&model=` | 列出指定模型的工具及其 JSON Schema | ToolList | --- -### LSP、格式化程式和 MCP +### LSP、格式化器和 MCP -| 方法 | 路径 | 描述 | 回应 | -| ------ | ------------ | ------------------- | ------------------------------------------------------ | -| `GET` | `/lsp` | 获取 LSP 服务器状态 | LSPStatus[] | -| `GET` | `/formatter` | 获取格式化程式状态 | FormatterStatus[] | -| `GET` | `/mcp` | 获取 MCP 服务器状态 | `{ [name: string]: `MCP状态` }` | -| `POST` | `/mcp` | 动态添加 MCP 服务器 | 主体:`{ name, config }`,返回 MCP 状态对象 | +| 方法 | 路径 | 描述 | 响应 | +| ------ | ------------ | ------------------- | -------------------------------------------------------- | +| `GET` | `/lsp` | 获取 LSP 服务器状态 | LSPStatus[] | +| `GET` | `/formatter` | 获取格式化器状态 | FormatterStatus[] | +| `GET` | `/mcp` | 获取 MCP 服务器状态 | `{ [name: string]: `MCPStatus` }` | +| `POST` | `/mcp` | 动态添加 MCP 服务器 | 请求体:`{ name, config }`,返回 MCP 状态对象 | --- -### 代理商 +### 代理 -| 方法 | 路径 | 描述 | 回应 | -| ----- | -------- | ------------------ | ------------------------------------------ | -| `GET` | `/agent` | 列出所有可用的代理 | 代理[] | +| 方法 | 路径 | 描述 | 响应 | +| ----- | -------- | ------------------ | ------------------------------------------- | +| `GET` | `/agent` | 列出所有可用的代理 | Agent[] | --- ### 日志 -| 方法 | 路径 | 描述 | 回应 | -| ------ | ------------------------------------------- | ------ | -------------------- | -| `POST` | 身体:`{ service, level, message, extra? }` | `/log` | 写入日志。 `boolean` | +| 方法 | 路径 | 描述 | 响应 | +| ------ | ------ | ----------------------------------------------------------- | --------- | +| `POST` | `/log` | 写入日志条目。请求体:`{ service, level, message, extra? }` | `boolean` | --- ### TUI -| 方法 | 路径 | 描述 | 回应 | -| ------ | ----------------------- | ----------------------------------------- | ------------ | -| `POST` | `/tui/append-prompt` | 将文字附加到提示 | `boolean` | -| `POST` | `/tui/open-help` | 开启帮助对话方块 | `boolean` | -| `POST` | `/tui/open-sessions` | 开启会话选择器 | `boolean` | -| `POST` | `/tui/open-themes` | 开启主题选择器 | `boolean` | -| `POST` | `/tui/open-models` | 开启模型选择器 | `boolean` | -| `POST` | `/tui/submit-prompt` | 提交当前提示 | `boolean` | -| `POST` | `/tui/clear-prompt` | 清除提示 | `boolean` | -| `POST` | `/tui/execute-command` | 执行命令 (`{ command }`) | `boolean` | -| `POST` | `/tui/show-toast` | 显示祝酒 (`{ title?, message, variant }`) | `boolean` | -| `GET` | `/tui/control/next` | 等待下一个控制请求 | 控制请求对象 | -| `POST` | `/tui/control/response` | 响应控制请求 (`{ body }`) | `boolean` | +| 方法 | 路径 | 描述 | 响应 | +| ------ | ----------------------- | ---------------------------------------------- | ------------ | +| `POST` | `/tui/append-prompt` | 向提示词追加文本 | `boolean` | +| `POST` | `/tui/open-help` | 打开帮助对话框 | `boolean` | +| `POST` | `/tui/open-sessions` | 打开会话选择器 | `boolean` | +| `POST` | `/tui/open-themes` | 打开主题选择器 | `boolean` | +| `POST` | `/tui/open-models` | 打开模型选择器 | `boolean` | +| `POST` | `/tui/submit-prompt` | 提交当前提示词 | `boolean` | +| `POST` | `/tui/clear-prompt` | 清除提示词 | `boolean` | +| `POST` | `/tui/execute-command` | 执行命令(`{ command }`) | `boolean` | +| `POST` | `/tui/show-toast` | 显示提示消息(`{ title?, message, variant }`) | `boolean` | +| `GET` | `/tui/control/next` | 等待下一个控制请求 | 控制请求对象 | +| `POST` | `/tui/control/response` | 响应控制请求(`{ body }`) | `boolean` | --- -### 授权 +### 认证 -| 方法 | 路径 | 描述 | 回应 | -| ----- | ----------- | ------------------------------------------ | --------- | -| `PUT` | `/auth/:id` | 设置身份验证凭据。正文必须与提供商架构匹配 | `boolean` | +| 方法 | 路径 | 描述 | 响应 | +| ----- | ----------- | -------------------------------------------- | --------- | +| `PUT` | `/auth/:id` | 设置认证凭据。请求体必须匹配提供商的数据结构 | `boolean` | --- -### 活动 +### 事件 -| 方法 | 路径 | 描述 | 回应 | -| ----- | -------- | ------------------------------------------------------------------- | ------------------ | -| `GET` | `/event` | 服务器发送事件流。第一个事件是 `server.connected`,之后是总线事件。 | 服务器发送事件流。 | +| 方法 | 路径 | 描述 | 响应 | +| ----- | -------- | ----------------------------------------------------------------- | ---------------- | +| `GET` | `/event` | 服务器发送事件流。第一个事件是 `server.connected`,之后是总线事件 | 服务器发送事件流 | --- ### 文档 -| 方法 | 路径 | 描述 | 回应 | -| ----- | ------ | --------------- | ------------------------- | -| `GET` | `/doc` | 开启API 3.1规范 | 具有OpenAPI规范的HTML页面 | +| 方法 | 路径 | 描述 | 响应 | +| ----- | ------ | ---------------- | ----------------------------- | +| `GET` | `/doc` | OpenAPI 3.1 规范 | 包含 OpenAPI 规范的 HTML 页面 | diff --git a/packages/web/src/content/docs/zh-cn/share.mdx b/packages/web/src/content/docs/zh-cn/share.mdx index 484e49f87033..8a7be16dc91f 100644 --- a/packages/web/src/content/docs/zh-cn/share.mdx +++ b/packages/web/src/content/docs/zh-cn/share.mdx @@ -1,43 +1,43 @@ --- title: 分享 -description: 分享您的 opencode 对话。 +description: 分享您的 OpenCode 对话。 --- -opencode 的共享功能允许您建立指向 opencode 对话的公共链接,以便您可以与蓝牙进行战斗或从其他人那里获得帮助。 +OpenCode 的分享功能允许您创建指向 OpenCode 对话的公开链接,方便与团队成员协作或向他人寻求帮助。 :::note -任何知道链接的人都可以公开访问共享对话。 +共享的对话对任何拥有链接的人都是公开可访问的。 ::: --- -## 它是如何运作的 +## 工作原理 -当您分享对话时,opencode: +当您分享一段对话时,OpenCode 会: -1. 为您的会话建立唯一的公共 URL -2. 将您的对话历史记录同步到我们的服务器 -3. 通过可共享链接访问对话 — `opncd.ai/s/` +1. 为您的会话创建一个唯一的公开 URL +2. 将您的对话历史同步到我们的服务器 +3. 通过可分享的链接使对话可访问 — `opncd.ai/s/` --- -## 分享 +## 分享模式 -opencode 支持三种控制对话共享方式的共享模式: +OpenCode 支持三种分享模式,用于控制对话的共享方式: --- -### 手动(默认) +### 手动模式(默认) -在默认情况下,opencode 使用手动共享模式。会话不会自动共享,但您可以使用 `/share` 命令手动共享它们: +默认情况下,OpenCode 使用手动分享模式。会话不会自动共享,但您可以使用 `/share` 命令手动分享: ``` /share ``` -这将生成一个唯一的 URL,将其复制到您的剪贴板中。 +这将生成一个唯一的 URL 并复制到您的剪贴板。 -要在[配置文件](/docs/config) 中显式设置手动模式: +要在[配置文件](/docs/config)中显式设置手动模式: ```json title="opencode.json" { @@ -50,7 +50,7 @@ opencode 支持三种控制对话共享方式的共享模式: ### 自动分享 -您可以通过将 [配置文件](/docs/config) 中的 `share` 选项设置为 `"auto"` 来为所有新对话启用自动共享: +您可以在[配置文件](/docs/config)中将 `share` 选项设置为 `"auto"`,为所有新对话启用自动分享: ```json title="opencode.json" { @@ -59,13 +59,13 @@ opencode 支持三种控制对话共享方式的共享模式: } ``` -启用自动共享后,每个新对话都会自动共享并生成链接。 +启用自动分享后,每个新对话都会自动共享并生成链接。 --- -### 已禁用 +### 禁用 -您可以通过将 [配置文件](/docs/config) 中的 `share` 选项设置为 `"disabled"` 来完全禁用共享: +您可以在[配置文件](/docs/config)中将 `share` 选项设置为 `"disabled"`,完全禁用分享功能: ```json title="opencode.json" { @@ -74,34 +74,33 @@ opencode 支持三种控制对话共享方式的共享模式: } ``` -为了在您的团队中针对特定项目强制执行此操作,请将其添加到您项目的 `opencode.json` 文件中,并将其提交到Git。 +要在团队中对特定项目强制执行此设置,请将其添加到项目的 `opencode.json` 文件中并提交到 Git。 --- -## 取消共享 +## 取消分享 -要停止共享对话并将其从公共访问中删除: +要停止分享对话并将其从公开访问中移除: ``` /unshare ``` -这将删除共享链接并删除与对话相关的数据。 +这将移除分享链接并删除与该对话相关的数据。 --- ## 隐私 -分享对话时需要记住一些事项。 +分享对话时需要注意以下几点。 --- -### 数据保留 +### 数据留存 -共享对话仍然可以访问,直到您明确取消共享。这 -包括: +共享的对话在您明确取消分享之前将一直保持可访问状态。这包括: -- 完整的对话历史记录 +- 完整的对话历史 - 所有消息和回复 - 会话元数据 @@ -109,20 +108,20 @@ opencode 支持三种控制对话共享方式的共享模式: ### 建议 -- 仅共享不包含敏感资讯的对话。 -- 分享之前查看对话内容。 -- 协作完成后取消共享对话。 -- 避免共享包括专有代码或机密数据的对话。 -- 对于敏感项目,完全禁用共享。 +- 仅分享不包含敏感信息的对话。 +- 分享前请检查对话内容。 +- 协作完成后请取消分享。 +- 避免分享包含专有代码或机密数据的对话。 +- 对于敏感项目,请完全禁用分享功能。 --- -## 对于企业 +## 企业版 -对于企业部署,共享功能可以是: +对于企业部署,分享功能可以: -- **出于安全合规性完全禁用** -- **仅限** 仅通过 SSO 进行身份验证的用户 -- **在您自己的基础设施上自行托管** +- 出于安全合规考虑**完全禁用** +- **限制**为仅通过 SSO 身份验证的用户可用 +- **自托管**在您自己的基础设施上 -[了解更多关于在您的组织中使用opencode的](/docs/enterprise)。 +[了解更多](/docs/enterprise)关于在您的组织中使用 OpenCode 的信息。 diff --git a/packages/web/src/content/docs/zh-cn/skills.mdx b/packages/web/src/content/docs/zh-cn/skills.mdx index f708a6812a68..1c4f2fe69df3 100644 --- a/packages/web/src/content/docs/zh-cn/skills.mdx +++ b/packages/web/src/content/docs/zh-cn/skills.mdx @@ -1,62 +1,62 @@ --- -title: 代理技能 -description: “通过 SKILL.md 定义可复用的行为” +title: "代理技能" +description: "通过 SKILL.md 定义可复用的行为" --- -代理技能使 OpenCode 能够从您的仓库或主目录中发现可重用的指令。 -技能通过原生 `skill` 工具输入导入 - 代理可以查看可用技能并可以在需要时加载完整内容。 +代理技能让 OpenCode 能够从你的仓库或主目录中发现可复用的指令。 +技能通过原生的 `skill` 工具按需加载——代理可以查看可用技能,并在需要时加载完整内容。 --- ## 放置文件 -为每个技能名称建立一个资料夹,并在其中放入`SKILL.md`。 -opencode 搜索这些位置: +为每个技能名称创建一个文件夹,并在其中放入 `SKILL.md`。 +OpenCode 会搜索以下位置: -- Project config: `.opencode/skills//SKILL.md` -- Global config: `~/.config/opencode/skills//SKILL.md` -- 专案Claude兼容:`.claude/skills//SKILL.md` -- 全域性 Claude 兼容: `~/.claude/skills//SKILL.md` -- 专案代理兼容:`.agents/skills//SKILL.md` -- 全球代理兼容:`~/.agents/skills//SKILL.md` +- 项目配置:`.opencode/skills//SKILL.md` +- 全局配置:`~/.config/opencode/skills//SKILL.md` +- 项目 Claude 兼容:`.claude/skills//SKILL.md` +- 全局 Claude 兼容:`~/.claude/skills//SKILL.md` +- 项目代理兼容:`.agents/skills//SKILL.md` +- 全局代理兼容:`~/.agents/skills//SKILL.md` --- -## 了解发现 +## 了解发现机制 -对于专案本地路径, opencode 从当前工作目录向上走,直到到达 git 工作树。 -It loads any matching `skills/*/SKILL.md` in `.opencode/` and any matching `.claude/skills/*/SKILL.md` or `.agents/skills/*/SKILL.md` along the way. +对于项目本地路径,OpenCode 会从当前工作目录向上遍历,直到到达 git 工作树根目录。 +在此过程中,它会加载 `.opencode/` 中所有匹配的 `skills/*/SKILL.md`,以及匹配的 `.claude/skills/*/SKILL.md` 或 `.agents/skills/*/SKILL.md`。 -Global definitions are also loaded from `~/.config/opencode/skills/*/SKILL.md`, `~/.claude/skills/*/SKILL.md`, and `~/.agents/skills/*/SKILL.md`. +全局定义也会从 `~/.config/opencode/skills/*/SKILL.md`、`~/.claude/skills/*/SKILL.md` 和 `~/.agents/skills/*/SKILL.md` 中加载。 --- -## 编写 Frontmatter +## 编写 frontmatter -每个 `SKILL.md` 必须以 YAML frontmatter 。 -仅识别这些栏位: +每个 `SKILL.md` 必须以 YAML frontmatter 开头。 +仅识别以下字段: - `name`(必填) - `description`(必填) -- `license`(任选) -- `compatibility`(任选) -- `metadata`(任选,字符串到字符串对映) +- `license`(可选) +- `compatibility`(可选) +- `metadata`(可选,字符串到字符串的映射) -未知的 frontmatter 栏位将被忽略。 +未知的 frontmatter 字段会被忽略。 --- ## 验证名称 -`name` 必须: +`name` 必须满足: -- 长度为 1–64 个字元 -- 为小写字母数字并带有单个连字元分隔符 -- 不以 `-` 开始或结束 +- 长度为 1–64 个字符 +- 仅包含小写字母和数字,可用单个连字符分隔 +- 不以 `-` 开头或结尾 - 不包含连续的 `--` -- 匹配包含 `SKILL.md` 的目录名 +- 与包含 `SKILL.md` 的目录名称一致 -等效的正规表示式: +等效的正则表达式: ```text ^[a-z0-9]+(-[a-z0-9]+)*$ @@ -66,14 +66,14 @@ Global definitions are also loaded from `~/.config/opencode/skills/*/SKILL.md`, ## 遵循长度规则 -`description` 必须是 1-1024 个字元。 -保持足够具体,以便代理能够正确选择。 +`description` 必须为 1-1024 个字符。 +请保持描述足够具体,以便代理能够正确选择。 --- -## 使用一个例子 +## 使用示例 -Create `.opencode/skills/git-release/SKILL.md` like this: +创建 `.opencode/skills/git-release/SKILL.md`,内容如下: ```markdown --- @@ -100,10 +100,10 @@ Ask clarifying questions if the target versioning scheme is unclear. --- -## 识别工具说明 +## 识别工具描述 -opencode 列出了 `skill` 工具描述中的可用技能。 -每个条目都包含技能名称和描述: +OpenCode 会在 `skill` 工具描述中列出可用技能。 +每个条目包含技能名称和描述: ```xml @@ -114,7 +114,7 @@ opencode 列出了 `skill` 工具描述中的可用技能。 ``` -代理通过呼叫工具来载入技能: +代理通过调用工具来加载技能: ``` skill({ name: "git-release" }) @@ -124,7 +124,7 @@ skill({ name: "git-release" }) ## 配置权限 -Control which skills agents can access using pattern-based permissions in `opencode.json`: +在 `opencode.json` 中使用基于模式的权限来控制代理可以访问哪些技能: ```json { @@ -139,21 +139,21 @@ Control which skills agents can access using pattern-based permissions in `openc } ``` -| 许可 | 行为 | -| ------- | -------------------------- | -| `allow` | 技能立即加载 | -| `deny` | 对特工隐藏技能,访问被拒绝 | -| `ask` | 加载前提示用户批准 | +| 权限 | 行为 | +| ------- | ------------------------ | +| `allow` | 技能立即加载 | +| `deny` | 对代理隐藏技能,拒绝访问 | +| `ask` | 加载前提示用户确认 | -模式支持万用字元:`internal-*` 匹配 `internal-docs`、`internal-tools` 等。 +模式支持通配符:`internal-*` 可匹配 `internal-docs`、`internal-tools` 等。 --- -## 覆盖每个代理 +## 按代理覆盖权限 -为特定代理授予与全域性默认权限不同的权限。 +为特定代理授予与全局默认值不同的权限。 -**对于自定义代理**(在代理前言中): +**自定义代理**(在代理 frontmatter 中): ```yaml --- @@ -163,7 +163,7 @@ permission: --- ``` -**For built-in agents** (in `opencode.json`): +**内置代理**(在 `opencode.json` 中): ```json { @@ -183,9 +183,9 @@ permission: ## 禁用技能工具 -完全禁用不应该使用技能的特工: +为不需要使用技能的代理完全禁用技能功能: -**对于定制代理**: +**自定义代理**: ```yaml --- @@ -194,7 +194,7 @@ tools: --- ``` -**对于内建代理**: +**内置代理**: ```json { @@ -212,11 +212,11 @@ tools: --- -## 解决加载问题 +## 排查加载问题 -如果某项技能没有显示: +如果某个技能没有显示: -1. 验证 `SKILL.md` 拼写为全部大写 -2. 检查 frontmatter 是否包括 `name` 和 `description` -3. 确保技能名称在所有位置都是唯一的 -4. 查询权限——具有`deny`的代理隐藏技能 +1. 确认 `SKILL.md` 文件名全部为大写字母 +2. 检查 frontmatter 是否包含 `name` 和 `description` +3. 确保技能名称在所有位置中唯一 +4. 检查权限设置——设为 `deny` 的技能会对代理隐藏 diff --git a/packages/web/src/content/docs/zh-cn/themes.mdx b/packages/web/src/content/docs/zh-cn/themes.mdx index a885fed19baa..d1abefed6d68 100644 --- a/packages/web/src/content/docs/zh-cn/themes.mdx +++ b/packages/web/src/content/docs/zh-cn/themes.mdx @@ -1,67 +1,67 @@ --- title: 主题 -description: 选择内建主题或定义您自己的主题。 +description: 选择内置主题或定义您自己的主题。 --- -使用 opencode,您可以从多个内建主题中进行选择,使用适合您的终端主题的主题,或定义您自己的自定义主题。 +通过 OpenCode,您可以从多个内置主题中进行选择,使用能自动适配终端主题的主题,或者定义您自己的自定义主题。 -By default, opencode uses our own `opencode` theme. +默认情况下,OpenCode 使用我们自己的 `opencode` 主题。 --- ## 终端要求 -为了使主题能够正确显示完整的调色板,您的终端必须支持**真彩色**(24 位颜色)。大多数现代终端默认支持此功能,但您可能需要启用它: +为了使主题能够正确显示完整的调色板,您的终端必须支持**真彩色**(24 位色)。大多数现代终端默认支持此功能,但您可能需要手动启用: -- **检查支持**:执行 `echo $COLORTERM` - 它应该输出 `truecolor` 或 `24bit` -- **启用真彩色**:在shell配置文件中设置环境变量`COLORTERM=truecolor` -- **您的终端兼容性**:确保终端模拟器支持24位颜色(大多数现代终端,​​​​如iTerm2、Alacritty、Kitty、Windows终端和最新版本的GNOME终端都支持) +- **检查支持情况**:运行 `echo $COLORTERM` — 输出应为 `truecolor` 或 `24bit` +- **启用真彩色**:在您的 shell 配置文件中设置环境变量 `COLORTERM=truecolor` +- **终端兼容性**:确保您的终端模拟器支持 24 位色(大多数现代终端如 iTerm2、Alacritty、Kitty、Windows Terminal 以及较新版本的 GNOME Terminal 均已支持) -如果没有真彩色支持,主题的颜色精度可能会降低或回落到最接近的 256 色近似值。 +如果没有真彩色支持,主题可能会出现色彩精度下降的情况,或者回退到最接近的 256 色近似值。 --- ## 内置主题 -opencode 带有几个内建主题。 +OpenCode 自带多个内置主题。 -| 名称 | 描述 | -| ---------------------- | ---------------------------------------------------------------------------- | -| `system` | 适应您所处的背景颜色 | -| `tokyonight` | Based on the [Tokyonight](https://github.com/folke/tokyonight.nvim) theme | -| `everforest` | Based on the [Everforest](https://github.com/sainnhe/everforest) theme | -| `ayu` | Based on the [Ayu](https://github.com/ayu-theme) dark theme | -| `catppuccin` | Based on the [Catppuccin](https://github.com/catppuccin) theme | -| `catppuccin-macchiato` | Based on the [Catppuccin](https://github.com/catppuccin) theme | -| `gruvbox` | Based on the [Gruvbox](https://github.com/morhetz/gruvbox) theme | -| `kanagawa` | Based on the [Kanagawa](https://github.com/rebelot/kanagawa.nvim) theme | -| `nord` | Based on the [Nord](https://github.com/nordtheme/nord) theme | -| `matrix` | 骇客风格黑底绿主题 | -| `one-dark` | Based on the [Atom One](https://github.com/Th3Whit3Wolf/one-nvim) Dark theme | +| 名称 | 描述 | +| ---------------------- | ------------------------------------------------------------------- | +| `system` | 自动适配终端的背景颜色 | +| `tokyonight` | 基于 [Tokyonight](https://github.com/folke/tokyonight.nvim) 主题 | +| `everforest` | 基于 [Everforest](https://github.com/sainnhe/everforest) 主题 | +| `ayu` | 基于 [Ayu](https://github.com/ayu-theme) 暗色主题 | +| `catppuccin` | 基于 [Catppuccin](https://github.com/catppuccin) 主题 | +| `catppuccin-macchiato` | 基于 [Catppuccin](https://github.com/catppuccin) 主题 | +| `gruvbox` | 基于 [Gruvbox](https://github.com/morhetz/gruvbox) 主题 | +| `kanagawa` | 基于 [Kanagawa](https://github.com/rebelot/kanagawa.nvim) 主题 | +| `nord` | 基于 [Nord](https://github.com/nordtheme/nord) 主题 | +| `matrix` | 黑客风格的黑底绿字主题 | +| `one-dark` | 基于 [Atom One](https://github.com/Th3Whit3Wolf/one-nvim) Dark 主题 | -此外,我们还在不断添加新主题。 +我们还在不断添加更多主题。 --- ## 系统主题 -`system` 主题旨在自动适应您的最终方案。与使用固定颜色的传统主题不同,_system_ 主题: +`system` 主题旨在自动适配您终端的配色方案。与使用固定颜色的传统主题不同,_system_ 主题具有以下特点: -- **生成灰度**:根据终端的背景颜色建立自定义灰度,确保最佳对比度。 -- **使用 ANSI 颜色**:使用标准 ANSI 颜色 (0-15) 进行语法突出显示和 UI 元素,尊重 Windows 的调色盘。 -- **保留默认设置**:使用 `none` 作为文字和背景颜色以保持本机的外观。 +- **生成灰度色阶**:根据终端的背景颜色创建自定义灰度色阶,确保最佳对比度。 +- **使用 ANSI 颜色**:利用标准 ANSI 颜色(0-15)进行语法高亮和 UI 元素渲染,遵循终端的调色板设置。 +- **保留终端默认值**:将文本和背景颜色设为 `none`,以保持终端的原生外观。 系统主题适合以下用户: -- 希望 opencode 与终端的外观相匹配 -- 使用自定义终端配色方案 -- 希望所有终端应用程序具有一致的外观 +- 希望 OpenCode 与终端的外观保持一致 +- 使用了自定义终端配色方案 +- 偏好所有终端应用程序拥有统一的视觉风格 --- ## 使用主题 -您可以通过使用 `/theme` 命令调出主题选择来选择主题。或者您可以在 [config](/docs/config) 中指定它。 +您可以通过 `/theme` 命令调出主题选择界面来选择主题,也可以在[配置](/docs/config)文件中直接指定。 ```json title="opencode.json" {3} { @@ -74,35 +74,35 @@ opencode 带有几个内建主题。 ## 自定义主题 -opencode 支持灵活的基于 JSON 的主题系统,允许用户轻松创建和自定义主题。 +OpenCode 支持灵活的基于 JSON 的主题系统,让用户可以轻松创建和自定义主题。 --- -### 优先级 +### 层级优先级 -主题按以下顺序从多个目录载入,其中后面的目录覆盖前面的目录: +主题按以下顺序从多个目录加载,后面的目录会覆盖前面的目录: -1. **内建主题** - 这些主题嵌入在二进制文件中 -2. **User config directory** - Defined in `~/.config/opencode/themes/*.json` or `$XDG_CONFIG_HOME/opencode/themes/*.json` -3. **Project root directory** - Defined in the `/.opencode/themes/*.json` -4. **Current working directory** - Defined in `./.opencode/themes/*.json` +1. **内置主题** — 嵌入在二进制文件中 +2. **用户配置目录** — 定义在 `~/.config/opencode/themes/*.json` 或 `$XDG_CONFIG_HOME/opencode/themes/*.json` +3. **项目根目录** — 定义在 `/.opencode/themes/*.json` +4. **当前工作目录** — 定义在 `./.opencode/themes/*.json` -如果多个目录包含同名主题,则将使用优先顺序较高的目录中的主题。 +如果多个目录包含同名主题,将使用优先级较高的目录中的主题。 --- ### 创建主题 -要创建自定义主题,请在主题目录中创建 JSON 档案。 +要创建自定义主题,请在上述任一主题目录中创建一个 JSON 文件。 -对于用户范围的主题: +创建用户级主题: ```bash no-frame mkdir -p ~/.config/opencode/themes vim ~/.config/opencode/themes/my-theme.json ``` -以及针对特定项目的主题。 +创建项目级主题: ```bash no-frame mkdir -p .opencode/themes @@ -113,34 +113,34 @@ vim .opencode/themes/my-theme.json ### JSON 格式 -主题使用灵活的 JSON 格式,支持: +主题使用灵活的 JSON 格式,支持以下特性: -- **十六进位制造颜色**: `"#ffffff"` -- **ANSI 颜色**: `3` (0-255) -- **颜色参考**: `"primary"` 或自定义定义 -- **深色/light 变体**: `{"dark": "#000", "light": "#fff"}` -- **无颜色**: `"none"` - 使用终端的默认颜色或透明 +- **十六进制颜色**:`"#ffffff"` +- **ANSI 颜色**:`3`(0-255) +- **颜色引用**:`"primary"` 或自定义定义的颜色名 +- **深色/浅色变体**:`{"dark": "#000", "light": "#fff"}` +- **无颜色**:`"none"` — 使用终端的默认颜色或透明背景 --- ### 颜色定义 -`defs` 部分是可选的,它允许您定义可在主题中引用的可重用颜色。 +`defs` 部分是可选的,它允许您定义可在主题中重复引用的可复用颜色。 --- ### 终端默认值 -特殊值 `"none"` 可用于任何颜色以继承默认的默认颜色。这对于建立与终端方案无缝的融合主题特别有用: +特殊值 `"none"` 可用于任何颜色属性,以继承终端的默认颜色。这在创建需要与终端配色方案无缝融合的主题时特别有用: -- `"text": "none"` - 使用遥控器的预设前景色 -- `"background": "none"` - 使用桌面的背景颜色 +- `"text": "none"` — 使用终端的默认前景色 +- `"background": "none"` — 使用终端的默认背景色 --- -### 例子 +### 示例 -以下是自定义主题的示例: +以下是一个自定义主题的完整示例: ```json title="my-theme.json" { diff --git a/packages/web/src/content/docs/zh-cn/tools.mdx b/packages/web/src/content/docs/zh-cn/tools.mdx index 82aa3b8a31fb..52929218989d 100644 --- a/packages/web/src/content/docs/zh-cn/tools.mdx +++ b/packages/web/src/content/docs/zh-cn/tools.mdx @@ -3,15 +3,15 @@ title: 工具 description: 管理 LLM 可以使用的工具。 --- -Tools allow the LLM to perform actions in your codebase. opencode comes with a set of built-in tools, but you can extend it with [custom tools](/docs/custom-tools) or [MCP servers](/docs/mcp-servers). +工具允许 LLM 在您的代码库中执行操作。OpenCode 自带一组内置工具,您也可以通过[自定义工具](/docs/custom-tools)或 [MCP 服务器](/docs/mcp-servers)来扩展它。 -默认情况下,所有工具都是**启用**并且不需要执行权限。您可以交叉[permissions](/docs/permissions) 控制工具行为。 +默认情况下,所有工具都是**启用**的,且无需权限即可运行。您可以通过[权限](/docs/permissions)来控制工具的行为。 --- ## 配置 -使用 `permission` 栏位控制工具行为。您可以允许、拒绝或要求批准每个工具。 +使用 `permission` 字段来控制工具行为。您可以对每个工具设置允许、拒绝或需要审批。 ```json title="opencode.json" { @@ -24,7 +24,7 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -您还可以使用通配符同时控制多个工具。例如,要求 MCP 服务器批准所有工具: +您还可以使用通配符同时控制多个工具。例如,要求某个 MCP 服务器的所有工具都需要审批: ```json title="opencode.json" { @@ -35,19 +35,19 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -[了解更多](/docs/permissions)关于配置许可权。 +[了解更多](/docs/permissions)关于配置权限的内容。 --- -## 內建工具 +## 内置工具 -以下是 opencode 中可用的所有内置工具。 +以下是 OpenCode 中所有可用的内置工具。 --- ### bash -在项目环境中执行shell命令。 +在项目环境中执行 shell 命令。 ```json title="opencode.json" {4} { @@ -58,13 +58,13 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -这个工具允许 LLM 运行终端命令,例如:`npm install`, `git status`,或者其他任何终端命令。 +该工具允许 LLM 运行终端命令,例如 `npm install`、`git status` 或其他任何 shell 命令。 --- ### edit -使用精确的字符串替换来修改现有文件。 +通过精确的字符串替换来修改现有文件。 ```json title="opencode.json" {4} { @@ -75,13 +75,13 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -该工具通过替换完全匹配的文本来对文件进行精确编辑。这是 LLM 修改代码的主要方式。 +该工具通过替换精确匹配的文本来对文件进行编辑。这是 LLM 修改代码的主要方式。 --- ### write -建立新文件或覆盖现有文件。 +创建新文件或覆盖现有文件。 ```json title="opencode.json" {4} { @@ -92,10 +92,10 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -使用此功能可允许 LLM 创建新文件。如果文件已存在,则会覆盖现有文件。 +使用此工具允许 LLM 创建新文件。如果文件已存在,则会覆盖现有文件。 :::note -`写入`工具由`编辑`权限控制,涵盖所有文件修改(`编辑`、`写入`、`修补`、`多重编辑`)。 +`write` 工具由 `edit` 权限控制,该权限涵盖所有文件修改操作(`edit`、`write`、`patch`、`multiedit`)。 ::: --- @@ -113,7 +113,7 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -该工具读取文件并返回其内容。它支持读取大型文件中的特定行范围。 +该工具读取文件并返回其内容。它支持对大文件读取指定行范围。 --- @@ -130,7 +130,7 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -快速搜索代码库中的内容。支持完整的正则表达式语法和文件模式过滤。 +在代码库中快速搜索内容。支持完整的正则表达式语法和文件模式过滤。 --- @@ -147,13 +147,13 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -使用类似 **/\*.js 或 src/**/\*.ts 的通配符模式搜索文件。返回按修改时间排序的匹配文件路径。 +使用 `**/*.js` 或 `src/**/*.ts` 等 glob 模式搜索文件。返回按修改时间排序的匹配文件路径。 --- ### list -列出给定路径中的文件和目录。 +列出指定路径下的文件和目录。 ```json title="opencode.json" {4} { @@ -164,16 +164,16 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -此工具用于列出目录内容。它接受通配符模式来筛选结果。 +该工具用于列出目录内容。它接受 glob 模式来过滤结果。 --- ### lsp(实验性) -与已配置的 LSP 服务器交互,以获取代码智能功能,例如定义、引用、悬停信息和调用层次结构。 +与已配置的 LSP 服务器交互,获取代码智能功能,如定义跳转、引用查找、悬停信息和调用层次结构。 :::note -只有当 OPENCODE_EXPERIMENTAL_LSP_TOOL=true(或 OPENCODE_EXPERIMENTAL=true)时,此工具才可用。 +该工具仅在设置 `OPENCODE_EXPERIMENTAL_LSP_TOOL=true`(或 `OPENCODE_EXPERIMENTAL=true`)时可用。 ::: ```json title="opencode.json" {4} @@ -187,7 +187,7 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s 支持的操作包括 `goToDefinition`、`findReferences`、`hover`、`documentSymbol`、`workspaceSymbol`、`goToImplementation`、`prepareCallHierarchy`、`incomingCalls` 和 `outgoingCalls`。 -要配置哪些 LSP 服务器可用于您的项目,请参阅 [LSP Servers](/docs/lsp). +要配置项目可用的 LSP 服务器,请参阅 [LSP 服务器](/docs/lsp)。 --- @@ -204,17 +204,17 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -此工具可将补丁文件应用到您的代码库。它可用于应用来自各种来源的差异和补丁。 +该工具将补丁文件应用到您的代码库中。适用于应用来自各种来源的 diff 和补丁。 :::note -`修补`工具由`编辑`权限控制,涵盖所有文件修改(`编辑`、`写入`、`修补`、`多重编辑`)。 +`patch` 工具由 `edit` 权限控制,该权限涵盖所有文件修改操作(`edit`、`write`、`patch`、`multiedit`)。 ::: --- ### skill -加载[技能](/docs/skills)(`SKILL.md` 文件)并在对话中返回其内容。 +加载一个[技能](/docs/skills)(即 `SKILL.md` 文件)并在对话中返回其内容。 ```json title="opencode.json" {4} { @@ -229,7 +229,7 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s ### todowrite -在编码会话过程中管理待办事项列表。 +在编码会话中管理待办事项列表。 ```json title="opencode.json" {4} { @@ -240,17 +240,17 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -创建和更新任务列表,以跟踪复杂操作的进度。LLM 利用此功能来组织多步骤任务。 +创建和更新任务列表以跟踪复杂操作的进度。LLM 使用此工具来组织多步骤任务。 :::note -此工具默认情况下对子代理禁用,但您可以手动启用它。 [了解更多](/docs/agents/#permissions) +该工具默认对子代理禁用,但您可以手动启用。[了解更多](/docs/agents/#permissions) ::: --- ### todoread -阅读现有的待办事项清单。 +读取现有的待办事项列表。 ```json title="opencode.json" {4} { @@ -261,10 +261,10 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -读取当前待办事项列表状态。LLM 使用此信息来跟踪哪些任务处于待处理状态或已完成状态。 +读取当前待办事项列表的状态。LLM 使用此工具来跟踪哪些任务待处理、哪些已完成。 :::note -此工具默认情况下对子代理禁用,但您可以手动启用它。 [了解更多](/docs/agents/#permissions) +该工具默认对子代理禁用,但您可以手动启用。[了解更多](/docs/agents/#permissions) ::: --- @@ -282,16 +282,16 @@ Tools allow the LLM to perform actions in your codebase. opencode comes with a s } ``` -允许LLM获取并读取网页。可用于查找文档或研究在线资源。 +允许 LLM 获取并读取网页内容。适用于查阅文档或研究在线资源。 --- ### websearch -在网络上搜索资料。 +在网络上搜索信息。 :::note -只有在使用 OpenCode 提供程序时,或者当 OPENCODE_ENABLE_EXA 环境变量被设置为任何真值(例如 true 或 1)时,此工具才可用。 +该工具仅在使用 OpenCode 提供商时,或当 `OPENCODE_ENABLE_EXA` 环境变量设置为任意真值(例如 `true` 或 `1`)时可用。 在启动 OpenCode 时启用: @@ -310,12 +310,12 @@ OPENCODE_ENABLE_EXA=1 opencode } ``` -利用 Exa AI 进行网络搜索,查找相关信息。可用于研究特定主题、了解时事新闻或收集超出训练数据范围的信息。 +使用 Exa AI 进行网络搜索以查找相关信息。适用于研究主题、了解时事动态或获取超出训练数据截止日期的信息。 -无需 API 密钥——该工具无需身份验证即可直接连接到 Exa AI 托管的 MCP 服务。 +无需 API 密钥——该工具无需身份验证即可直接连接到 Exa AI 的托管 MCP 服务。 :::tip -当您需要查找信息时,请使用`网页搜索`;当您需要从特定 URL 检索内容时,请使用`网页获取`。 +当您需要查找信息(发现)时使用 `websearch`,当您需要从特定 URL 获取内容(检索)时使用 `webfetch`。 ::: --- @@ -333,42 +333,42 @@ OPENCODE_ENABLE_EXA=1 opencode } ``` -该工具允许 LLM 在执行任务期间向用户提问。它在以下方面很有用: +该工具允许 LLM 在执行任务期间向用户提问。适用于以下场景: - 收集用户偏好或需求 -- 澄清含糊不清的指示 -- 就实施方案做出决定 -- 提供关于选择下一步方向的选项 +- 澄清模糊的指令 +- 获取实现方案的决策 +- 提供方向选择的选项 -每个问题都包含标题、问题正文和选项列表。用户可以从提供的选项中选择答案,也可以输入自定义答案。如果有多个问题,用户可以在提交所有答案之前在不同问题之间切换。 +每个问题包含标题、问题正文和选项列表。用户可以从提供的选项中选择,也可以输入自定义答案。当有多个问题时,用户可以在提交所有答案之前在各问题之间切换浏览。 --- ## 自定义工具 -自定义工具允许您定义LLM可以调用的自定义函数。这些函数在您的配置文件中定义,并且可以执行任意代码。 +自定义工具允许您定义 LLM 可以调用的自定义函数。这些函数在您的配置文件中定义,可以执行任意代码。 -[了解更多](/docs/custom-tools)关于创建自定义工具。 +[了解更多](/docs/custom-tools)关于创建自定义工具的内容。 --- ## MCP 服务器 -MCP(模型上下文协议)服务器允许您集成外部工具和服务。这包括数据库访问、API 集成和第三方服务。 +MCP(Model Context Protocol)服务器允许您集成外部工具和服务,包括数据库访问、API 集成和第三方服务。 -[了解更多](/docs/mcp-servers)关于配置MCP服务器。 +[了解更多](/docs/mcp-servers)关于配置 MCP 服务器的内容。 --- -## 内部规则 +## 内部机制 -在内部,`grep`、 `通配符` 和 `罗列` 等工具底层都使用了 ripgrep。默认情况下,ripgrep 会遵循 .gitignore 文件中的规则,这意味着 .gitignore 文件中列出的文件和目录将被排除在搜索和列表之外。 +在内部,`grep`、`glob` 和 `list` 等工具底层使用 [ripgrep](https://github.com/BurntSushi/ripgrep)。默认情况下,ripgrep 遵循 `.gitignore` 中的模式,这意味着 `.gitignore` 中列出的文件和目录将被排除在搜索和列表结果之外。 --- ### 忽略模式 -为了使工具不跳过那些通常会被忽略的文件,请在项目根目录下创建一个 `.ignore` 文件。该文件内定义的目录可以不会被跳过。 +要包含通常会被忽略的文件,请在项目根目录下创建一个 `.ignore` 文件。该文件可以显式允许某些路径。 ```text title=".ignore" !node_modules/ @@ -376,4 +376,4 @@ MCP(模型上下文协议)服务器允许您集成外部工具和服务。 !build/ ``` -例如,这个 `.ignore` 文件允许 ripgrep 在 `node_modules/`、`dist/` 和 `build/` 目录中搜索,即使它们已在 `.gitignore` 中列出。 +例如,这个 `.ignore` 文件允许 ripgrep 在 `node_modules/`、`dist/` 和 `build/` 目录中进行搜索,即使它们已在 `.gitignore` 中列出。 diff --git a/packages/web/src/content/docs/zh-cn/troubleshooting.mdx b/packages/web/src/content/docs/zh-cn/troubleshooting.mdx index 5aa2764d9e07..3f22cbf89576 100644 --- a/packages/web/src/content/docs/zh-cn/troubleshooting.mdx +++ b/packages/web/src/content/docs/zh-cn/troubleshooting.mdx @@ -1,67 +1,67 @@ --- title: 故障排除 -description: 常见问题以及如何解决它们。 +description: 常见问题及其解决方法。 --- -要排除 opencode 的问题,请首先检查其存储在磁盘上的日志和本地数据。 +要调试 OpenCode 的问题,请先检查其存储在磁盘上的日志和本地数据。 --- ## 日志 -日志文件写入: +日志文件写入位置: - **macOS/Linux**: `~/.local/share/opencode/log/` -- **Windows**: Press `WIN+R` and paste `%USERPROFILE%\.local\share\opencode\log` +- **Windows**: 按 `WIN+R` 并粘贴 `%USERPROFILE%\.local\share\opencode\log` -日志档案以时间命名(例如`2025-01-09T123456.log`),并保留最近10个日志档案。 +日志文件以时间戳命名(例如 `2025-01-09T123456.log`),并保留最近的 10 个日志文件。 -You can set the log level with the `--log-level` command-line option to get more detailed debug information. For example, `opencode --log-level DEBUG`. +你可以通过 `--log-level` 命令行选项设置日志级别以获取更详细的调试信息。例如:`opencode --log-level DEBUG`。 --- ## 存储 -opencode程序将会话数据和其他应用程序数据存储在磁碟上: +OpenCode 将会话数据和其他应用数据存储在磁盘上: - **macOS/Linux**: `~/.local/share/opencode/` -- **Windows**: Press `WIN+R` and paste `%USERPROFILE%\.local\share\opencode` +- **Windows**: 按 `WIN+R` 并粘贴 `%USERPROFILE%\.local\share\opencode` 该目录包含: -- `auth.json` - 身份验证凭据,例如 API 密钥、OAuth Tokens +- `auth.json` - 身份验证数据,如 API 密钥、OAuth Token - `log/` - 应用日志 -- `project/` - 项目特定数据,例如会话和消息数据 - - 如果项目位于 Git 仓库中,则存储在 `.//storage/` 中 - - 如果不是 Git 存储库,则存储在 `./global/storage/` 中 +- `project/` - 项目特定数据,如会话和消息数据 + - 如果项目位于 Git 仓库中,则存储在 `.//storage/` + - 如果不是 Git 仓库,则存储在 `./global/storage/` --- -## 桌面应用程序 +## 桌面应用 -opencode Desktop runs a local opencode server (the `opencode-cli` sidecar) in the background. Most issues are caused by a misbehaving plugin, a corrupted cache, or a bad server setting. +OpenCode Desktop 会在后台运行一个本地 OpenCode 服务器(即 `opencode-cli` 附属进程)。大多数问题是由插件异常、缓存损坏或错误的服务器设置引起的。 ### 快速检查 -- 完全退出并重新启动应用程序。 -- 如果应用程序显示错误界面,请单击“**重新启动**”并复制错误详细信息。 -- macOS only: `OpenCode` menu -> **Reload Webview** (helps if the UI is blank/frozen). +- 完全退出并重新启动应用。 +- 如果应用显示错误页面,请点击**重新启动**并复制错误详情。 +- 仅限 macOS:`OpenCode` 菜单 -> **Reload Webview**(当 UI 空白或冻结时有效)。 --- ### 禁用插件 -如果桌面应用程序在启动时崩溃、挂起或行为异常,请首先禁用插件。 +如果桌面应用在启动时崩溃、卡住或行为异常,请先禁用插件。 -#### 检查全域性配置 +#### 检查全局配置 -开启全域性文件并查询`plugin`键。 +打开你的全局配置文件,查找 `plugin` 键。 -- **macOS/Linux**: `~/.config/opencode/opencode.jsonc` (or `~/.config/opencode/opencode.json`) -- **macOS/Linux** (older installs): `~/.local/share/opencode/opencode.jsonc` -- **Windows**: Press `WIN+R` and paste `%USERPROFILE%\.config\opencode\opencode.jsonc` +- **macOS/Linux**: `~/.config/opencode/opencode.jsonc`(或 `~/.config/opencode/opencode.json`) +- **macOS/Linux**(旧版安装): `~/.local/share/opencode/opencode.jsonc` +- **Windows**: 按 `WIN+R` 并粘贴 `%USERPROFILE%\.config\opencode\opencode.jsonc` -如果您配置了插件,请通过删除密钥或将其设置为空数组来暂时禁用它们: +如果你配置了插件,请通过移除该键或将其设置为空数组来临时禁用它们: ```jsonc { @@ -72,118 +72,118 @@ opencode Desktop runs a local opencode server (the `opencode-cli` sidecar) in th #### 检查插件目录 -opencode 还可以从磁碟加载本地外挂。暂时将它们移开(或重新命名资料夹)并重新启动桌面应用程序: +OpenCode 还可以从磁盘加载本地插件。临时将这些插件移走(或重命名文件夹),然后重新启动桌面应用: -- **全域性插件** +- **全局插件** - **macOS/Linux**: `~/.config/opencode/plugins/` - - **Windows**: Press `WIN+R` and paste `%USERPROFILE%\.config\opencode\plugins` -- **项目插件**(仅当您使用每个项目配置时) + - **Windows**: 按 `WIN+R` 并粘贴 `%USERPROFILE%\.config\opencode\plugins` +- **项目插件**(仅当你使用了项目级配置时) - `/.opencode/plugins/` -如果应用程序再次开始工作,请一次重新启用一个插件,以找出导致问题的插件。 +如果应用恢复正常,请逐个重新启用插件,找出导致问题的那个。 --- ### 清除缓存 -如果取消外挂没有帮助(或者外挂安装被卡住),请清除缓存,方便opencode可以重建它。 +如果禁用插件没有帮助(或插件安装卡住了),请清除缓存以便 OpenCode 重新构建。 -1. 完全退出 opencode 桌面。 +1. 完全退出 OpenCode Desktop。 2. 删除缓存目录: -- **macOS**: Finder -> `Cmd+Shift+G` -> paste `~/.cache/opencode` -- **Linux**: delete `~/.cache/opencode` (or run `rm -rf ~/.cache/opencode`) -- **Windows**: Press `WIN+R` and paste `%USERPROFILE%\.cache\opencode` +- **macOS**: Finder -> `Cmd+Shift+G` -> 粘贴 `~/.cache/opencode` +- **Linux**: 删除 `~/.cache/opencode`(或运行 `rm -rf ~/.cache/opencode`) +- **Windows**: 按 `WIN+R` 并粘贴 `%USERPROFILE%\.cache\opencode` -3. 重新启动 opencode 桌面。 +3. 重新启动 OpenCode Desktop。 --- ### 修复服务器连接问题 -opencode Desktop 可以启动自己的本地服务器(默认配置)或连线到您的服务器 URL。 +OpenCode Desktop 可以启动自己的本地服务器(默认行为),也可以连接到你配置的服务器 URL。 -如果您看到**“连线失败”**对话中断(或者应用程序永远无法穿透启动萤幕),请检查自定义服务器URL。 +如果你看到**"Connection Failed"**对话框(或应用始终停留在启动画面),请检查自定义服务器 URL。 -#### 清除桌面桌面服务器 URL +#### 清除桌面默认服务器 URL -在主屏幕中,单击服务器名称(带有状态点)以打开服务器选择器。在“**默认服务器**”部分中,单击“**清除**”。 +在主页面上,点击服务器名称(带有状态指示点)以打开服务器选择器。在**默认服务器**部分,点击**清除**。 -#### 从配置中删除 `server.port` / `server.hostname` +#### 从配置中移除 `server.port` / `server.hostname` -If your `opencode.json(c)` contains a `server` section, temporarily remove it and restart the desktop app. +如果你的 `opencode.json(c)` 包含 `server` 部分,请临时移除该部分并重新启动桌面应用。 #### 检查环境变量 -如果您在环境中设置了 `OPENCODE_PORT`,桌面应用程序将尝试将交换机用于本地服务器。 +如果你在环境中设置了 `OPENCODE_PORT`,桌面应用将尝试使用该端口作为本地服务器端口。 -- 取消设置`OPENCODE_PORT`(或选择一个休闲摊)并重新启动。 +- 取消设置 `OPENCODE_PORT`(或选择一个空闲端口)并重新启动。 --- -### Linux:Wayland / X11 问题 +### Linux: Wayland / X11 问题 -在 Linux 上,某些 Wayland 设置可能会导致空白视窗或合成器错误。 +在 Linux 上,某些 Wayland 设置可能会导致窗口空白或合成器错误。 -- 如果您在 Wayland 程序上并且应用的是 blank/crashing,请尝试使用 `OC_ALLOW_WAYLAND=1` 启动。 -- 如果这让事情变得更糟糕,请完成其删除并尝试在 X11 会话下启动。 +- 如果你使用 Wayland 且应用出现空白或崩溃,请尝试使用 `OC_ALLOW_WAYLAND=1` 启动。 +- 如果情况变得更糟,请移除该设置并尝试在 X11 会话下启动。 --- -### Windows:WebView2执行时 +### Windows: WebView2 运行时 -在 Windows 上,opencode 桌面需要 Microsoft Edge **WebView2 执行时**。如果应用程序打开为空白视窗或无法启动,请 install/update WebView2 并重试。 +在 Windows 上,OpenCode Desktop 需要 Microsoft Edge **WebView2 Runtime**。如果应用打开后是空白窗口或无法启动,请安装或更新 WebView2 后重试。 --- -### Windows:一般问题 +### Windows: 常见性能问题 -If you're experiencing slow performance, file access issues, or terminal problems on Windows, try using [WSL (Windows Subsystem for Linux)](/docs/windows-wsl). WSL provides a Linux environment that works more seamlessly with opencode's features. +如果你在 Windows 上遇到性能缓慢、文件访问问题或终端问题,请尝试使用 [WSL (Windows Subsystem for Linux)](/docs/windows-wsl)。WSL 提供了一个 Linux 环境,能更好地与 OpenCode 的功能兼容。 --- ### 通知不显示 -opencode 桌面仅在以下情况下显示系统通知: +OpenCode Desktop 仅在以下情况下显示系统通知: -- 在您的作业系统中设置为 opencode 启用了通知,并且 -- 应用程序视窗未聚焦。 +- 在操作系统设置中已为 OpenCode 启用通知,且 +- 应用窗口未处于焦点状态。 --- -### 重置桌面应用程序储存(最后的手段) +### 重置桌面应用存储(最后手段) -如果应用程序无法并且启动您无法从 UI 内部清除设置,请重置桌面应用程序的存储状态。 +如果应用无法启动且你无法从 UI 内部清除设置,请重置桌面应用的保存状态。 -1. 退出 opencode 桌面。 -2. 查询并删除这些文件(它们位于 opencode 桌面应用程序数据目录中): +1. 退出 OpenCode Desktop。 +2. 找到并删除以下文件(它们位于 OpenCode Desktop 应用数据目录中): -- `opencode.settings.dat` (desktop default server URL) -- `opencode.global.dat` and `opencode.workspace.*.dat` (UI state like recent servers/projects) +- `opencode.settings.dat`(桌面默认服务器 URL) +- `opencode.global.dat` 和 `opencode.workspace.*.dat`(UI 状态,如最近的服务器/项目) -快速找到目录: +快速找到该目录: -- **macOS**:Finder -> `Cmd+Shift+G` -> `~/Library/Application Support`(然后搜索上面的档名) -- **Linux**:在`~/.local/share`下搜索上述档名 -- **Windows**:按 `WIN+R` -> `%APPDATA%` (然后搜索上面的档名) +- **macOS**: Finder -> `Cmd+Shift+G` -> `~/Library/Application Support`(然后搜索上述文件名) +- **Linux**: 在 `~/.local/share` 下搜索上述文件名 +- **Windows**: 按 `WIN+R` -> `%APPDATA%`(然后搜索上述文件名) --- -## 寻求帮助 +## 获取帮助 -如果您遇到 opencode 问题: +如果你遇到 OpenCode 的问题: -1. **报告 GitHub** 上的问题 +1. **在 GitHub 上报告问题** - 报告错误或请求功能的最佳方式是利用我们的 GitHub 存储库: + 报告 Bug 或请求功能的最佳方式是通过我们的 GitHub 仓库: [**github.com/anomalyco/opencode/issues**](https://github.com/anomalyco/opencode/issues) - 在建立新问题之前,请搜索现有问题以查看您的问题是否已被报告。 + 在创建新 Issue 之前,请先搜索已有的 Issue,看看你的问题是否已被报告。 -2. **加入我们的不和谐** +2. **加入我们的 Discord** - 获得实时帮助和社群讨论,请加入我们的Discord服务器: + 如需实时帮助和社区讨论,请加入我们的 Discord 服务器: [**opencode.ai/discord**](https://opencode.ai/discord) @@ -191,35 +191,34 @@ opencode 桌面仅在以下情况下显示系统通知: ## 常见问题 -以下是一些常见问题以及解决方法。 +以下是一些常见问题及其解决方法。 --- -### opencode 无法启动 +### OpenCode 无法启动 -1. 检查日志中是否有错误消息 -2. 尝试使用 `--print-logs` 执行以查看终端中的输出 -3. Ensure you have the latest version with `opencode upgrade` +1. 检查日志中的错误消息 +2. 尝试使用 `--print-logs` 运行以在终端中查看输出 +3. 使用 `opencode upgrade` 确保你使用的是最新版本 --- ### 身份验证问题 -1. 尝试使用 TUI 中的 `/connect` 命令重新进行身份验证 -2. 检查您的API 密钥是否有效 -3. 保证您的网路允许连线到达辉煌的API +1. 尝试在 TUI 中使用 `/connect` 命令重新进行身份验证 +2. 检查你的 API 密钥是否有效 +3. 确保你的网络允许连接到提供商的 API --- ### 模型不可用 -1. 检查您是否已通过提供商的身份验证 +1. 检查你是否已通过提供商的身份验证 2. 验证配置中的模型名称是否正确 3. 某些模型可能需要特定的访问权限或订阅 -如果您遇到 `ProviderModelNotFoundError` 您很可能是错误的 -在某处引用模型。 -模型应该像这样引用:`/` +如果你遇到 `ProviderModelNotFoundError`,很可能是在某处错误地引用了模型。 +模型应按如下方式引用:`/` 示例: @@ -227,54 +226,54 @@ opencode 桌面仅在以下情况下显示系统通知: - `openrouter/google/gemini-2.5-flash` - `opencode/kimi-k2` -To figure out what models you have access to, run `opencode models` +要查看你有权访问哪些模型,请运行 `opencode models` --- -### 提供商初始化错误 +### ProviderInitError -如果遇到 ProviderInitError,您的配置可能无效或损坏。 +如果你遇到 ProviderInitError,很可能是配置无效或已损坏。 -要解决这个问题: +要解决此问题: -1. 首先,按照[提供商指南](/docs/providers) 验证您的事业是否已正确设置 -2. 如果问题仍然存在,请尝试清除储存的配置: +1. 首先,按照[提供商指南](/docs/providers)验证你的提供商是否已正确设置 +2. 如果问题仍然存在,请尝试清除已存储的配置: ```bash rm -rf ~/.local/share/opencode ``` - On Windows, press `WIN+R` and delete: `%USERPROFILE%\.local\share\opencode` + 在 Windows 上,按 `WIN+R` 并删除:`%USERPROFILE%\.local\share\opencode` -3. 使用 TUI 中的 `/connect` 命令指示您的企业重新进行身份验证。 +3. 在 TUI 中使用 `/connect` 命令重新与提供商进行身份验证。 --- -### AI_API_CallError 和提供包问题 +### AI_APICallError 和提供商包问题 -如果您遇到 API 呼叫错误,这可能是由于过去提供包造成的。 opencode 根据需要动态安装提供包(OpenAI、Anthropic、Google 等)将其缓存放在本地。 +如果你遇到 API 调用错误,可能是由于提供商包过期导致的。OpenCode 会根据需要动态安装提供商包(OpenAI、Anthropic、Google 等)并将它们缓存到本地。 -要解决provider 包问题: +要解决提供商包问题: -1. 清除provider 包缓存: +1. 清除提供商包缓存: ```bash rm -rf ~/.cache/opencode ``` - On Windows, press `WIN+R` and delete: `%USERPROFILE%\.cache\opencode` + 在 Windows 上,按 `WIN+R` 并删除:`%USERPROFILE%\.cache\opencode` -2. 重新启动 opencode 以重新安装最新的提供包 +2. 重新启动 OpenCode 以重新安装最新的提供商包 -这将需要 opencode 下载最新版本的提供包,这通常可以解决模型参数和 API 更改的兼容性问题。 +这将强制 OpenCode 下载最新版本的提供商包,通常可以解决模型参数和 API 变更带来的兼容性问题。 --- -### 复制/粘贴在 Linux 上不可用 +### 在 Linux 上复制/粘贴不可用 -Linux 用户需要安装以下剪贴簿实用程序之一才能使 copy/paste 功能正常工作: +Linux 用户需要安装以下剪贴板工具之一,复制/粘贴功能才能正常工作: -**对于X11系统:** +**对于 X11 系统:** ```bash apt install -y xclip @@ -282,7 +281,7 @@ apt install -y xclip apt install -y xsel ``` -** 对于 Wayland 系统:** +**对于 Wayland 系统:** ```bash apt install -y wl-clipboard @@ -297,4 +296,4 @@ Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & export DISPLAY=:99.0 ``` -opencode 将检测您是否正在使用 Wayland 并更喜欢 `wl-clipboard`,否则将尝试按以下顺序剪贴簿工具:`xclip` 和 `xsel`。 +OpenCode 会检测你是否正在使用 Wayland 并优先使用 `wl-clipboard`,否则将按以下顺序尝试查找剪贴板工具:`xclip` 和 `xsel`。 diff --git a/packages/web/src/content/docs/zh-cn/tui.mdx b/packages/web/src/content/docs/zh-cn/tui.mdx index abc5d0c1e9df..e34c088cb3a9 100644 --- a/packages/web/src/content/docs/zh-cn/tui.mdx +++ b/packages/web/src/content/docs/zh-cn/tui.mdx @@ -1,25 +1,25 @@ --- title: TUI -description: 使用 opencode 终端用户界面。 +description: 使用 OpenCode 终端用户界面。 --- import { Tabs, TabItem } from "@astrojs/starlight/components" -opencode 提供交互式终端介面或 TUI,以便使用 LLM 处理您的专案。 +OpenCode 提供了一个交互式终端界面(TUI),用于配合 LLM 处理您的项目。 -执行opencode启动当前目录的TUI。 +运行 OpenCode 即可启动当前目录的 TUI。 ```bash opencode ``` -或者您可以为特定的工作目录启动它。 +或者您可以为指定的工作目录启动它。 ```bash opencode /path/to/project ``` -进入TUI后,您可以查看消息进行提示。 +进入 TUI 后,您可以输入消息进行提示。 ```text Give me a quick summary of the codebase. @@ -45,25 +45,25 @@ How is auth handled in @packages/functions/src/api/index.ts? ## Bash 命令 -以`!`开始一条消息以执行shell命令。 +以 `!` 开头的消息会作为 shell 命令执行。 ```bash frame="none" !ls -la ``` -命令的输出将作为工具结果添加到对话中。 +命令的输出会作为工具结果添加到对话中。 --- ## 命令 -使用 opencode TUI 时,您可以输入 `/` 后跟命令名称来快速执行操作。例如: +使用 OpenCode TUI 时,您可以输入 `/` 后跟命令名称来快速执行操作。例如: ```bash frame="none" /help ``` -大多数命令还是以使用 `ctrl+x` 作为主键的键系结,其中 `ctrl+x` 是默认主键。 [了解更多](/docs/keybinds)。 +大多数命令还支持以 `ctrl+x` 作为前导键的快捷键,其中 `ctrl+x` 是默认前导键。[了解更多](/docs/keybinds)。 以下是所有可用的斜杠命令: @@ -71,7 +71,7 @@ How is auth handled in @packages/functions/src/api/index.ts? ### connect -将提供商添加到 opencode。你可以从可用提供商中选择,并添加它们的 API 密钥。 +将提供商添加到 OpenCode。允许您从可用的提供商中选择并添加其 API 密钥。 ```bash frame="none" /connect @@ -81,85 +81,85 @@ How is auth handled in @packages/functions/src/api/index.ts? ### compact -压缩当前会话。_别名_: `/summarize` +压缩当前会话。_别名_:`/summarize` ```bash frame="none" /compact ``` -** 快捷键:** `ctrl+x c` +**快捷键:** `ctrl+x c` --- ### details -切换工具执行详细信息。 +切换工具执行详情的显示。 ```bash frame="none" /details ``` -** 快捷键:** `ctrl+x d` +**快捷键:** `ctrl+x d` --- ### editor -开启外部编辑器来编写消息。使用`EDITOR`环境变量中设定的编辑器。 [了解更多](#editor-setup)。 +打开外部编辑器来编写消息。使用 `EDITOR` 环境变量中设置的编辑器。[了解更多](#editor-setup)。 ```bash frame="none" /editor ``` -** 快捷键:** `ctrl+x e` +**快捷键:** `ctrl+x e` --- ### exit -退出opencode。 _别名_:`/quit`、`/q` +退出 OpenCode。_别名_:`/quit`、`/q` ```bash frame="none" /exit ``` -** 快捷键:** `ctrl+x q` +**快捷键:** `ctrl+x q` --- ### export -将当前对话汇出到 Markdown 并在默认编辑器中开启。使用 `EDITOR` 环境变数中设定的编辑器。 [了解更多](#editor-setup)。 +将当前对话导出为 Markdown 并在默认编辑器中打开。使用 `EDITOR` 环境变量中设置的编辑器。[了解更多](#editor-setup)。 ```bash frame="none" /export ``` -** 快捷键:** `ctrl+x x` +**快捷键:** `ctrl+x x` --- ### help -显示帮助对话方块。 +显示帮助对话框。 ```bash frame="none" /help ``` -** 快捷键:** `ctrl+x h` +**快捷键:** `ctrl+x h` --- ### init -Create or update `AGENTS.md` file. [Learn more](/docs/rules). +创建或更新 `AGENTS.md` 文件。[了解更多](/docs/rules)。 ```bash frame="none" /init ``` -** 快捷键:** `ctrl+x i` +**快捷键:** `ctrl+x i` --- @@ -171,83 +171,82 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). /models ``` -** 快捷键:** `ctrl+x m` +**快捷键:** `ctrl+x m` --- ### new -开始新的会话。 _别名_: `/clear` +开始新的会话。_别名_:`/clear` ```bash frame="none" /new ``` -** 快捷键:** `ctrl+x n` +**快捷键:** `ctrl+x n` --- ### redo -删除之前重做消除的讯息。仅在使用`/undo`后可用。 +重做之前撤销的消息。仅在使用 `/undo` 后可用。 :::tip -任何文件更改也将被恢复。 +所有文件更改也会被恢复。 ::: -在内部,这使用 Git 来管理文件更改。所以你的专案**需要 -是一个Git存储库**。 +在内部,这使用 Git 来管理文件更改。因此您的项目**需要是一个 Git 仓库**。 ```bash frame="none" /redo ``` -** 快捷键:** `ctrl+x r` +**快捷键:** `ctrl+x r` --- ### sessions -上市会话并在会话之间切换。 _别名_:`/resume`、`/continue` +列出会话并在会话之间切换。_别名_:`/resume`、`/continue` ```bash frame="none" /sessions ``` -** 快捷键:** `ctrl+x l` +**快捷键:** `ctrl+x l` --- ### share -共享当前会话。 [了解更多](/docs/share)。 +分享当前会话。[了解更多](/docs/share)。 ```bash frame="none" /share ``` -** 快捷键:** `ctrl+x s` +**快捷键:** `ctrl+x s` --- ### themes -列出可用的主题。 +列出可用主题。 ```bash frame="none" /theme ``` -** 快捷键:** `ctrl+x t` +**快捷键:** `ctrl+x t` --- ### thinking -切换对话中 thinking/reasoning 块的可视性。启用后,您可以看到支持扩展思考的模型的推理过程。 +切换对话中思考/推理块的可见性。启用后,您可以看到支持扩展思考的模型的推理过程。 :::note -该命令仅控制是否**显示** - 不启用或取消模型的推理功能。要切换实际推理功能,请使用 `ctrl+t` 回圈切换模型变体。 +此命令仅控制思考块是否**显示** — 它不会启用或禁用模型的推理能力。要切换实际的推理能力,请使用 `ctrl+t` 循环切换模型变体。 ::: ```bash frame="none" @@ -258,26 +257,25 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). ### undo -撤消对话中的最后一条消息。删除最近的用户消息、所有后续响应以及任何文件更改。 +撤销对话中的最后一条消息。移除最近的用户消息、所有后续响应以及所有文件更改。 :::tip -所做的任何文件更改也将被恢复。 +所做的任何文件更改也会被还原。 ::: -在内部,这使用 Git 来管理文件更改。所以你的专案**需要 -是一个Git存储库**。 +在内部,这使用 Git 来管理文件更改。因此您的项目**需要是一个 Git 仓库**。 ```bash frame="none" /undo ``` -** 快捷键:** `ctrl+x u` +**快捷键:** `ctrl+x u` --- ### unshare -取消共享当前会话。 [了解更多](/docs/share#un-sharing)。 +取消分享当前会话。[了解更多](/docs/share#un-sharing)。 ```bash frame="none" /unshare @@ -301,8 +299,8 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). export EDITOR="code --wait" ``` - 要使其永久存在,请将其添加到您的 shell 配置文件中; - `~/.bashrc`、`~/.zshrc` 等 + 要使其永久生效,请将其添加到您的 shell 配置文件中; + `~/.bashrc`、`~/.zshrc` 等。 @@ -315,8 +313,7 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). set EDITOR=code --wait ``` - 要使其永久化,请使用 **系统属性** > **环境 - 变量**。 + 要使其永久生效,请使用**系统属性** > **环境变量**。 @@ -329,33 +326,33 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). $env:EDITOR = "code --wait" ``` - 要使其永久化,请将其添加到您的 PowerShell 配置文件中。 + 要使其永久生效,请将其添加到您的 PowerShell 配置文件中。 -流行的编辑器选项包括: +常用的编辑器选项包括: - `code` - Visual Studio Code -- `cursor` - 游标 -- `windsurf` - 风帆冲浪 +- `cursor` - Cursor +- `windsurf` - Windsurf - `nvim` - Neovim 编辑器 - `vim` - Vim 编辑器 -- `nano` - 奈米编辑器 -- `notepad` - Windows 文章书 -- `subl` - 崇高文字 +- `nano` - Nano 编辑器 +- `notepad` - Notepad(Windows 记事本) +- `subl` - Sublime Text :::note -一些编辑器如 VS Code 需要以 `--wait` 标志启动。 +某些编辑器(如 VS Code)需要以 `--wait` 标志启动。 ::: -某些编辑器需要命令列参数才能在阻止模式下执行。 `--wait` 标志使编辑器程序阻塞直至关闭。 +某些编辑器需要命令行参数才能以阻塞模式运行。`--wait` 标志使编辑器进程阻塞直到关闭。 --- ## 配置 -您可以使用 opencode 配置文件自定义 TUI 行为。 +您可以通过 OpenCode 配置文件自定义 TUI 行为。 ```json title="opencode.json" { @@ -371,20 +368,20 @@ Create or update `AGENTS.md` file. [Learn more](/docs/rules). ### 选项 -- `scroll_acceleration` - 启用 macOS 式滚动加速以实现平滑、自然的滚动。启用后,滚动速度会随着快速滚动滚动而增加,并在较慢的移动时保持精确。 **此设定优先于 `scroll_speed` 并在启用时覆盖它。 ** -- `scroll_speed` - 控制使用滚动控制器时 TUI 滚动的速度(简单:`1`)。默认为 `3`。 **注意:如果 `scroll_acceleration.enabled` 设置为 `true`,则忽略此设置。 ** +- `scroll_acceleration` - 启用 macOS 风格的滚动加速,实现平滑、自然的滚动体验。启用后,快速滚动时速度会增加,慢速移动时保持精确。**此设置优先于 `scroll_speed`,启用时会覆盖它。** +- `scroll_speed` - 控制使用滚动命令时 TUI 的滚动速度(最小值:`1`)。默认为 `3`。**注意:如果 `scroll_acceleration.enabled` 设置为 `true`,则此设置会被忽略。** --- ## 自定义 -您可以使用命令选项板(`ctrl+x h` 或 `/help`)自定义 TUI 查看的各个方面。这些设置在重新启动后仍然存在。 +您可以使用命令面板(`ctrl+x h` 或 `/help`)自定义 TUI 视图的各个方面。这些设置在重启后仍会保留。 --- -#### 用户名称显示 +#### 用户名显示 -切换您的用户名称是否出现在聊天消息中。通过以下方式访问: +切换您的用户名是否显示在聊天消息中。通过以下方式访问: -- 命令面板:搜索“用户名称”或“隐藏用户名称” -- 该设置会自动保留,放在 TUI 会话中被记住 +- 命令面板:搜索 "username" 或 "hide username" +- 该设置会自动保存,并在各个 TUI 会话中保持记忆 diff --git a/packages/web/src/content/docs/zh-cn/web.mdx b/packages/web/src/content/docs/zh-cn/web.mdx index e13540d1fd36..5b5a31653f28 100644 --- a/packages/web/src/content/docs/zh-cn/web.mdx +++ b/packages/web/src/content/docs/zh-cn/web.mdx @@ -1,39 +1,39 @@ --- title: Web -description: 在浏览器中使用opencode。 +description: 在浏览器中使用 OpenCode。 --- -opencode 可以在浏览器中作为 Web 应用程序执行,消耗终端可以提供同样强大的 AI 编码体验。 +OpenCode 可以作为 Web 应用在浏览器中运行,无需终端即可获得同样强大的 AI 编码体验。 -![opencode Web - New Session](../../../assets/web/web-homepage-new-session.png) +![OpenCode Web - New Session](../../../assets/web/web-homepage-new-session.png) -## 入门 +## 快速开始 -绕过执行以下命令启动 Web 简介: +运行以下命令启动 Web 界面: ```bash opencode web ``` -这将在 `127.0.0.1` 上启动一个具有随机可用端口的本地服务器,并自动在默认浏览器中开启 opencode。 +这会在 `127.0.0.1` 上启动一个本地服务器,使用随机可用端口,并自动在默认浏览器中打开 OpenCode。 :::caution -如果未设置`OPENCODE_SERVER_PASSWORD`,服务器将不安全。这对于本地使用来说很好,但应该针对网路访问进行设置。 +如果未设置 `OPENCODE_SERVER_PASSWORD`,服务器将没有安全保护。本地使用没有问题,但在网络访问时应当设置密码。 ::: :::tip[Windows 用户] -For the best experience, run `opencode web` from [WSL](/docs/windows-wsl) rather than PowerShell. This ensures proper file system access and terminal integration. +为获得最佳体验,建议从 [WSL](/docs/windows-wsl) 而非 PowerShell 运行 `opencode web`。这可以确保正确的文件系统访问和终端集成。 ::: --- ## 配置 -您可以使用命令行标志或在[config file](/docs/config).conf 中配置Web服务器。 +你可以通过命令行标志或[配置文件](/docs/config)来配置 Web 服务器。 ### 端口 -默认情况下,opencode 选择一个可用的端口。您可以指定一个端口: +默认情况下,OpenCode 会选择一个可用端口。你也可以指定端口: ```bash opencode web --port 4096 @@ -41,13 +41,13 @@ opencode web --port 4096 ### 主机名 -默认情况下,服务器绑定到`127.0.0.1`(仅限本地主机)。要使opencode在您的网路上可访问: +默认情况下,服务器绑定到 `127.0.0.1`(仅限本地访问)。要使 OpenCode 在网络中可访问: ```bash opencode web --hostname 0.0.0.0 ``` -使用`0.0.0.0`时,opencode将显示本地地址和网络地址: +使用 `0.0.0.0` 时,OpenCode 会同时显示本地地址和网络地址: ``` Local access: http://localhost:4096 @@ -56,15 +56,15 @@ opencode web --hostname 0.0.0.0 ### mDNS 发现 -启用 mDNS 使您的服务器在本地网上可以发现: +启用 mDNS 可以让你的服务器在本地网络中被自动发现: ```bash opencode web --mdns ``` -This automatically sets the hostname to `0.0.0.0` and advertises the server as `opencode.local`. +这会自动将主机名设置为 `0.0.0.0`,并将服务器广播为 `opencode.local`。 -您可以自定义 mDNS 域名以在同一网路上执行多个示例: +你可以自定义 mDNS 域名,以便在同一网络中运行多个实例: ```bash opencode web --mdns --mdns-domain myproject.local @@ -72,61 +72,61 @@ opencode web --mdns --mdns-domain myproject.local ### CORS -允许CORS使用其他域(对于自定义前缀有用): +要为 CORS 添加额外的允许域名(适用于自定义前端): ```bash opencode web --cors https://example.com ``` -### 验证 +### 身份验证 -要保护访问,请使用 `OPENCODE_SERVER_PASSWORD` 环境变量设置密码: +要保护服务器访问,可以通过 `OPENCODE_SERVER_PASSWORD` 环境变量设置密码: ```bash OPENCODE_SERVER_PASSWORD=secret opencode web ``` -The username defaults to `opencode` but can be changed with `OPENCODE_SERVER_USERNAME`. +用户名默认为 `opencode`,可以通过 `OPENCODE_SERVER_USERNAME` 进行更改。 --- -## 使用web界面 +## 使用 Web 界面 -启动后,web界面将提供对您的 opencode 会话的访问。 +启动后,Web 界面提供对 OpenCode 会话的访问。 ### 会话 -从主页查看和管理您的会话。您可以查看活动会话并开始新会话。 +在主页上查看和管理你的会话。你可以查看活跃的会话,也可以创建新的会话。 -![opencode Web - Active Session](../../../assets/web/web-homepage-active-session.png) +![OpenCode Web - Active Session](../../../assets/web/web-homepage-active-session.png) ### 服务器状态 -单击“查看服务器”可查看连接的服务器及其状态。 +点击"See Servers"可以查看已连接的服务器及其状态。 -![opencode Web - See Servers](../../../assets/web/web-homepage-see-servers.png) +![OpenCode Web - See Servers](../../../assets/web/web-homepage-see-servers.png) --- ## 连接终端 -您可以将终端 TUI 连线到正在执行的 Web 服务器: +你可以将终端 TUI 连接到正在运行的 Web 服务器: ```bash -# Start the web server +# 启动 Web 服务器 opencode web --port 4096 -# In another terminal, attach the TUI +# 在另一个终端中连接 TUI opencode attach http://localhost:4096 ``` -这允许您同时使用 Web 界面和终端,共享相同的会话和状态。 +这样你就可以同时使用 Web 界面和终端,共享相同的会话和状态。 --- ## 配置文件 -You can also configure server settings in your `opencode.json` config file: +你也可以在 `opencode.json` 配置文件中设置服务器选项: ```json { @@ -139,4 +139,4 @@ You can also configure server settings in your `opencode.json` config file: } ``` -命令行标志优先于配置文件设置。 +命令行标志的优先级高于配置文件中的设置。 diff --git a/packages/web/src/content/docs/zh-cn/windows-wsl.mdx b/packages/web/src/content/docs/zh-cn/windows-wsl.mdx index 95ef1bd6d811..853011acee23 100644 --- a/packages/web/src/content/docs/zh-cn/windows-wsl.mdx +++ b/packages/web/src/content/docs/zh-cn/windows-wsl.mdx @@ -1,37 +1,37 @@ --- title: Windows (WSL) -description: 在 Windows 上通过 WSL 使用 opencode。 +description: 通过 WSL 在 Windows 上运行 OpenCode 以获得最佳体验。 --- import { Steps } from "@astrojs/starlight/components" -虽然 opencode 可以直接在 Windows 上运行,但为了获得最佳体验,我们推荐使用 [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)。WSL 提供了一个与 opencode 功能无缝协作的 Linux 环境。 +虽然 OpenCode 可以直接在 Windows 上运行,但我们推荐使用 [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) 以获得最佳体验。WSL 提供了一个 Linux 环境,能够与 OpenCode 的各项功能无缝配合。 -:::tip[为什么使用 WSL?] -WSL 提供更好的文件系统性能、完整的终端支持,以及与 opencode 依赖的开发工具的兼容性。 +:::tip[为什么选择 WSL?] +WSL 提供更出色的文件系统性能、完整的终端支持,以及与 OpenCode 所依赖的开发工具的良好兼容性。 ::: --- -## 设置 +## 安装配置 1. **安装 WSL** - 如果你还没有安装,请按照 Microsoft 官方指南 [安装 WSL](https://learn.microsoft.com/en-us/windows/wsl/install)。 + 如果尚未安装,请参照 Microsoft 官方指南[安装 WSL](https://learn.microsoft.com/en-us/windows/wsl/install)。 -2. **在 WSL 中安装 opencode** +2. **在 WSL 中安装 OpenCode** - 完成 WSL 设置后,打开 WSL 终端并使用任一[安装方式](/docs/)安装 opencode。 + WSL 设置完成后,打开 WSL 终端,使用任一[安装方式](/docs/)安装 OpenCode。 ```bash curl -fsSL https://opencode.ai/install | bash ``` -3. **从 WSL 使用 opencode** +3. **从 WSL 中使用 OpenCode** - 进入你的项目目录(可通过 `/mnt/c/`、`/mnt/d/` 等访问 Windows 文件)并运行 opencode。 + 导航到你的项目目录(通过 `/mnt/c/`、`/mnt/d/` 等路径访问 Windows 文件),然后运行 OpenCode。 ```bash cd /mnt/c/Users/YourName/project @@ -44,54 +44,53 @@ WSL 提供更好的文件系统性能、完整的终端支持,以及与 openco ## 桌面应用 + WSL 服务器 -如果你想使用 opencode 桌面应用,但希望在 WSL 中运行服务器: +如果你希望使用 OpenCode 桌面应用,同时在 WSL 中运行服务器: -1. **在 WSL 中启动服务器**,并使用 `--hostname 0.0.0.0` 以允许外部连接: +1. **在 WSL 中启动服务器**,添加 `--hostname 0.0.0.0` 以允许外部连接: ```bash opencode serve --hostname 0.0.0.0 --port 4096 ``` -2. **将桌面应用连接到** `http://localhost:4096` +2. **在桌面应用中连接到** `http://localhost:4096` :::note -如果你的环境中 `localhost` 不可用,请改用 WSL 的 IP 地址连接(在 WSL 中执行:`hostname -I`),并使用 `http://:4096`。 +如果 `localhost` 在你的环境中无法使用,请改用 WSL 的 IP 地址进行连接(在 WSL 中运行:`hostname -I`),使用 `http://:4096`。 ::: :::caution -使用 `--hostname 0.0.0.0` 时,请设置 `OPENCODE_SERVER_PASSWORD` 来保护服务器。 +使用 `--hostname 0.0.0.0` 时,请设置 `OPENCODE_SERVER_PASSWORD` 以保护服务器安全。 +::: ```bash OPENCODE_SERVER_PASSWORD=your-password opencode serve --hostname 0.0.0.0 ``` -::: - --- ## Web 客户端 + WSL -在 Windows 上获得最佳 Web 体验: +要在 Windows 上获得最佳的 Web 体验: -1. **请在 WSL 终端中运行 `opencode web`**,而不是在 PowerShell 中运行: +1. **在 WSL 终端中运行 `opencode web`**,而非在 PowerShell 中运行: ```bash opencode web --hostname 0.0.0.0 ``` -2. **在 Windows 浏览器中访问** `http://localhost:`(opencode 会打印该 URL) +2. **在 Windows 浏览器中访问** `http://localhost:`(OpenCode 会输出该 URL) -从 WSL 运行 `opencode web` 可以确保正确的文件系统访问和终端集成,同时仍可在 Windows 浏览器中访问。 +从 WSL 中运行 `opencode web` 可确保正确的文件系统访问和终端集成,同时仍可通过 Windows 浏览器进行访问。 --- ## 访问 Windows 文件 -WSL 可以通过 `/mnt/` 目录访问你所有的 Windows 文件: +WSL 可以通过 `/mnt/` 目录访问你的所有 Windows 文件: -- `C:` drive → `/mnt/c/` -- `D:` drive → `/mnt/d/` -- 其他盘符同理 +- `C:` 盘 → `/mnt/c/` +- `D:` 盘 → `/mnt/d/` +- 其他盘符以此类推... 示例: @@ -101,13 +100,13 @@ opencode ``` :::tip -为了获得更流畅的体验,建议将仓库克隆或复制到 WSL 文件系统中(例如 `~/code/`),并在那里运行 opencode。 +为了获得更流畅的体验,建议将仓库克隆或复制到 WSL 文件系统中(例如 `~/code/` 目录下),然后在该位置运行 OpenCode。 ::: --- -## 提示 +## 使用技巧 -- 即使项目存放在 Windows 盘符中,也建议在 WSL 中运行 opencode,文件访问会更顺畅 -- 可将 opencode 与 VS Code 的 [WSL 扩展](https://code.visualstudio.com/docs/remote/wsl)配合使用,形成一体化开发流程 -- opencode 的配置和会话会保存在 WSL 环境中的 `~/.local/share/opencode/` +- 对于存储在 Windows 驱动器上的项目,在 WSL 中运行 OpenCode 即可无缝访问文件 +- 搭配 VS Code 的 [WSL 扩展](https://code.visualstudio.com/docs/remote/wsl) 使用 OpenCode,打造一体化的开发工作流 +- OpenCode 的配置和会话数据存储在 WSL 环境中的 `~/.local/share/opencode/` diff --git a/packages/web/src/content/docs/zh-cn/zen.mdx b/packages/web/src/content/docs/zh-cn/zen.mdx index d03836dc55a3..39358c417007 100644 --- a/packages/web/src/content/docs/zh-cn/zen.mdx +++ b/packages/web/src/content/docs/zh-cn/zen.mdx @@ -1,6 +1,6 @@ --- title: Zen -description: 由 opencode 提供的精选模型列表。 +description: 由 OpenCode 提供的精选模型列表。 --- import config from "../../../../config.mjs" @@ -13,56 +13,47 @@ OpenCode Zen 是由 OpenCode 团队提供的一组经过测试和验证的模型 OpenCode Zen 目前处于测试阶段。 ::: -Zen 的工作方式与 opencode 中的任何其他提供商相同。您登录 OpenCode Zen 并获得 -你的API钥匙。它是**完全可选的**,你不需要使用它即可使用 -opencode。 +Zen 的工作方式与 OpenCode 中的任何其他提供商相同。你只需登录 OpenCode Zen 并获取你的 API 密钥。它是**完全可选的**,你无需使用它也能正常使用 OpenCode。 --- ## 背景 -市面上有很多模型,但其中只有少数几个 -这些模型可以很好地用作编码代理。此外,大多数提供商都 -配置非常不同;所以你会得到截然不同的效率和质量。 +市面上有大量的模型,但其中只有少数能够很好地充当编码代理。此外,大多数提供商的配置方式差异很大,因此你获得的性能和质量也会截然不同。 :::tip -我们测试了一组与 opencode 配合良好的模型并提供商。 +我们测试了一组与 OpenCode 配合良好的精选模型和提供商。 ::: -因此,如果您通过 OpenRouter 之类的东西使用模型,您永远无法 -确定您是否获得了您想要的模型的最佳版本。 +所以如果你通过 OpenRouter 之类的服务使用模型,你永远无法确定是否获得了你想要的模型的最佳版本。 -为了解决这个问题,我们做了几件事: +为了解决这个问题,我们做了以下几件事: -1. 我们测试了一组选定的模型,并与他们的团队讨论了如何 - 最好执行它们。 -2. 然后我们与一些提供商合作以确保这些服务得到服务 - 正确。 -3. 最后,我们对 model/provider 的组合进行了基准测试,总结了 - 并附上一份我们觉得不错的推荐清单。 +1. 我们测试了一组精选的模型,并与它们的团队讨论了最佳运行方式。 +2. 然后我们与几家提供商合作,确保这些模型能被正确地提供服务。 +3. 最后,我们对模型与提供商的组合进行了基准测试,整理出了一份我们有信心推荐的列表。 -OpenCode Zen 是一个AI网关,让您可以访问这些模型。 +OpenCode Zen 是一个 AI 网关,让你可以访问这些模型。 --- -## 它是如何运作的 +## 工作原理 -OpenCode Zen 的工作方式与 opencode 中的任何其他功能相同。 +OpenCode Zen 的工作方式与 OpenCode 中的任何其他提供商相同。 -1. 您登录 **OpenCode Zen**,添加您的账单 - 详细信息,然后复制您的 API 密钥。 -2. 您在 TUI 中执行 `/connect` 命令,选择 OpenCode Zen,然后贴上 API 密钥。 -3. 在 TUI 中执行 `/models` 以查看我们推荐的模型列表。 +1. 登录 **OpenCode Zen**,添加你的账单信息,然后复制你的 API 密钥。 +2. 在 TUI 中运行 `/connect` 命令,选择 OpenCode Zen,然后粘贴你的 API 密钥。 +3. 在 TUI 中运行 `/models` 查看我们推荐的模型列表。 -您需要按请求付费,并且您可以将积分添加到您的账户中。 +你按请求付费,并且可以向你的账户中充值。 --- ## 端点 -您还可以通过以下 API 端点访问我们的模型。 +你还可以通过以下 API 端点访问我们的模型。 -| 模型 | 模型ID | 端点 | AI SDK 套件 | +| 模型 | 模型 ID | 端点 | AI SDK 包 | | ------------------ | ------------------ | -------------------------------------------------- | --------------------------- | | GPT 5.2 | gpt-5.2 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` | | GPT 5.2 Codex | gpt-5.2-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` | @@ -82,10 +73,11 @@ OpenCode Zen 的工作方式与 opencode 中的任何其他功能相同。 | Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | | Gemini 3 Pro | gemini-3-pro | `https://opencode.ai/zen/v1/models/gemini-3-pro` | `@ai-sdk/google` | | Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` | +| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| MiniMax M2.1 Free | minimax-m2.1-free | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | +| GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| GLM 4.7 Free | glm-4.7-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.6 | glm-4.6 | `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` | | Kimi K2.5 Free | kimi-k2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | @@ -94,15 +86,13 @@ OpenCode Zen 的工作方式与 opencode 中的任何其他功能相同。 | Qwen3 Coder 480B | qwen3-coder | `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` | -opencode 配置中的 [model id](/docs/config/#models) -uses the format `opencode/`. For example, for GPT 5.2 Codex, you would -use `opencode/gpt-5.2-codex` in your config. +在 OpenCode 配置中,[模型 ID](/docs/config/#models) 使用 `opencode/` 格式。例如,对于 GPT 5.2 Codex,你需要在配置中使用 `opencode/gpt-5.2-codex`。 --- ### 模型 -您可以从以下位置获取可用模型及其元数据的完整列表: +你可以从以下地址获取可用模型及其元数据的完整列表: ``` https://opencode.ai/zen/v1/models @@ -112,142 +102,135 @@ https://opencode.ai/zen/v1/models ## 定价 -我们支持即用即付模式。以下是**每 100 万Tokens的价格**。 - -| 模型 | 输入 | 输出 | 缓存读取 | 缓存写入 | -| ---------------------------------- | ---------- | ---------- | ---------- | ---------- | -| Big Pickle | 免费 | 免费 | 免费 | - | -| MiniMax M2.1 Free | 免费 | 免费 | 免费 | - | -| MiniMax M2.1 | 0.30 美元 | 1.20 美元 | 0.10 美元 | - | -| GLM 4.7 Free | 免费 | 免费 | 免费 | - | -| GLM 4.7 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | -| GLM 4.6 | 0.60 美元 | 2.20 美元 | 0.10 美元 | - | -| Kimi K2.5 Free | 免费 | 免费 | 免费 | - | -| Kimi K2.5 | 0.60 美元 | 3.00 美元 | 0.08 美元 | - | -| Kimi K2 Thinking | 0.40 美元 | 2.50 美元 | - | - | -| Kimi K2 | 0.40 美元 | 2.50 美元 | - | - | -| Qwen3 Coder 480B | 0.45 美元 | 1.50 美元 | - | - | -| Claude Sonnet 4.5(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | -| Claude Sonnet 4.5(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | -| Claude Sonnet 4(≤ 200K Tokens) | 3.00 美元 | 15.00 美元 | 0.30 美元 | 3.75 美元 | -| Claude Sonnet 4(> 200K Tokens) | 6.00 美元 | 22.50 美元 | 0.60 美元 | 7.50 美元 | -| Claude Haiku 4.5 | 1.00 美元 | 5.00 美元 | 0.10 美元 | 1.25 美元 | -| Claude Haiku 3.5 | 0.80 美元 | 4.00 美元 | 0.08 美元 | 1.00 美元 | -| Claude Opus 4.6(≤ 200K Tokens) | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | -| Claude Opus 4.6(> 200K Tokens) | 10.00 美元 | 37.50 美元 | 1.00 美元 | 12.50 美元 | -| Claude Opus 4.5 | 5.00 美元 | 25.00 美元 | 0.50 美元 | 6.25 美元 | -| Claude Opus 4.1 | 15.00 美元 | 75.00 美元 | 1.50 美元 | 18.75 美元 | -| Gemini 3 Pro(≤20万 Tokens) | 2.00 美元 | 12.00 美元 | 0.20 美元 | - | -| Gemini 3 Pro(>20万 Tokens) | 4.00 美元 | 18.00 美元 | 0.40 美元 | - | -| Gemini 3 Flash | 0.50 美元 | 3.00 美元 | 0.05 美元 | - | -| GPT 5.2 | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | -| GPT 5.2 Codex | 1.75 美元 | 14.00 美元 | 0.175 美元 | - | -| GPT 5.1 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5.1 Codex Max | 1.25 美元 | 10.00 美元 | 0.125 美元 | - | -| GPT 5.1 Codex Mini | 0.25 美元 | 2.00 美元 | 0.025 美元 | - | -| GPT 5 | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5 Codex | 1.07 美元 | 8.50 美元 | 0.107 美元 | - | -| GPT 5 Nano | 免费 | 免费 | 免费 | - | - -您可能会在您的使用历史记录中注意到*Claude Haiku 3.5*。这是一个[低成本模型](/docs/config/#models),用于生成会话标题。 +我们支持按量付费模式。以下是**每 100 万 Token** 的价格。 + +| 模型 | 输入 | 输出 | 缓存读取 | 缓存写入 | +| -------------------------------- | ------ | ------ | -------- | -------- | +| Big Pickle | 免费 | 免费 | 免费 | - | +| MiniMax M2.5 Free | 免费 | 免费 | 免费 | - | +| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | - | +| MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | +| GLM 5 | $1.00 | $3.20 | $0.20 | - | +| GLM 4.7 | $0.60 | $2.20 | $0.10 | - | +| GLM 4.6 | $0.60 | $2.20 | $0.10 | - | +| Kimi K2.5 Free | 免费 | 免费 | 免费 | - | +| Kimi K2.5 | $0.60 | $3.00 | $0.08 | - | +| Kimi K2 Thinking | $0.40 | $2.50 | - | - | +| Kimi K2 | $0.40 | $2.50 | - | - | +| Qwen3 Coder 480B | $0.45 | $1.50 | - | - | +| Claude Sonnet 4.5 (≤ 200K Token) | $3.00 | $15.00 | $0.30 | $3.75 | +| Claude Sonnet 4.5 (> 200K Token) | $6.00 | $22.50 | $0.60 | $7.50 | +| Claude Sonnet 4 (≤ 200K Token) | $3.00 | $15.00 | $0.30 | $3.75 | +| Claude Sonnet 4 (> 200K Token) | $6.00 | $22.50 | $0.60 | $7.50 | +| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | +| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | +| Claude Opus 4.6 (≤ 200K Token) | $5.00 | $25.00 | $0.50 | $6.25 | +| Claude Opus 4.6 (> 200K Token) | $10.00 | $37.50 | $1.00 | $12.50 | +| Claude Opus 4.5 | $5.00 | $25.00 | $0.50 | $6.25 | +| Claude Opus 4.1 | $15.00 | $75.00 | $1.50 | $18.75 | +| Gemini 3 Pro (≤ 200K Token) | $2.00 | $12.00 | $0.20 | - | +| Gemini 3 Pro (> 200K Token) | $4.00 | $18.00 | $0.40 | - | +| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | +| GPT 5.2 | $1.75 | $14.00 | $0.175 | - | +| GPT 5.2 Codex | $1.75 | $14.00 | $0.175 | - | +| GPT 5.1 | $1.07 | $8.50 | $0.107 | - | +| GPT 5.1 Codex | $1.07 | $8.50 | $0.107 | - | +| GPT 5.1 Codex Max | $1.25 | $10.00 | $0.125 | - | +| GPT 5.1 Codex Mini | $0.25 | $2.00 | $0.025 | - | +| GPT 5 | $1.07 | $8.50 | $0.107 | - | +| GPT 5 Codex | $1.07 | $8.50 | $0.107 | - | +| GPT 5 Nano | 免费 | 免费 | 免费 | - | + +你可能会在使用记录中看到 _Claude Haiku 3.5_。这是一个[低成本模型](/docs/config/#models),用于生成会话标题。 :::note -信用卡费用按成本转嫁(4.4% + 每笔交易 0.30 美元);除此之外我们不收取任何费用。 +信用卡手续费按成本转嫁(每笔交易 4.4% + $0.30);除此之外我们不收取任何额外费用。 ::: -免费模型: +免费模型说明: -- GLM 4.7 免费版本在 opencode 上限时提供。团队正在利用这段时间收集反馈并改进模型。 -- Kimi K2.5 在 opencode 限时免费发布。团队正在利用这段时间收集反馈并改进模型。 -- MiniMax M2.1 在 opencode 限时免费供应。团队正在利用这段时间收集反馈并改进模型。 -- Big Pickle 是一个隐形模型,在 opencode 上限时免费。团队正在利用这个临时收集反馈并改进模型。 +- Kimi K2.5 Free 在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。 +- MiniMax M2.5 Free 在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。 +- Big Pickle 是一个隐身模型,在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。 -如果您有任何疑问,请联络我们。 +如有任何疑问,请联系我们。 --- -### 自动重新载入 +### 自动充值 -如果您的余额低于 5 美元,Zen 将自动充值 20 美元。 +如果你的余额低于 $5,Zen 将自动充值 $20。 -您可以更改自动充值金额。您还可以完全禁用自动重新载入。 +你可以更改自动充值的金额,也可以完全禁用自动充值功能。 --- -### 每月限额 +### 月度限额 -您还可以为整个工作区和每个工作区设置每月使用限制 -你的团队成员。 +你还可以为整个工作区以及团队中的每个成员设置月度使用限额。 -例如,假设您将每月使用中断设置为 20 美元,Zen 将不会使用 -一个月超过 20 美元。但如果你启用了自动重新加载,Zen 可能会结束 -如果您的余额低于 5 美元,则向您收取超过 20 美元的费用。 +例如,假设你将月度使用限额设为 $20,Zen 在一个月内的使用量将不会超过 $20。但如果你启用了自动充值,当余额低于 $5 时,Zen 可能会向你收取超过 $20 的费用。 --- ## 隐私 -我们所有的模型都在美国托管。我们的提供商遵循零保留政策,不会将您的数据用于模型训练,但以下情况除外: +我们所有的模型都托管在美国。我们的提供商遵循零保留政策,不会将你的数据用于模型训练,但以下情况除外: -- Big Pickle:在免费期间,收集可用于改进模型的数据。 -- GLM 4.7 免费:在免费期间,收集可用于改进模型的数据。 -- Kimi K2.5 免费:在免费期间,收集可用于改进模型的数据。 -- MiniMax M2.1 免费:在免费期间,收集可用于改进模型的数据。 -- OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data). -- Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage). +- Big Pickle:在免费期间,收集的数据可能会被用于改进模型。 +- Kimi K2.5 Free:在免费期间,收集的数据可能会被用于改进模型。 +- MiniMax M2.5 Free:在免费期间,收集的数据可能会被用于改进模型。 +- OpenAI API:请求会根据 [OpenAI 数据政策](https://platform.openai.com/docs/guides/your-data)保留 30 天。 +- Anthropic API:请求会根据 [Anthropic 数据政策](https://docs.anthropic.com/en/docs/claude-code/data-usage)保留 30 天。 --- -## 对于团队 +## 团队版 -Zen 也非常适合团队使用。您可以邀请您可以邀请队友,分配角色,管理团队使用的模型等。 +Zen 也非常适合团队使用。你可以邀请队友、分配角色、管理团队使用的模型等。 :::note -作为测试版的一部分,工作空间目前对团队免费。 +作为测试版的一部分,工作区功能目前对团队免费开放。 ::: -作为测试版的一部分,管理工作空间目前对团队免费。我们将会 -很快就会分享更多有关定价的细节。 +作为测试版的一部分,管理工作区目前对团队免费。我们将很快公布更多定价详情。 --- ### 角色 -您可以邀请团队成员到您的工作区并分配角色: +你可以邀请团队成员加入你的工作区并分配角色: -- **管理员**:管理模型、成员、API 密钥和计费/账单 +- **管理员**:管理模型、成员、API 密钥和账单 - **成员**:仅管理自己的 API 密钥 -管理员还可以为每个成员设置每月支出限额,以控制成本。 +管理员还可以为每个成员设置月度支出限额,以控制成本。 --- ### 模型访问 -管理员可以启用或禁用工作区的特定模型。对禁用模型发出的请求会返回错误。 +管理员可以启用或禁用工作区中的特定模型。对已禁用模型发出的请求将返回错误。 -这对于您想要禁用以下模型的情况很有帮助: -收集数据。 +这在你想要禁用某个会收集数据的模型时非常有用。 --- -### 使用你自己的密钥 +### 自带密钥 -你可以使用自己的 OpenAI 或 Anthropic API 密钥,同时继续使用 Zen 的其他模型。 +你可以使用自己的 OpenAI 或 Anthropic API 密钥,同时仍然可以访问 Zen 中的其他模型。 -使用你自己的 API 密钥时,Tokens 会直接由对应提供商计费,而不是由 Zen 计费。 +当你使用自己的密钥时,Token 费用由提供商直接计费,而非通过 Zen 计费。 -例如,你的组织可能已经有 OpenAI 或 Anthropic 的 API 密钥, -你希望优先使用它们,而不是 Zen 提供的密钥。 +例如,你的组织可能已经拥有 OpenAI 或 Anthropic 的密钥,你希望使用它们而不是 Zen 提供的密钥。 --- -## 为什么使用 Zen +## 目标 -我们构建 OpenCode Zen 是为了: +我们创建 OpenCode Zen 的目的是: -1. **基准测试**最适合编码代理的 models/providers。 -2. 可以优先使用 **高质量** 选项,而不是被迫降级性能或改用更便宜的提供商。 -3. 通过按成本价计费传递任何**降价收益**,额外费用仅为处理费。 -4. 通过可与其他编码代理一起使用实现 **无锁定**,你也始终可以把其他提供商与 opencode 组合使用。 +1. 为编码代理**基准测试**最佳的模型和提供商组合。 +2. 提供**最高质量**的选项,不降低性能或路由到更廉价的提供商。 +3. 以成本价销售来传递任何**降价优惠**;唯一的加价仅用于覆盖我们的处理费用。 +4. **无锁定**,允许你将其与任何其他编码代理配合使用,同时也始终允许你在 OpenCode 中使用任何其他提供商。 diff --git a/packages/web/src/content/docs/zh-tw/acp.mdx b/packages/web/src/content/docs/zh-tw/acp.mdx index 26ddcff9003a..4dc7baef3ef5 100644 --- a/packages/web/src/content/docs/zh-tw/acp.mdx +++ b/packages/web/src/content/docs/zh-tw/acp.mdx @@ -1,31 +1,31 @@ --- title: ACP 支援 -description: 在任何 ACP 相容編輯器中使用 OpenCode。 +description: 在任何相容 ACP 的編輯器中使用 OpenCode。 --- -OpenCode 支援 [Agent Client Protocol](https://agentclientprotocol.com) 或 (ACP),允許您直接在相容的編輯器和 IDE 中使用它。 +OpenCode 支援 [Agent Client Protocol](https://agentclientprotocol.com)(ACP),允許你直接在相容的編輯器和 IDE 中使用它。 :::tip -有關支援 ACP 的編輯器和工具的列表,請查看 [ACP progress report](https://zed.dev/blog/acp-progress-report#available-now)。 +有關支援 ACP 的編輯器和工具列表,請查看 [ACP 進展報告](https://zed.dev/blog/acp-progress-report#available-now)。 ::: -ACP 是一種開放協議,用於標準化程式碼編輯器和 AI 程式碼代理之間的通訊。 +ACP 是一個開放協議,用於標準化程式碼編輯器與 AI 編碼代理之間的通訊。 --- ## 設定 -要透過 ACP 使用 OpenCode,請將編輯器設定為執行 `opencode acp` 指令。 +要透過 ACP 使用 OpenCode,請在編輯器中設定執行 `opencode acp` 命令。 -該指令將 OpenCode 作為 ACP 相容的子程序啟動,透過 stdio 透過 JSON-RPC 與您的編輯器進行通訊。 +該命令會將 OpenCode 作為相容 ACP 的子程序啟動,透過 stdio 上的 JSON-RPC 與編輯器進行通訊。 -以下是支援 ACP 的流行編輯器的範例。 +以下是支援 ACP 的常用編輯器的設定範例。 --- ### Zed -新增到您的 [Zed](https://zed.dev) 設定 (`~/.config/zed/settings.json`): +新增到你的 [Zed](https://zed.dev) 設定檔(`~/.config/zed/settings.json`)中: ```json title="~/.config/zed/settings.json" { @@ -38,9 +38,9 @@ ACP 是一種開放協議,用於標準化程式碼編輯器和 AI 程式碼代 } ``` -要打開它,請使用 **命令面板** 中的 `agent: new thread` 操作。 +開啟方式:在**命令面板**中執行 `agent: new thread` 操作。 -您還可以透過編輯 `keymap.json` 來綁定鍵盤快速鍵: +你也可以透過編輯 `keymap.json` 來繫結鍵盤快速鍵: ```json title="keymap.json" [ @@ -67,9 +67,9 @@ ACP 是一種開放協議,用於標準化程式碼編輯器和 AI 程式碼代 --- -### JetBrains IDE +### JetBrains IDEs -根據 [文件](https://www.jetbrains.com/help/ai-assistant/acp.html) 新增到你的 [JetBrains IDE](https://www.jetbrains.com/) acp.json: +根據[文件](https://www.jetbrains.com/help/ai-assistant/acp.html),將以下內容新增到你的 [JetBrains IDE](https://www.jetbrains.com/) 的 acp.json 中: ```json title="acp.json" { @@ -82,13 +82,13 @@ ACP 是一種開放協議,用於標準化程式碼編輯器和 AI 程式碼代 } ``` -要打開它,請在 AI Chat 代理選擇器中使用新的「opencode」代理。 +開啟方式:在 AI Chat 代理選擇器中選擇新的 'OpenCode' 代理。 --- ### Avante.nvim -新增到您的 [Avante.nvim](https://github.com/yetone/avante.nvim) 設定: +新增到你的 [Avante.nvim](https://github.com/yetone/avante.nvim) 設定中: ```lua { @@ -121,7 +121,7 @@ ACP 是一種開放協議,用於標準化程式碼編輯器和 AI 程式碼代 ### CodeCompanion.nvim -要將 OpenCode 用作 [CodeCompanion.nvim](https://github.com/olimorris/codecompanion.nvim) 中的 ACP 代理,請將以下內容新增到 Neovim 設定中: +要在 [CodeCompanion.nvim](https://github.com/olimorris/codecompanion.nvim) 中將 OpenCode 用作 ACP 代理,請將以下內容新增到你的 Neovim 設定中: ```lua require("codecompanion").setup({ @@ -136,21 +136,21 @@ require("codecompanion").setup({ }) ``` -此設定將 CodeCompanion 設定為使用 OpenCode 作為聊天的 ACP 代理。 +此設定將 CodeCompanion 設為使用 OpenCode 作為聊天的 ACP 代理。 -如果您需要傳遞環境變數(如 `OPENCODE_API_KEY`),請參閱 CodeCompanion.nvim 文件中的 [設定適配器:環境變數](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key) 了解完整詳細資訊。 +如果需要傳遞環境變數(如 `OPENCODE_API_KEY`),請參閱 CodeCompanion.nvim 文件中的[設定適配器:環境變數](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key)了解詳細資訊。 ## 支援 -OpenCode 透過 ACP 的工作方式與在終端機中的工作方式相同。支援所有功能: +OpenCode 透過 ACP 使用時與在終端機中使用的效果完全一致。所有功能均受支援: :::note -目前不支援某些內建斜線指令,例如 `/undo` 和 `/redo`。 +部分內建斜線命令(如 `/undo` 和 `/redo`)目前暫不支援。 ::: -- 內建工具(檔案操作、終端機指令等) -- 自定義工具和斜線指令 +- 內建工具(檔案操作、終端機命令等) +- 自訂工具和斜線命令 - 在 OpenCode 設定中設定的 MCP 伺服器 -- `AGENTS.md` 的專案特定規則 -- 自定義格式化程式和 linter +- 來自 `AGENTS.md` 的專案級規則 +- 自訂格式化工具和程式碼檢查工具 - 代理和權限系統 diff --git a/packages/web/src/content/docs/zh-tw/agents.mdx b/packages/web/src/content/docs/zh-tw/agents.mdx index c4d3d656bca1..fa8f10254348 100644 --- a/packages/web/src/content/docs/zh-tw/agents.mdx +++ b/packages/web/src/content/docs/zh-tw/agents.mdx @@ -3,133 +3,133 @@ title: 代理 description: 設定和使用專門的代理。 --- -代理是專門的 AI 助理,可以針對特定任務和工作流程進行設定。它們允許您建立具有自定義提示、模型和工具存取權限的專用工具。 +代理是專門的 AI 助手,可以針對特定任務和工作流程進行設定。它們允許您建立具有自訂提示詞、模型和工具存取權限的專用工具。 :::tip -使用計畫代理來分析程式碼並審閱建議,而無需進行任何程式碼變更。 +使用 Plan 代理來分析程式碼和審查建議,而不會進行任何程式碼變更。 ::: -您可以在工作階段期間在代理之間切換,或使用 `@` 提及來呼叫它們。 +您可以在工作階段期間切換代理,或使用 `@` 提及來呼叫它們。 --- ## 類型 -opencode 中有兩種類型的代理;主要代理和子代理。 +OpenCode 中有兩種類型的代理:主代理和子代理。 --- -### 主要代理 +### 主代理 -主要代理是與您直接互動的主要助理。您可以使用 **Tab** 鍵或您設定的 `switch_agent` 鍵綁定循環切換它們。這些代理處理您的主要對話。工具存取是透過權限設定的 - 例如,「Build」啟用了所有工具,而「Plan」則受到限制。 +主代理是您直接互動的主要助手。您可以使用 **Tab** 鍵或設定的 `switch_agent` 快速鍵來循環切換它們。這些代理處理您的主要對話。工具存取透過權限進行設定——例如,Build 啟用了所有工具,而 Plan 則受到限制。 :::tip -您可以在工作階段期間使用 **Tab** 鍵在主要代理之間切換。 +您可以在工作階段期間使用 **Tab** 鍵在主代理之間切換。 ::: -opencode 附帶兩個內建的主要代理:**Build** 和 **Plan**。我們將在下面看看這些。 +OpenCode 內建了兩個主代理:**Build** 和 **Plan**。我們將在下面介紹它們。 --- ### 子代理 -子代理是主要代理可以呼叫來執行特定任務的專業助理。您也可以透過在訊息中 **@提及** 它們來手動呼叫它們。 +子代理是主代理可以呼叫來執行特定任務的專業助手。您也可以透過在訊息中 **@ 提及**它們來手動呼叫。 -opencode 附帶兩個內建子代理:**General** 和 **Explore**。我們將在下面看看這個。 +OpenCode 內建了兩個子代理:**General** 和 **Explore**。我們將在下面介紹它們。 --- -## 內建 +## 內建代理 -opencode 附帶兩個內建主代理和兩個內建子代理。 +OpenCode 內建了兩個主代理和兩個子代理。 --- -### 使用 Build (構建) +### 使用 Build _模式_:`primary` -Build 是啟用所有工具的**預設**主要代理。這是用於需要完全存取檔案操作和系統指令的開發工作的標準代理。 +Build 是啟用了所有工具的**預設**主代理。這是用於需要完全存取檔案操作和系統命令的開發工作的標準代理。 --- -### 使用 Plan (計畫) +### 使用 Plan _模式_:`primary` -專為規劃和分析而設計的受限代理。我們使用權限系統為您提供更多控制並防止意外變更。 -預設情況下,以下所有項均設定為 `ask`: +一個專為規劃和分析設計的受限代理。我們使用權限系統來為您提供更多控制權,並防止意外變更。 +預設情況下,以下所有項均設為 `ask`: -- `file edits`:所有寫入、修補和編輯 -- `bash`:所有 bash 指令 +- `file edits`:所有寫入、補丁和編輯 +- `bash`:所有 bash 命令 -當您希望 LLM 分析程式碼、建議變更或建立計畫而不對程式碼庫進行任何實際修改時,此代理非常有用。 +當您希望 LLM 分析程式碼、建議變更或建立計畫,而不對程式碼庫進行任何實際修改時,此代理非常有用。 --- -### 使用 General (一般) +### 使用 General _模式_:`subagent` -用於研究複雜問題和執行多步驟任務的通用代理。具有完整的工具存取權限(待辦事項除外),因此可以在需要時變更檔案。使用它可以並行執行多個工作單元。 +一個用於研究複雜問題和執行多步驟任務的通用代理。擁有完整的工具存取權限(todo 除外),因此可以在需要時修改檔案。可用於並行執行多個工作單元。 --- -### 使用 Explore (探索) +### 使用 Explore _模式_:`subagent` -用於探索程式碼庫的快速唯讀代理。無法修改檔案。當您需要按模式快速尋找檔案、搜尋程式碼中的關鍵字或回答有關程式碼庫的問題時,請使用此功能。 +一個用於探索程式碼庫的快速唯讀代理。無法修改檔案。當您需要按模式快速查找檔案、搜尋程式碼中的關鍵字或回答有關程式碼庫的問題時,請使用此代理。 --- -### 使用 Compact (壓縮) +### 使用 Compaction _模式_:`primary` -隱藏的系統代理,將長上下文壓縮為較小的摘要。它會在需要時自動執行,並且無法在 UI 中選擇。 +隱藏的系統代理,將長上下文壓縮為較小的摘要。它會在需要時自動執行,且無法在 UI 中選擇。 --- -### 使用 Title (標題) +### 使用 Title _模式_:`primary` -生成短工作階段標題的隱藏系統代理。它會自動執行,並且無法在 UI 中選擇。 +隱藏的系統代理,用於產生簡短的工作階段標題。它會自動執行,且無法在 UI 中選擇。 --- -### 使用 Summarize (摘要) +### 使用 Summary _模式_:`primary` -建立工作階段摘要的隱藏系統代理。它會自動執行,並且無法在 UI 中選擇。 +隱藏的系統代理,用於建立工作階段摘要。它會自動執行,且無法在 UI 中選擇。 --- ## 用法 -1. 對於主要代理,請在工作階段期間使用 **Tab** 鍵循環切換它們。您也可以使用設定的 `switch_agent` 鍵綁定。 +1. 對於主代理,在工作階段期間使用 **Tab** 鍵循環切換。您也可以使用設定的 `switch_agent` 快速鍵。 -2. 可以呼叫子代理: - - **自動**由主要代理根據其描述執行專門任務。 - - 透過在訊息中 **@提及** 子代理手動進行。例如: +2. 子代理可以透過以下方式呼叫: + - 由主代理根據其描述**自動**呼叫以執行專門任務。 + - 透過在訊息中 **@ 提及**子代理來手動呼叫。例如: ```txt frame="none" @general help me search for this function ``` -3. **工作階段之間導航**:當子代理建立自己的子工作階段時,您可以使用以下指令在父工作階段和所有子工作階段之間導航: - - **\+Right**(或您設定的 `session_child_cycle` 鍵綁定)向前循環父級 → 子級 1 → 子級 2 → ... → 父級 - - **\+Left**(或您設定的 `session_child_cycle_reverse` 鍵綁定)向後循環父級 ← 子級 1 ← 子級 2 ← ... ← 父級 +3. **工作階段間導覽**:當子代理建立自己的子工作階段時,您可以使用以下方式在父工作階段和所有子工作階段之間導覽: + - **\+Right**(或設定的 `session_child_cycle` 快速鍵)向前循環:父工作階段 → 子工作階段1 → 子工作階段2 → ... → 父工作階段 + - **\+Left**(或設定的 `session_child_cycle_reverse` 快速鍵)向後循環:父工作階段 ← 子工作階段1 ← 子工作階段2 ← ... ← 父工作階段 - 這使您可以在主要對話和專門的子代理工作之間無縫切換。 + 這使您可以在主對話和專門的子代理工作之間無縫切換。 --- ## 設定 -您可以自定義內建代理或透過設定建立您自己的代理。可以透過兩種方式設定代理: +您可以自訂內建代理或透過設定建立自己的代理。代理可以透過兩種方式進行設定: --- @@ -178,10 +178,10 @@ _模式_:`primary` ### Markdown -您也可以使用 Markdown 檔案定義代理。將它們放入: +您還可以使用 Markdown 檔案定義代理。將它們放在: - 全域:`~/.config/opencode/agents/` -- 每個專案:`.opencode/agents/` +- 專案級:`.opencode/agents/` ```markdown title="~/.config/opencode/agents/review.md" --- @@ -205,19 +205,19 @@ You are in code review mode. Focus on: Provide constructive feedback without making direct changes. ``` -Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理。 +Markdown 檔案名稱即為代理名稱。例如,`review.md` 會建立一個名為 `review` 的代理。 --- ## 選項 -讓我們詳細看看這些設定選項。 +讓我們詳細了解這些設定選項。 --- -### 描述 (Description) +### 描述 -使用 `description` 選項提供代理的作用以及何時使用它的簡要描述。 +使用 `description` 選項提供代理的功能及使用場景的簡要描述。 ```json title="opencode.json" { @@ -233,11 +233,11 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 --- -### 溫度 (Temperature) +### 溫度 -使用 `temperature` 設定控制 LLM 回應的隨機性和創造性。 +使用 `temperature` 設定控制 LLM 回應的隨機性和創造力。 -較低的值使回應更加集中和確定,而較高的值則增加創造力和可變性。 +較低的值使回應更加集中和確定,而較高的值則增加創造力和多樣性。 ```json title="opencode.json" { @@ -252,11 +252,11 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 } ``` -溫度值的範圍通常為 0.0 到 1.0: +溫度值通常範圍為 0.0 到 1.0: -- **0.0-0.2**:非常集中且確定的回應,非常適合程式碼分析和規劃 -- **0.3-0.5**:具有一定創造力的平衡回應,適合一般開發任務 -- **0.6-1.0**:更有創意和多樣化的反應,有助於腦力激盪和探索 +- **0.0-0.2**:非常集中和確定性的回應,適合程式碼分析和規劃 +- **0.3-0.5**:平衡的回應,兼顧一定創造力,適合一般開發任務 +- **0.6-1.0**:更有創造力和多樣性的回應,適合腦力激盪和探索 ```json title="opencode.json" { @@ -276,15 +276,15 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 } ``` -如果未指定溫度,opencode 將使用特定於模型的預設值;大多數模型通常為 0,Qwen 模型為 0.55。 +如果未指定溫度,OpenCode 將使用模型特定的預設值;大多數模型通常為 0,Qwen 模型為 0.55。 --- -### 最大步數 (Steps) +### 最大步數 -控制代理在被迫僅使用文字回應之前可以執行的最大代理迭代次數。這允許希望控制成本的使用者對代理操作設定限制。 +控制代理在被強制以純文字回應之前可以執行的最大代理迭代次數。這允許希望控制成本的使用者對代理操作設定限制。 -如果未設定,代理將繼續迭代,直到模型選擇停止或使用者中斷工作階段。 +如果未設定此選項,代理將持續迭代,直到模型選擇停止或使用者中斷工作階段。 ```json title="opencode.json" { @@ -298,7 +298,7 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 } ``` -當達到限制時,代理會收到特殊的系統提示,指示其回應其工作摘要和建議的剩餘任務。 +當達到限制時,代理會收到一個特殊的系統提示詞,指示其回覆工作摘要和建議的剩餘任務。 :::caution 舊版 `maxSteps` 欄位已棄用。請改用 `steps`。 @@ -306,9 +306,9 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 --- -### 禁用 (Disable) +### 停用 -設定為 `true` 以禁用代理。 +設為 `true` 以停用代理。 ```json title="opencode.json" { @@ -322,9 +322,9 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 --- -### 提示 (Prompt) +### 提示詞 -使用 `prompt` 設定為此代理指定自定義系統提示檔案。提示檔案應包含特定於代理目的的說明。 +使用 `prompt` 設定為代理指定自訂系統提示詞檔案。提示詞檔案應包含針對代理用途的具體指令。 ```json title="opencode.json" { @@ -336,16 +336,16 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 } ``` -該路徑是相對於設定檔所在位置的。因此,這適用於全域 opencode 設定和專案特定設定。 +此路徑相對於設定檔所在位置。因此它同時適用於全域 OpenCode 設定和專案級設定。 --- -### 模型 (Model) +### 模型 -使用 `model` 設定覆蓋此代理的模型。對於使用針對不同任務最佳化的不同模型很有用。例如,更快的規劃模型、更強大的實作模型。 +使用 `model` 設定為代理覆寫模型。適用於針對不同任務使用不同的最佳化模型。例如,用更快的模型進行規劃,用更強大的模型進行實作。 :::tip -如果您不指定模型,主代理將使用 [全域設定的模型](/docs/config#models),而子代理將使用呼叫子代理的主代理的模型。 +如果您不指定模型,主代理將使用[全域設定的模型](/docs/config#models),而子代理將使用呼叫它的主代理所使用的模型。 ::: ```json title="opencode.json" @@ -358,13 +358,13 @@ Markdown 檔名成為代理名稱。例如,`review.md` 建立 `review` 代理 } ``` -opencode 設定中的模型 ID 使用格式 `provider/model-id`。例如,如果您使用 [OpenCode Zen](/docs/zen),則您將使用 `opencode/gpt-5.1-codex` 來表示 GPT 5.1 Codex。 +OpenCode 設定中的模型 ID 使用 `provider/model-id` 格式。例如,如果您使用 [OpenCode Zen](/docs/zen),則可以使用 `opencode/gpt-5.1-codex` 來表示 GPT 5.1 Codex。 --- -### 工具 (Tools) +### 工具 -使用 `tools` 設定控制此代理中可用的工具。您可以透過將特定工具設定為 `true` 或 `false` 來啟用或禁用特定工具。 +使用 `tools` 設定控制代理中可用的工具。您可以透過將特定工具設為 `true` 或 `false` 來啟用或停用它們。 ```json title="opencode.json" {3-6,9-12} { @@ -385,10 +385,10 @@ opencode 設定中的模型 ID 使用格式 `provider/model-id`。例如,如 ``` :::note -特定於代理的設定會覆蓋全域設定。 +代理級設定會覆寫全域設定。 ::: -您也可以使用萬用字元同時控制多個工具。例如,要禁用 MCP 伺服器中的所有工具: +您還可以使用萬用字元同時控制多個工具。例如,要停用 MCP 伺服器中的所有工具: ```json title="opencode.json" { @@ -405,17 +405,17 @@ opencode 設定中的模型 ID 使用格式 `provider/model-id`。例如,如 } ``` -[了解有關工具的更多資訊](/docs/tools)。 +[了解更多關於工具的資訊](/docs/tools)。 --- -### 權限 (Permissions) +### 權限 您可以設定權限來管理代理可以執行的操作。目前,`edit`、`bash` 和 `webfetch` 工具的權限可以設定為: -- `"ask"` — 執行工具之前提示批准 -- `"allow"` — 未經批准允許所有操作 -- `"deny"` — 禁用該工具 +- `"ask"` — 執行工具前提示審批 +- `"allow"` — 允許所有操作,無需審批 +- `"deny"` — 停用該工具 ```json title="opencode.json" { @@ -426,7 +426,7 @@ opencode 設定中的模型 ID 使用格式 `provider/model-id`。例如,如 } ``` -您可以覆蓋每個代理的這些權限。 +您可以按代理覆寫這些權限。 ```json title="opencode.json" {3-5,8-10} { @@ -463,7 +463,7 @@ permission: Only analyze code and suggest changes. ``` -您可以設定特定 bash 指令的權限。 +您可以為特定的 bash 命令設定權限。 ```json title="opencode.json" {7} { @@ -481,7 +481,7 @@ Only analyze code and suggest changes. } ``` -這可以採用全域模式。 +這可以使用 glob 模式。 ```json title="opencode.json" {7} { @@ -498,8 +498,8 @@ Only analyze code and suggest changes. } ``` -您也可以使用 `*` 萬用字元來管理所有指令的權限。 -由於最後一個匹配規則優先,因此將 `*` 萬用字元放在前面,將特定規則放在後面。 +您還可以使用 `*` 萬用字元來管理所有命令的權限。 +由於最後匹配的規則優先,請將 `*` 萬用字元放在前面,將具體規則放在後面。 ```json title="opencode.json" {8} { @@ -517,13 +517,13 @@ Only analyze code and suggest changes. } ``` -[了解有關權限的更多資訊](/docs/permissions)。 +[了解更多關於權限的資訊](/docs/permissions)。 --- -### 模式 (Mode) +### 模式 -使用 `mode` 設定控制代理的模式。 `mode` 選項用於確定如何使用代理。 +使用 `mode` 設定控制代理的模式。`mode` 選項用於確定代理的使用方式。 ```json title="opencode.json" { @@ -535,13 +535,13 @@ Only analyze code and suggest changes. } ``` -`mode` 選項可設定為 `primary`、`subagent` 或 `all`。如果未指定 `mode`,則預設為 `all`。 +`mode` 選項可以設為 `primary`、`subagent` 或 `all`。如果未指定 `mode`,則預設為 `all`。 --- -### 隱藏 (Hidden) +### 隱藏 -使用 `@` 從 `hidden: true` 自動完成選單隱藏子代理。對於只能由其他代理透過任務工具以程式化方式呼叫的內部子代理很有用。 +使用 `hidden: true` 將子代理從 `@` 自動補全選單中隱藏。適用於只應由其他代理透過 Task 工具以程式化方式呼叫的內部子代理。 ```json title="opencode.json" { @@ -554,17 +554,17 @@ Only analyze code and suggest changes. } ``` -這僅影響自動完成選單中的使用者可見性。如果權限允許,模型仍然可以透過任務工具呼叫隱藏代理。 +這僅影響自動補全選單中的使用者可見性。如果權限允許,模型仍然可以透過 Task 工具呼叫隱藏的代理。 :::note -僅適用於 `mode: subagent` 代理。 +僅適用於 `mode: subagent` 的代理。 ::: --- -### 任務權限 (Task Permissions) +### 任務權限 -使用 `permission.task` 控制代理可以透過任務工具呼叫哪些子代理。使用 glob 模式進行靈活匹配。 +使用 `permission.task` 控制代理可以透過 Task 工具呼叫哪些子代理。使用 glob 模式進行靈活匹配。 ```json title="opencode.json" { @@ -583,21 +583,21 @@ Only analyze code and suggest changes. } ``` -當設定為 `deny` 時,子代理將從任務工具描述中完全刪除,因此模型不會嘗試呼叫它。 +當設為 `deny` 時,子代理將從 Task 工具描述中完全移除,因此模型不會嘗試呼叫它。 :::tip -規則按順序評估,**最後匹配的規則獲勝**。在上面的範例中,`orchestrator-planner` 匹配 `*`(拒絕)和 `orchestrator-*`(允許),但由於 `orchestrator-*` 位於 `*` 之後,因此結果為 `allow`。 +規則按順序評估,**最後匹配的規則優先**。在上面的範例中,`orchestrator-planner` 同時匹配 `*`(deny)和 `orchestrator-*`(allow),但由於 `orchestrator-*` 在 `*` 之後,所以結果為 `allow`。 ::: :::tip -使用者始終可以透過 `@` 自動完成選單直接呼叫任何子代理,即使代理的任務權限會拒絕它。 +使用者始終可以透過 `@` 自動補全選單直接呼叫任何子代理,即使代理的任務權限會拒絕它。 ::: --- -### 顏色 (Color) +### 顏色 -使用 `color` 選項自定義代理在 UI 中的視覺外觀。這會影響代理在介面中的顯示方式。 +使用 `color` 選項自訂代理在 UI 中的視覺外觀。這會影響代理在介面中的顯示方式。 使用有效的十六進位顏色(例如 `#FF5733`)或主題顏色:`primary`、`secondary`、`accent`、`success`、`warning`、`error`、`info`。 @@ -618,7 +618,7 @@ Only analyze code and suggest changes. ### Top P -使用 `top_p` 選項控制回應多樣性。控制隨機性的溫度替代方案。 +使用 `top_p` 選項控制回應多樣性。這是控制隨機性的溫度替代方案。 ```json title="opencode.json" { @@ -634,11 +634,11 @@ Only analyze code and suggest changes. --- -### 額外選項 (Extra) +### 其他選項 -您在代理設定中指定的任何其他選項都將作為模型選項**直接**傳遞給供應商。這允許您使用特定於供應商的功能和參數。 +您在代理設定中指定的任何其他選項都將作為模型選項**直接傳遞**給提供商。這允許您使用提供商特定的功能和參數。 -例如,使用 OpenAI 的推理模型,您可以控制推理工作: +例如,使用 OpenAI 的推理模型時,您可以控制推理力度: ```json title="opencode.json" {6,7} { @@ -653,41 +653,41 @@ Only analyze code and suggest changes. } ``` -這些附加選項是特定於模型和供應商的。檢查供應商的文件以獲取可用參數。 +這些附加選項是模型和提供商特定的。請查閱您的提供商文件以取得可用參數。 :::tip -執行 `opencode models` 查看可用模型的列表。 +執行 `opencode models` 查看可用模型列表。 ::: --- ## 建立代理 -您可以使用以下指令建立新代理: +您可以使用以下命令建立新代理: ```bash opencode agent create ``` -此互動式指令將: +此互動式命令將: -1. 詢問代理保存在哪裡;全域或特定專案。 +1. 詢問代理的儲存位置——全域或專案級。 2. 描述代理應該做什麼。 -3. 生成適當的系統提示和標識符。 +3. 產生合適的系統提示詞和識別碼。 4. 讓您選擇代理可以存取哪些工具。 -5. 最後,使用代理設定建立一個 markdown 檔案。 +5. 最後,建立一個包含代理設定的 Markdown 檔案。 --- -## 使用案例 +## 使用場景 -以下是不同代理的一些常見使用案例。 +以下是不同代理的一些常見使用場景。 - **Build 代理**:啟用所有工具的完整開發工作 -- **Plan 代理**:分析規劃,不做改動 +- **Plan 代理**:分析和規劃,不進行任何變更 - **Review 代理**:具有唯讀存取權限和文件工具的程式碼審查 -- **Debug 代理**:專注於啟用 bash 和讀取工具的調查 -- **Docs 代理**:使用檔案操作但不使用系統指令的文件編寫 +- **Debug 代理**:專注於問題排查,啟用 bash 和讀取工具 +- **Docs 代理**:文件編寫,具有檔案操作但不使用系統命令 --- @@ -696,7 +696,7 @@ opencode agent create 以下是一些您可能會覺得有用的範例代理。 :::tip -您有想要分享的代理嗎? [提交 PR](https://github.com/anomalyco/opencode)。 +您有想要分享的代理嗎?[提交 PR](https://github.com/anomalyco/opencode)。 ::: --- @@ -723,7 +723,7 @@ Focus on: --- -### 安全稽核員 +### 安全稽核代理 ```markdown title="~/.config/opencode/agents/security-auditor.md" --- diff --git a/packages/web/src/content/docs/zh-tw/cli.mdx b/packages/web/src/content/docs/zh-tw/cli.mdx index 580526524bf6..f11066dcf56a 100644 --- a/packages/web/src/content/docs/zh-tw/cli.mdx +++ b/packages/web/src/content/docs/zh-tw/cli.mdx @@ -1,17 +1,17 @@ --- -title: 命令列介面 -description: opencode CLI 選項和指令。 +title: CLI +description: OpenCode CLI 選項和指令。 --- import { Tabs, TabItem } from "@astrojs/starlight/components" -預設情況下,OpenCode CLI 在不帶任何參數執行時啟動 [TUI](/docs/tui)。 +OpenCode CLI 在不帶任何參數執行時,預設啟動 [TUI](/docs/tui)。 ```bash opencode ``` -但它也接受本頁記錄的指令。這允許您以程式化方式與 OpenCode 互動。 +但它也接受本頁面中記錄的指令,使您可以透過程式方式與 OpenCode 進行互動。 ```bash opencode run "Explain how closures work in JavaScript" @@ -21,36 +21,36 @@ opencode run "Explain how closures work in JavaScript" ### tui -啟動 OpenCode TUI。 +啟動 OpenCode 終端機使用者介面。 ```bash opencode [project] ``` -#### 旗標 (Flags) +#### 旗標 | 旗標 | 簡寫 | 說明 | | ------------ | ---- | ------------------------------------------------------------- | | `--continue` | `-c` | 繼續上一個工作階段 | -| `--session` | `-s` | 繼續指定的工作階段 ID | -| `--fork` | | 繼續時分岔工作階段(與 `--continue` 或 `--session` 一起使用) | -| `--prompt` | | 使用的提示 | -| `--model` | `-m` | 使用的模型 (provider/model) | -| `--agent` | | 使用的代理 | +| `--session` | `-s` | 要繼續的工作階段 ID | +| `--fork` | | 繼續時分岔工作階段(與 `--continue` 或 `--session` 搭配使用) | +| `--prompt` | | 要使用的提示詞 | +| `--model` | `-m` | 要使用的模型,格式為 provider/model | +| `--agent` | | 要使用的代理 | | `--port` | | 監聽連接埠 | -| `--hostname` | | 監聽的主機名稱 | +| `--hostname` | | 監聽主機名稱 | --- ## 指令 -OpenCode CLI 還具有以下指令。 +OpenCode CLI 還提供以下指令。 --- ### agent -管理 OpenCode 代理。 +管理 OpenCode 的代理。 ```bash opencode agent [command] @@ -60,13 +60,13 @@ opencode agent [command] ### attach -將終端機連接到透過 `serve` 或 `web` 指令啟動的已執行的 OpenCode 後端伺服器。 +將終端機連接到已透過 `serve` 或 `web` 指令啟動的 OpenCode 後端伺服器。 ```bash opencode attach [url] ``` -這允許將 TUI 與遠端 OpenCode 後端一起使用。例如: +這允許將 TUI 與遠端 OpenCode 後端搭配使用。例如: ```bash # Start the backend server for web/mobile access @@ -78,22 +78,22 @@ opencode attach http://10.20.30.40:4096 #### 旗標 -| 旗標 | 簡寫 | 說明 | -| ----------- | ---- | --------------------- | -| `--dir` | | 啟動 TUI 的工作目錄 | -| `--session` | `-s` | 繼續指定的工作階段 ID | +| 旗標 | 簡寫 | 說明 | +| ----------- | ---- | ------------------- | +| `--dir` | | 啟動 TUI 的工作目錄 | +| `--session` | `-s` | 要繼續的工作階段 ID | --- #### create -使用自定義設定建立新代理。 +使用自訂設定建立新的代理。 ```bash opencode agent create ``` -此指令將指導您使用自定義系統提示和工具設定建立新代理。 +此指令將引導您使用自訂系統提示詞和工具設定來建立新的代理。 --- @@ -109,7 +109,7 @@ opencode agent list ### auth -用於管理供應商的憑證和登入的指令。 +管理供應商的憑證和登入資訊的指令。 ```bash opencode auth [command] @@ -119,25 +119,25 @@ opencode auth [command] #### login -OpenCode 由 [Models.dev](https://models.dev) 上的供應商列表提供支援,因此您可以使用 `opencode auth login` 為您想要使用的任何供應商設定 API 金鑰。它儲存在 `~/.local/share/opencode/auth.json` 中。 +OpenCode 基於 [Models.dev](https://models.dev) 的供應商列表運作,因此您可以使用 `opencode auth login` 為任何想要使用的供應商設定 API 金鑰。金鑰儲存在 `~/.local/share/opencode/auth.json` 中。 ```bash opencode auth login ``` -當 OpenCode 啟動時,它會從憑證檔案載入供應商。如果您的環境中定義了任何金鑰或專案中的 `.env` 檔案。 +OpenCode 啟動時會從憑證檔案載入供應商資訊,同時也會載入環境變數或專案中 `.env` 檔案中定義的金鑰。 --- #### list -列出憑證檔案中儲存的所有經過身分驗證的供應商。 +列出憑證檔案中儲存的所有已認證供應商。 ```bash opencode auth list ``` -或者簡短的版本。 +或使用簡寫版本。 ```bash opencode auth ls @@ -147,7 +147,7 @@ opencode auth ls #### logout -透過從憑證檔案中清除供應商,將您從供應商中登出。 +從憑證檔案中清除供應商資訊以完成登出。 ```bash opencode auth logout @@ -157,7 +157,7 @@ opencode auth logout ### github -管理 GitHub 代理以實現儲存庫自動化。 +管理用於儲存庫自動化的 GitHub 代理。 ```bash opencode github [command] @@ -173,13 +173,13 @@ opencode github [command] opencode github install ``` -這將設定必要的 GitHub Actions 工作流程並指導您完成設定過程。 [了解更多](/docs/github)。 +此指令會設定必要的 GitHub Actions 工作流程並引導您完成設定過程。[了解更多](/docs/github)。 --- #### run -執行 GitHub 代理。這通常用在 GitHub Actions 中。 +執行 GitHub 代理。通常在 GitHub Actions 中使用。 ```bash opencode github run @@ -196,7 +196,7 @@ opencode github run ### mcp -管理模型上下文協議 (MCP) 伺服器。 +管理 Model Context Protocol 伺服器。 ```bash opencode mcp [command] @@ -212,7 +212,7 @@ opencode mcp [command] opencode mcp add ``` -此指令將指導您新增本地或遠端 MCP 伺服器。 +此指令將引導您新增本地或遠端 MCP 伺服器。 --- @@ -224,7 +224,7 @@ opencode mcp add opencode mcp list ``` -或者使用簡短版本。 +或使用簡寫版本。 ```bash opencode mcp ls @@ -234,7 +234,7 @@ opencode mcp ls #### auth -使用啟用 OAuth 的 MCP 伺服器進行身分驗證。 +對支援 OAuth 的 MCP 伺服器進行認證。 ```bash opencode mcp auth [name] @@ -242,13 +242,13 @@ opencode mcp auth [name] 如果您不提供伺服器名稱,系統將提示您從可用的支援 OAuth 的伺服器中進行選擇。 -您還可以列出支援 OAuth 的伺服器及其身分驗證狀態。 +您還可以列出支援 OAuth 的伺服器及其認證狀態。 ```bash opencode mcp auth list ``` -或者使用簡短版本。 +或使用簡寫版本。 ```bash opencode mcp auth ls @@ -258,7 +258,7 @@ opencode mcp auth ls #### logout -刪除 MCP 伺服器的 OAuth 憑證。 +移除 MCP 伺服器的 OAuth 憑證。 ```bash opencode mcp logout [name] @@ -268,7 +268,7 @@ opencode mcp logout [name] #### debug -除錯 MCP 伺服器的 OAuth 連線問題。 +偵錯 MCP 伺服器的 OAuth 連線問題。 ```bash opencode mcp debug @@ -284,11 +284,11 @@ opencode mcp debug opencode models [provider] ``` -此指令以 `provider/model` 格式顯示您設定的供應商中可用的所有模型。 +此指令以 `provider/model` 的格式顯示所有已設定供應商中可用的模型。 -這對於確定 [你的設定](/docs/config/) 中使用的確切模型名稱很有用。 +這對於確定在[設定檔](/docs/config/)中使用的確切模型名稱非常有用。 -您可以選擇傳遞供應商 ID 以按該供應商篩選模型。 +您可以選擇傳入供應商 ID 來按供應商篩選模型。 ```bash opencode models anthropic @@ -296,12 +296,12 @@ opencode models anthropic #### 旗標 -| 旗標 | 說明 | -| ----------- | ---------------------------------------- | -| `--refresh` | 從 models.dev 刷新模型快取 | -| `--verbose` | 使用更詳細的模型輸出(包括成本等元資料) | +| 旗標 | 說明 | +| ----------- | ------------------------------------------ | +| `--refresh` | 從 models.dev 重新整理模型快取 | +| `--verbose` | 使用更詳細的模型輸出(包含費用等中繼資料) | -使用 `--refresh` 旗標來更新快取的模型列表。當新模型已新增到供應商並且您希望在 OpenCode 中查看它們時,這非常有用。 +使用 `--refresh` 旗標可以更新快取的模型列表。當供應商新增了模型並且您希望在 OpenCode 中看到它們時,此功能非常有用。 ```bash opencode models --refresh @@ -311,19 +311,19 @@ opencode models --refresh ### run -透過直接傳遞提示以非互動模式執行 opencode。 +以非互動模式執行 OpenCode,直接傳入提示詞。 ```bash opencode run [message..] ``` -這對於撰寫指令碼、自動化,或者當您想要快速得到答案而不啟動完整的 TUI 時非常有用。例如: +這對於指令碼編寫、自動化或無需啟動完整 TUI 即可快速取得答案的情境非常有用。例如: ```bash "opencode run" opencode run Explain the use of context in Go ``` -您還可以附加到正在執行的 `opencode serve` 實例,以避免每次執行時 MCP 伺服器冷啟動時間: +您還可以連接到正在執行的 `opencode serve` 實例,以避免每次執行時 MCP 伺服器的冷啟動時間: ```bash # Start a headless server in one terminal @@ -335,47 +335,47 @@ opencode run --attach http://localhost:4096 "Explain async/await in JavaScript" #### 旗標 -| 旗標 | 短 | 說明 | -| ------------ | ---- | --------------------------------------------------------------- | -| `--command` | | 要執行的指令,使用訊息作為參數 | -| `--continue` | `-c` | 繼續上一個工作階段 | -| `--session` | `-s` | 繼續指定的工作階段 ID | -| `--fork` | | 繼續時分岔工作階段(與 `--continue` 或 `--session` 一起使用) | -| `--share` | | 分享工作階段 | -| `--model` | `-m` | 使用的模型 (provider/model) | -| `--agent` | | 使用的代理 | -| `--file` | `-f` | 要附加到訊息的檔案 | -| `--format` | | 格式:預設 (formatted) 或 json (原始 JSON 事件) | -| `--title` | | 工作階段標題(如果未提供值,則使用截斷的提示) | -| `--attach` | | 連接到正在執行的 opencode 伺服器(例如,http://localhost:4096) | -| `--port` | | 本地伺服器的連接埠(預設為隨機連接埠) | +| 旗標 | 簡寫 | 說明 | +| ------------ | ---- | -------------------------------------------------------------- | +| `--command` | | 要執行的指令,使用 message 作為參數 | +| `--continue` | `-c` | 繼續上一個工作階段 | +| `--session` | `-s` | 要繼續的工作階段 ID | +| `--fork` | | 繼續時分岔工作階段(與 `--continue` 或 `--session` 搭配使用) | +| `--share` | | 分享工作階段 | +| `--model` | `-m` | 要使用的模型,格式為 provider/model | +| `--agent` | | 要使用的代理 | +| `--file` | `-f` | 附加到訊息的檔案 | +| `--format` | | 格式:default(格式化輸出)或 json(原始 JSON 事件) | +| `--title` | | 工作階段標題(未提供值時使用截斷的提示詞) | +| `--attach` | | 連接到正在執行的 opencode 伺服器(例如 http://localhost:4096) | +| `--port` | | 本地伺服器連接埠(預設為隨機連接埠) | --- ### serve -啟動無介面 opencode 伺服器以進行 API 存取。查看 [伺服器文件](/docs/server) 以獲取完整的 HTTP 介面。 +啟動無介面的 OpenCode 伺服器以提供 API 存取。查看[伺服器文件](/docs/server)了解完整的 HTTP 介面。 ```bash opencode serve ``` -這將啟動一個 HTTP 伺服器,該伺服器提供對 opencode 功能的 API 存取,而無需 TUI 介面。設定 `OPENCODE_SERVER_PASSWORD` 以啟用 HTTP 基本身分驗證(使用者名稱預設為 `opencode`)。 +此指令啟動一個 HTTP 伺服器,提供對 OpenCode 功能的 API 存取,無需 TUI 介面。設定 `OPENCODE_SERVER_PASSWORD` 可啟用 HTTP 基本認證(使用者名稱預設為 `opencode`)。 #### 旗標 | 旗標 | 說明 | | ------------ | -------------------------- | | `--port` | 監聽連接埠 | -| `--hostname` | 監聽的主機名稱 | +| `--hostname` | 監聽主機名稱 | | `--mdns` | 啟用 mDNS 探索 | -| `--cors` | 允許 CORS 的其他瀏覽器來源 | +| `--cors` | 允許 CORS 的額外瀏覽器來源 | --- ### session -管理 opencode 工作階段。 +管理 OpenCode 工作階段。 ```bash opencode session [command] @@ -385,7 +385,7 @@ opencode session [command] #### list -列出所有 opencode 工作階段。 +列出所有 OpenCode 工作階段。 ```bash opencode session list @@ -393,16 +393,16 @@ opencode session list ##### 旗標 -| 旗標 | 短 | 說明 | -| ------------- | ---- | ------------------------------ | -| `--max-count` | `-n` | 限制為最近 N 個工作階段 | -| `--format` | | 輸出格式:table 或 json(table) | +| 旗標 | 簡寫 | 說明 | +| ------------- | ---- | ------------------------------------- | +| `--max-count` | `-n` | 限制為最近 N 個工作階段 | +| `--format` | | 輸出格式:table 或 json(預設 table) | --- ### stats -顯示 opencode 工作階段的 Tokens 使用情況和成本統計資訊。 +顯示 OpenCode 工作階段的 Token 用量和費用統計資訊。 ```bash opencode stats @@ -410,36 +410,36 @@ opencode stats #### 旗標 -| 旗標 | 說明 | -| ----------- | -------------------------------------------------------- | -| `--days` | 顯示過去 N 天(所有時間)的統計數據 | -| `--tools` | 顯示的工具數量(全部) | -| `--models` | 顯示模型使用情況細分(預設隱藏)。傳遞一個數字來顯示前 N | -| `--project` | 按專案過濾(所有專案,空字串:當前專案) | +| 旗標 | 說明 | +| ----------- | ---------------------------------------------------- | +| `--days` | 顯示最近 N 天的統計資訊(預設為所有時間) | +| `--tools` | 顯示的工具數量(預設為全部) | +| `--models` | 顯示模型用量明細(預設隱藏)。傳入數字可顯示前 N 個 | +| `--project` | 按專案篩選(預設為所有專案,傳入空字串表示當前專案) | --- ### export -將工作階段數據導出為 JSON。 +將工作階段資料匯出為 JSON。 ```bash opencode export [sessionID] ``` -如果您不提供工作階段 ID,系統將提示您從可用工作階段中進行選擇。 +如果您不提供工作階段 ID,系統將提示您從可用的工作階段中進行選擇。 --- ### import -從 JSON 檔案或 opencode 分享 URL 匯入工作階段數據。 +從 JSON 檔案或 OpenCode 分享連結匯入工作階段資料。 ```bash opencode import ``` -您以從本地檔案或 opencode 分享 URL 匯入。 +您可以從本地檔案或 OpenCode 分享連結匯入。 ```bash opencode import session.json @@ -450,48 +450,48 @@ opencode import https://opncd.ai/s/abc123 ### web -使用 Web 介面啟動無介面 opencode 伺服器。 +啟動帶有 Web 介面的無介面 OpenCode 伺服器。 ```bash opencode web ``` -這將啟動 HTTP 伺服器並打開網頁瀏覽器以透過 Web 介面存取 opencode。設定 `OPENCODE_SERVER_PASSWORD` 以啟用 HTTP 基本身分驗證(使用者名稱預設為 `opencode`)。 +此指令啟動一個 HTTP 伺服器並開啟瀏覽器,透過 Web 介面存取 OpenCode。設定 `OPENCODE_SERVER_PASSWORD` 可啟用 HTTP 基本認證(使用者名稱預設為 `opencode`)。 #### 旗標 | 旗標 | 說明 | | ------------ | -------------------------- | | `--port` | 監聽連接埠 | -| `--hostname` | 監聽的主機名稱 | +| `--hostname` | 監聽主機名稱 | | `--mdns` | 啟用 mDNS 探索 | -| `--cors` | 允許 CORS 的其他瀏覽器來源 | +| `--cors` | 允許 CORS 的額外瀏覽器來源 | --- ### acp -啟動 ACP (Agent Client Protocol) 伺服器。 +啟動 ACP(Agent Client Protocol)伺服器。 ```bash opencode acp ``` -此指令啟動一個 ACP 伺服器,該伺服器使用 nd-JSON 透過 stdin/stdout 進行通訊。 +此指令啟動一個透過 stdin/stdout 使用 nd-JSON 進行通訊的 ACP 伺服器。 #### 旗標 -| 旗標 | 說明 | -| ------------ | -------------- | -| `--cwd` | 工作目錄 | -| `--port` | 監聽連接埠 | -| `--hostname` | 監聽的主機名稱 | +| 旗標 | 說明 | +| ------------ | ------------ | +| `--cwd` | 工作目錄 | +| `--port` | 監聽連接埠 | +| `--hostname` | 監聽主機名稱 | --- ### uninstall -解除安裝 opencode 並刪除所有相關檔案。 +解除安裝 OpenCode 並刪除所有相關檔案。 ```bash opencode uninstall @@ -499,30 +499,30 @@ opencode uninstall #### 旗標 -| 旗標 | 短 | 說明 | -| --------------- | ---- | -------------------------------- | -| `--keep-config` | `-c` | 保留設定檔 | -| `--keep-data` | `-d` | 保留工作階段數據和快照 | -| `--dry-run` | | 顯示在不刪除的情況下將刪除的內容 | -| `--force` | `-f` | 跳過確認提示 | +| 旗標 | 簡寫 | 說明 | +| --------------- | ---- | ------------------------------ | +| `--keep-config` | `-c` | 保留設定檔 | +| `--keep-data` | `-d` | 保留工作階段資料和快照 | +| `--dry-run` | | 顯示將被刪除的內容但不實際刪除 | +| `--force` | `-f` | 跳過確認提示 | --- ### upgrade -將 opencode 更新到最新版本或特定版本。 +將 OpenCode 更新到最新版本或指定版本。 ```bash opencode upgrade [target] ``` -升級到最新版本。 +更新到最新版本。 ```bash opencode upgrade ``` -升級到特定版本。 +更新到指定版本。 ```bash opencode upgrade v0.1.48 @@ -532,72 +532,72 @@ opencode upgrade v0.1.48 | 旗標 | 簡寫 | 說明 | | ---------- | ---- | ------------------------------------------ | -| `--method` | `-m` | 使用的安裝方法;curl、npm、pnpm、bun、brew | +| `--method` | `-m` | 使用的安裝方式:curl、npm、pnpm、bun、brew | --- ## 全域旗標 -opencode CLI 採用以下全域旗標。 +OpenCode CLI 接受以下全域旗標。 -| 旗標 | 短 | 說明 | +| 旗標 | 簡寫 | 說明 | | -------------- | ---- | ------------------------------------ | -| `--help` | `-h` | 顯示說明 | -| `--version` | `-v` | 列印版本號 | -| `--print-logs` | | 將記錄列印到 stderr | -| `--log-level` | | 記錄等級(debug, info, warn, error) | +| `--help` | `-h` | 顯示說明資訊 | +| `--version` | `-v` | 印出版本號 | +| `--print-logs` | | 將日誌輸出到 stderr | +| `--log-level` | | 日誌等級(DEBUG、INFO、WARN、ERROR) | --- ## 環境變數 -可以使用環境變數設定 opencode。 - -| 變數 | 類型 | 說明 | -| ------------------------------------- | ------- | --------------------------------------------- | -| `OPENCODE_AUTO_SHARE` | boolean | 自動分享工作階段 | -| `OPENCODE_GIT_BASH_PATH` | string | Windows 上 Git Bash 可執行檔案的路徑 | -| `OPENCODE_CONFIG` | string | 設定檔路徑 | -| `OPENCODE_CONFIG_DIR` | string | 設定目錄的路徑 | -| `OPENCODE_CONFIG_CONTENT` | string | 內聯 json 設定內容 | -| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | 禁用自動更新檢查 | -| `OPENCODE_DISABLE_PRUNE` | boolean | 禁用舊數據的修剪 | -| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | 禁用自動終端機標題更新 | -| `OPENCODE_PERMISSION` | string | 內聯 json 權限設定 | -| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | 禁用預設外掛 | -| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | 禁用自動 LSP 伺服器下載 | -| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | 啟用實驗模型 | -| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | 禁用自動上下文壓縮 | -| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | 禁止從 `.claude` 讀取(提示+技巧) | -| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | 禁用讀取 `~/.claude/CLAUDE.md` | -| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | 禁用載入 `.claude/skills` | -| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | 禁用從遠端來源獲取模型 | -| `OPENCODE_FAKE_VCS` | string | 用於測試目的的假 VCS 供應商 | -| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | 禁用檔案時間檢查以進行最佳化 | -| `OPENCODE_CLIENT` | string | 客戶端標識符(預設為 `cli`) | -| `OPENCODE_ENABLE_EXA` | boolean | 啟用 Exa 網路搜尋工具 | -| `OPENCODE_SERVER_PASSWORD` | string | 為 `serve`/`web` 啟用基本身分驗證 | -| `OPENCODE_SERVER_USERNAME` | string | 覆蓋基本身分驗證使用者名稱(預設 `opencode`) | -| `OPENCODE_MODELS_URL` | string | 用於獲取模型設定的自定義 URL | - ---- - -### 實驗性的 - -這些環境變數啟用可能會更改或刪除的實驗性功能。 - -| 變數 | 類型 | 說明 | -| ----------------------------------------------- | ------- | ----------------------------------- | -| `OPENCODE_EXPERIMENTAL` | boolean | 啟用所有實驗性功能 | -| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | 啟用圖示探索 | -| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | 在 TUI 中禁用選擇時複製 | -| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | bash 指令的預設超時(以毫秒為單位) | -| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | LLM 回應的最大輸出 tokens | -| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | 為整個目錄啟用檔案觀察器 | -| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | 啟用 oxfmt 格式化程式 | -| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | 啟用實驗性 LSP 工具 | -| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | 禁用檔案觀察器 | -| `OPENCODE_EXPERIMENTAL_EXA` | boolean | 啟用實驗性 Exa 功能 | -| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | 啟用實驗性 LSP 類型檢查 | -| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | 啟用實驗性 Markdown 功能 | -| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | 啟用計畫模式 | +OpenCode 可以透過環境變數進行設定。 + +| 變數 | 類型 | 說明 | +| ------------------------------------- | ------- | ------------------------------------------- | +| `OPENCODE_AUTO_SHARE` | boolean | 自動分享工作階段 | +| `OPENCODE_GIT_BASH_PATH` | string | Windows 上 Git Bash 可執行檔的路徑 | +| `OPENCODE_CONFIG` | string | 設定檔路徑 | +| `OPENCODE_CONFIG_DIR` | string | 設定目錄路徑 | +| `OPENCODE_CONFIG_CONTENT` | string | 內嵌 JSON 設定內容 | +| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | 停用自動更新檢查 | +| `OPENCODE_DISABLE_PRUNE` | boolean | 停用舊資料清理 | +| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | 停用自動終端機標題更新 | +| `OPENCODE_PERMISSION` | string | 內嵌 JSON 權限設定 | +| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | 停用預設外掛程式 | +| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | 停用 LSP 伺服器自動下載 | +| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | 啟用實驗性模型 | +| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | 停用自動上下文壓縮 | +| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | 停用讀取 `.claude`(提示詞 + 技能) | +| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | 停用讀取 `~/.claude/CLAUDE.md` | +| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | 停用載入 `.claude/skills` | +| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | 停用從遠端來源擷取模型 | +| `OPENCODE_FAKE_VCS` | string | 用於測試目的的模擬 VCS 供應商 | +| `OPENCODE_DISABLE_FILETIME_CHECK` | boolean | 停用檔案時間檢查最佳化 | +| `OPENCODE_CLIENT` | string | 用戶端識別碼(預設為 `cli`) | +| `OPENCODE_ENABLE_EXA` | boolean | 啟用 Exa 網路搜尋工具 | +| `OPENCODE_SERVER_PASSWORD` | string | 為 `serve`/`web` 啟用基本認證 | +| `OPENCODE_SERVER_USERNAME` | string | 覆寫基本認證使用者名稱(預設為 `opencode`) | +| `OPENCODE_MODELS_URL` | string | 自訂模型設定擷取 URL | + +--- + +### 實驗性功能 + +這些環境變數用於啟用可能會變更或移除的實驗性功能。 + +| 變數 | 類型 | 說明 | +| ----------------------------------------------- | ------- | ------------------------------- | +| `OPENCODE_EXPERIMENTAL` | boolean | 啟用所有實驗性功能 | +| `OPENCODE_EXPERIMENTAL_ICON_DISCOVERY` | boolean | 啟用圖示探索 | +| `OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT` | boolean | 停用 TUI 中的選取即複製 | +| `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS` | number | bash 指令的預設逾時時間(毫秒) | +| `OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX` | number | LLM 回應的最大輸出 Token 數 | +| `OPENCODE_EXPERIMENTAL_FILEWATCHER` | boolean | 啟用整個目錄的檔案監看器 | +| `OPENCODE_EXPERIMENTAL_OXFMT` | boolean | 啟用 oxfmt 格式化器 | +| `OPENCODE_EXPERIMENTAL_LSP_TOOL` | boolean | 啟用實驗性 LSP 工具 | +| `OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER` | boolean | 停用檔案監看器 | +| `OPENCODE_EXPERIMENTAL_EXA` | boolean | 啟用實驗性 Exa 功能 | +| `OPENCODE_EXPERIMENTAL_LSP_TY` | boolean | 啟用實驗性 LSP 類型檢查 | +| `OPENCODE_EXPERIMENTAL_MARKDOWN` | boolean | 啟用實驗性 Markdown 功能 | +| `OPENCODE_EXPERIMENTAL_PLAN_MODE` | boolean | 啟用計畫模式 | diff --git a/packages/web/src/content/docs/zh-tw/commands.mdx b/packages/web/src/content/docs/zh-tw/commands.mdx index 88069e27196f..ede5a7081ddc 100644 --- a/packages/web/src/content/docs/zh-tw/commands.mdx +++ b/packages/web/src/content/docs/zh-tw/commands.mdx @@ -1,21 +1,21 @@ --- title: 指令 -description: 為重複任務建立自定義指令。 +description: 為重複任務建立自訂指令。 --- -自定義指令允許您指定在 TUI 中執行該指令時要執行的提示。 +自訂指令允許您指定一個提示詞,當在 TUI 中執行該指令時會執行這個提示詞。 ```bash frame="none" /my-command ``` -除了 `/init`、`/undo`、`/redo`、`/share`、`/help` 等內建指令之外,還有自定義指令。 [了解更多](/docs/tui#commands)。 +自訂指令是 `/init`、`/undo`、`/redo`、`/share`、`/help` 等內建指令之外的補充。[了解更多](/docs/tui#commands)。 --- ## 建立指令檔案 -在 `commands/` 目錄中建立 markdown 檔案來定義自定義指令。 +在 `commands/` 目錄中建立 markdown 檔案來定義自訂指令。 建立 `.opencode/commands/test.md`: @@ -30,7 +30,7 @@ Run the full test suite with coverage report and show any failures. Focus on the failing tests and suggest fixes. ``` -frontmatter 定義指令屬性。內容成為範本。 +frontmatter 定義指令屬性,內容則成為範本。 透過輸入 `/` 後跟指令名稱來使用該指令。 @@ -42,13 +42,13 @@ frontmatter 定義指令屬性。內容成為範本。 ## 設定 -您 可以透過 opencode 設定或透過在 `commands/` 目錄中建立 markdown 檔案來新增自定義指令。 +您可以透過 OpenCode 設定或在 `commands/` 目錄中建立 markdown 檔案來新增自訂指令。 --- ### JSON -在 opencode [設定](/docs/config) 中使用 `command` 選項: +在 OpenCode [設定](/docs/config)中使用 `command` 選項: ```json title="opencode.jsonc" {4-12} { @@ -67,7 +67,7 @@ frontmatter 定義指令屬性。內容成為範本。 } ``` -現在您可以在 TUI 中執行此指令: +現在您可以在 TUI 中執行這個指令: ```bash frame="none" /test @@ -77,10 +77,10 @@ frontmatter 定義指令屬性。內容成為範本。 ### Markdown -您也可以使用 Markdown 檔案定義指令。將它們放入: +您還可以使用 markdown 檔案定義指令。將它們放在: - 全域:`~/.config/opencode/commands/` -- 每個專案:`.opencode/commands/` +- 專案層級:`.opencode/commands/` ```markdown title="~/.config/opencode/commands/test.md" --- @@ -93,7 +93,7 @@ Run the full test suite with coverage report and show any failures. Focus on the failing tests and suggest fixes. ``` -Markdown 檔名成為指令名。例如,`test.md` 讓您執行: +markdown 檔案名稱即為指令名稱。例如,`test.md` 允許您執行: ```bash frame="none" /test @@ -101,15 +101,15 @@ Markdown 檔名成為指令名。例如,`test.md` 讓您執行: --- -## 提示設定 +## 提示詞設定 -自定義指令的提示支援幾個特殊的預留位置和語法。 +自訂指令的提示詞支援多種特殊佔位符和語法。 --- -### 參數 (Arguments) +### 參數 -使用 `$ARGUMENTS` 預留位置將參數傳遞給指令。 +使用 `$ARGUMENTS` 佔位符向指令傳遞參數。 ```md title=".opencode/commands/component.md" --- @@ -120,20 +120,20 @@ Create a new React component named $ARGUMENTS with TypeScript support. Include proper typing and basic structure. ``` -使用參數執行指令: +帶參數執行指令: ```bash frame="none" /component Button ``` -`$ARGUMENTS` 將替換為 `Button`。 +`$ARGUMENTS` 將被替換為 `Button`。 -您也可以使用位置參數存取各個參數: +您還可以使用位置參數存取各個參數: - `$1` - 第一個參數 - `$2` - 第二個參數 - `$3` - 第三個參數 -- 等等... +- 以此類推... 例如: @@ -152,19 +152,19 @@ with the following content: $3 /create-file config.json src "{ \"key\": \"value\" }" ``` -這取代了: +替換結果為: -- `$1` 與 `config.json` -- `$2` 與 `src` -- `$3` 與 `{ "key": "value" }` +- `$1` 替換為 `config.json` +- `$2` 替換為 `src` +- `$3` 替換為 `{ "key": "value" }` --- ### Shell 輸出 -使用 _!`command`_ 將 [bash 指令](/docs/tui#bash-commands) 輸出注入到提示中。 +使用 _!`command`_ 將 [bash 指令](/docs/tui#bash-commands)輸出注入到提示詞中。 -例如,要建立分析測試覆蓋率的自定義指令: +例如,建立一個分析測試覆蓋率的自訂指令: ```md title=".opencode/commands/analyze-coverage.md" --- @@ -190,13 +190,13 @@ Recent git commits: Review these changes and suggest any improvements. ``` -指令在專案的根目錄中執行,其輸出成為提示的一部分。 +指令在專案的根目錄中執行,其輸出會成為提示詞的一部分。 --- -### 檔案參考 +### 檔案參照 -使用 `@` 後跟檔名將檔案包含在指令中。 +使用 `@` 後跟檔案名稱在指令中參照檔案。 ```md title=".opencode/commands/review-component.md" --- @@ -207,19 +207,19 @@ Review the component in @src/components/Button.tsx. Check for performance issues and suggest improvements. ``` -檔案內容會自動包含在提示中。 +檔案內容會自動包含在提示詞中。 --- ## 選項 -讓我們詳細看看設定選項。 +讓我們詳細了解各設定選項。 --- -### 範本 (Template) +### Template -`template` 選項定義執行指令時將發送到 LLM 的提示。 +`template` 選項定義執行指令時傳送給 LLM 的提示詞。 ```json title="opencode.json" { @@ -235,9 +235,9 @@ Check for performance issues and suggest improvements. --- -### 描述 (Description) +### Description -使用 `description` 選項提供指令功能的簡要描述。 +使用 `description` 選項提供指令功能的簡要說明。 ```json title="opencode.json" { @@ -249,15 +249,15 @@ Check for performance issues and suggest improvements. } ``` -當您輸入指令時,這將在 TUI 中顯示為描述。 +當您輸入指令時,這將在 TUI 中顯示為說明。 --- -### 代理 (Agent) +### Agent -使用 `agent` 設定可選擇指定哪個 [代理](/docs/agents) 應執行此指令。 -如果這是 [子代理](/docs/agents/#subagents),該指令將預設觸發子代理呼叫。 -要禁用此行為,請將 `subtask` 設定為 `false`。 +使用 `agent` 設定可選擇指定由哪個[代理](/docs/agents)執行此指令。 +如果這是一個[子代理](/docs/agents/#subagents),該指令預設會觸發子代理呼叫。 +要停用此行為,請將 `subtask` 設定為 `false`。 ```json title="opencode.json" { @@ -269,15 +269,15 @@ Check for performance issues and suggest improvements. } ``` -這是一個**可選**設定選項。如果未指定,則預設為您當前的代理。 +這是一個**可選的**設定選項。如果未指定,預設使用您當前的代理。 --- -### 子任務 (Subtask) +### Subtask -使用 `subtask` 布林值強制指令觸發 [子代理](/docs/agents/#subagents) 呼叫。 -如果您希望指令不污染您的主要上下文並且將**強制**代理充當子代理,那麼這非常有用, -即使 `mode` 在 [代理](/docs/agents) 設定上設定為 `primary`。 +使用 `subtask` 布林值強制指令觸發[子代理](/docs/agents/#subagents)呼叫。 +如果您希望指令不污染主要上下文,這會很有用,它會**強制**代理作為子代理執行, +即使[代理](/docs/agents)設定中的 `mode` 設定為 `primary`。 ```json title="opencode.json" { @@ -289,11 +289,11 @@ Check for performance issues and suggest improvements. } ``` -這是一個**可選**設定選項。 +這是一個**可選的**設定選項。 --- -### 模型 (Model) +### Model 使用 `model` 設定覆寫此指令的預設模型。 @@ -307,16 +307,16 @@ Check for performance issues and suggest improvements. } ``` -這是一個**可選**設定選項。 +這是一個**可選的**設定選項。 --- -### 內建 +## 內建指令 -opencode 包含 `/init`、`/undo`、`/redo`、`/share`、`/help` 等內建指令; [了解更多](/docs/tui#commands)。 +OpenCode 包含多個內建指令,如 `/init`、`/undo`、`/redo`、`/share`、`/help`;[了解更多](/docs/tui#commands)。 :::note -自定義指令可以覆寫內建指令。 +自訂指令可以覆寫內建指令。 ::: -如果您定義同名的自定義指令,它將覆寫內建指令。 +如果您定義了同名的自訂指令,它將覆寫內建指令。 diff --git a/packages/web/src/content/docs/zh-tw/config.mdx b/packages/web/src/content/docs/zh-tw/config.mdx index 817b08673b10..3715dd0c9f77 100644 --- a/packages/web/src/content/docs/zh-tw/config.mdx +++ b/packages/web/src/content/docs/zh-tw/config.mdx @@ -1,15 +1,15 @@ --- title: 設定 -description: 使用 opencode JSON 設定。 +description: 使用 OpenCode JSON 設定。 --- -您可以使用 JSON 設定檔設定 opencode。 +您可以使用 JSON 設定檔來設定 OpenCode。 --- ## 格式 -opencode 支援 **JSON** 和 **JSONC**(帶註解的 JSON)格式。 +OpenCode 支援 **JSON** 和 **JSONC**(帶註解的 JSON)格式。 ```jsonc title="opencode.jsonc" { @@ -25,15 +25,15 @@ opencode 支援 **JSON** 和 **JSONC**(帶註解的 JSON)格式。 ## 位置 -您可以將設定放置在幾個不同的位置,它們有一個不同的優先順序。 +您可以將設定放置在不同的位置,它們具有不同的優先順序。 :::note -設定檔**合併在一起**,而不是取代。 +設定檔是**合併在一起**的,而不是替換。 ::: -設定檔被合併在一起,而不是被取代。以下設定位置的設定被合併。僅當鍵值衝突時,後面的設定才會覆寫前面的設定。保留所有設定中的非衝突設定。 +設定檔是合併在一起的,而不是被替換。來自以下設定位置的設定會被合併。後面的設定僅在鍵衝突時覆寫前面的設定。所有設定中的非衝突設定都會被保留。 -例如,如果您的全域設定設定 `theme: "opencode"` 和 `autoupdate: true`,並且您的專案設定設定 `model: "anthropic/claude-sonnet-4-5"`,則最終設定將包括所有三個設定。 +例如,如果您的全域設定設定了 `theme: "opencode"` 和 `autoupdate: true`,而您的專案設定設定了 `model: "anthropic/claude-sonnet-4-5"`,則最終設定將包含所有三個設定。 --- @@ -42,27 +42,27 @@ opencode 支援 **JSON** 和 **JSONC**(帶註解的 JSON)格式。 設定來源按以下順序載入(後面的來源覆寫前面的來源): 1. **遠端設定**(來自 `.well-known/opencode`)- 組織預設值 -2. **全域設定** (`~/.config/opencode/opencode.json`) - 使用者偏好設定 -3. **自定義設定** (`OPENCODE_CONFIG` env var) - 自定義覆寫 -4. **專案設定**(專案中的 `opencode.json`)- 專案特定的設定 -5. **`.opencode` 目錄** - 代理、指令、外掛 -6. **內聯設定** (`OPENCODE_CONFIG_CONTENT` env var) - 執行時覆寫 +2. **全域設定**(`~/.config/opencode/opencode.json`)- 使用者偏好 +3. **自訂設定**(`OPENCODE_CONFIG` 環境變數)- 自訂覆寫 +4. **專案設定**(專案中的 `opencode.json`)- 專案特定設定 +5. **`.opencode` 目錄** - 代理、指令、外掛程式 +6. **內嵌設定**(`OPENCODE_CONFIG_CONTENT` 環境變數)- 執行時覆寫 這意味著專案設定可以覆寫全域預設值,全域設定可以覆寫遠端組織預設值。 :::note -`.opencode` 和 `~/.config/opencode` 目錄對子目錄使用**複數名稱**:`agents/`、`commands/`、`modes/`、`plugins/`、`skills/`、`tools/` 和 `themes/`。為了向後相容,也支援單數名稱(例如 `agent/`)。 +`.opencode` 和 `~/.config/opencode` 目錄的子目錄使用**複數名稱**:`agents/`、`commands/`、`modes/`、`plugins/`、`skills/`、`tools/` 和 `themes/`。為了向後相容,也支援單數名稱(例如 `agent/`)。 ::: --- ### 遠端 -組織可以透過 `.well-known/opencode` 端點提供預設設定。當您向支援它的供應商進行身分驗證時,會自動取得該資訊。 +組織可以透過 `.well-known/opencode` 端點提供預設設定。當您使用支援該功能的供應商進行身分驗證時,會自動擷取此設定。 -首先載入遠端設定,作為基礎層。所有其他設定來源(全域、專案)都可以覆寫這些預設值。 +遠端設定最先載入,作為基礎層。所有其他設定來源(全域、專案)都可以覆寫這些預設值。 -例如,如果您的組織提供預設禁用的 MCP 伺服器: +例如,如果您的組織提供了預設停用的 MCP 伺服器: ```json title="Remote config from .well-known/opencode" { @@ -94,63 +94,63 @@ opencode 支援 **JSON** 和 **JSONC**(帶註解的 JSON)格式。 ### 全域 -將全域 opencode 設定放在 `~/.config/opencode/opencode.json` 中。使用全域設定來實現使用者範圍的偏好設定,例如主題、供應商或按鍵綁定。 +將全域 OpenCode 設定放在 `~/.config/opencode/opencode.json` 中。使用全域設定來設定使用者層級的偏好,例如主題、供應商或快捷鍵。 全域設定覆寫遠端組織預設值。 --- -### 每個專案 +### 專案層級 -在專案根目錄中新增 `opencode.json`。專案設定在標準設定檔中具有最高優先級 - 它覆寫全域設定和遠端設定。 +在專案根目錄中新增 `opencode.json`。專案設定在標準設定檔中具有最高優先級——它會覆寫全域設定和遠端設定。 :::tip 將專案特定設定放在專案的根目錄中。 ::: -當 opencode 啟動時,它會在當前目錄中尋找設定檔或遍歷到最近的 Git 目錄。 +當 OpenCode 啟動時,它會在當前目錄中尋找設定檔,或向上遍歷到最近的 Git 目錄。 -這也可以安全地簽入 Git 並使用與全域模式相同的模式。 +該設定檔也可以安全地提交到 Git 中,並使用與全域設定相同的 Schema。 --- -### 自定義路徑 +### 自訂路徑 -使用 `OPENCODE_CONFIG` 環境變數指定自定義設定檔路徑。 +使用 `OPENCODE_CONFIG` 環境變數指定自訂設定檔路徑。 ```bash export OPENCODE_CONFIG=/path/to/my/custom-config.json opencode run "Hello world" ``` -自定義設定按優先順序在全域設定和專案設定之間載入。 +自訂設定在優先順序中位於全域設定和專案設定之間載入。 --- -### 自定義目錄 +### 自訂目錄 -使用 `OPENCODE_CONFIG_DIR` 環境變數指定自定義設定目錄。將在該目錄中搜尋代理、指令、模式和外掛,就像標準 `.opencode` 目錄一樣,並且應該遵循相同的結構。 +使用 `OPENCODE_CONFIG_DIR` 環境變數指定自訂設定目錄。該目錄會像標準 `.opencode` 目錄一樣被搜尋代理、指令、模式和外掛程式,並且應遵循相同的結構。 ```bash export OPENCODE_CONFIG_DIR=/path/to/my/config-directory opencode run "Hello world" ``` -自定義目錄在全域設定和 `.opencode` 目錄之後載入,因此它**可以覆寫**它們的設定。 +自訂目錄在全域設定和 `.opencode` 目錄之後載入,因此**可以覆寫**它們的設定。 --- -## 架構 +## Schema -設定檔具有在 [**`opencode.ai/config.json`**](https://opencode.ai/config.json) 中定義的架構。 +設定檔具有在 [**`opencode.ai/config.json`**](https://opencode.ai/config.json) 中定義的 Schema。 -您的編輯器應該能夠根據架構進行驗證和自動完成。 +您的編輯器應該能夠基於該 Schema 進行驗證和自動補全。 --- ### TUI -您可以透過 `tui` 選項設定特定於 TUI 的設定。 +您可以透過 `tui` 選項設定 TUI 相關設定。 ```json title="opencode.json" { @@ -167,17 +167,17 @@ opencode run "Hello world" 可用選項: -- `scroll_acceleration.enabled` - 啟用 macOS 風格的捲動加速。 **優先於 `scroll_speed`。** -- `scroll_speed` - 自定義捲動速度倍數(預設值:`3`,最小值:`1`)。如果 `scroll_acceleration.enabled` 是 `true`,則忽略。 -- `diff_style` - 控制差異顯示。 `"auto"` 適應終端機寬度,`"stacked"` 始終顯示單列。 +- `scroll_acceleration.enabled` - 啟用 macOS 風格的捲動加速。**優先於 `scroll_speed`。** +- `scroll_speed` - 自訂捲動速度倍率(預設值:`3`,最小值:`1`)。如果 `scroll_acceleration.enabled` 為 `true`,則忽略此選項。 +- `diff_style` - 控制差異呈現方式。`"auto"` 根據終端機寬度自適應,`"stacked"` 始終顯示單列。 -[在此處了解有關使用 TUI 的更多資訊](/docs/tui)。 +[在此了解更多關於 TUI 的資訊](/docs/tui)。 --- ### 伺服器 -您可以透過 `opencode serve` 選項為 `opencode web` 和 `server` 指令設定伺服器設定。 +您可以透過 `server` 選項為 `opencode serve` 和 `opencode web` 指令設定伺服器設定。 ```json title="opencode.json" { @@ -194,13 +194,13 @@ opencode run "Hello world" 可用選項: -- `port` - 監聽的連接埠。 -- `hostname` - 要監聽的主機名稱。當 `mdns` 啟用且未設定主機名稱時,預設為 `0.0.0.0`。 -- `mdns` - 啟用 mDNS 服務探索。這允許網路上的其他設備發現您的 opencode 伺服器。 -- `mdnsDomain` - mDNS 服務的自定義網域名稱。預設為 `opencode.local`。對於在同一網路上執行多個實例很有用。 -- `cors` - 從基於瀏覽器的客戶端使用 HTTP 伺服器時允許 CORS 的其他來源。值必須是完整來源(通訊協定+主機+可選連接埠),例如 `https://app.example.com`。 +- `port` - 監聽連接埠。 +- `hostname` - 監聽主機名稱。當 `mdns` 啟用且未設定主機名稱時,預設為 `0.0.0.0`。 +- `mdns` - 啟用 mDNS 服務探索。這允許網路上的其他裝置發現您的 OpenCode 伺服器。 +- `mdnsDomain` - mDNS 服務的自訂網域名稱。預設為 `opencode.local`。適用於在同一網路上執行多個實例的情境。 +- `cors` - 從基於瀏覽器的用戶端使用 HTTP 伺服器時允許 CORS 的額外來源。值必須是完整的來源(通訊協定 + 主機 + 可選連接埠),例如 `https://app.example.com`。 -[在此處了解有關伺服器的更多資訊](/docs/server)。 +[在此了解更多關於伺服器的資訊](/docs/server)。 --- @@ -218,13 +218,13 @@ opencode run "Hello world" } ``` -[在此處了解有關工具的更多資訊](/docs/tools)。 +[在此了解更多關於工具的資訊](/docs/tools)。 --- ### 模型 -您可以透過 `provider`、`model` 和 `small_model` 選項來設定要在 opencode 設定中使用的供應商和模型。 +您可以透過 `provider`、`model` 和 `small_model` 選項在 OpenCode 設定中設定要使用的供應商和模型。 ```json title="opencode.json" { @@ -235,7 +235,7 @@ opencode run "Hello world" } ``` -`small_model` 選項為標題生成等輕量級任務設定單獨的模型。預設情況下,如果您的供應商可以提供更便宜的模型,opencode 會嘗試使用更便宜的模型,否則它會退回到您的主模型。 +`small_model` 選項為標題生成等輕量級任務設定單獨的模型。預設情況下,如果您的供應商有更便宜的模型可用,OpenCode 會嘗試使用該模型,否則會回退到您的主模型。 供應商選項可以包括 `timeout` 和 `setCacheKey`: @@ -253,16 +253,16 @@ opencode run "Hello world" } ``` -- `timeout` - 請求超時以毫秒為單位(預設值:300000)。設定為 `false` 以禁用。 -- `setCacheKey` - 確保始終為指定的供應商設定快取金鑰。 +- `timeout` - 請求逾時時間,單位為毫秒(預設值:300000)。設定為 `false` 可停用逾時。 +- `setCacheKey` - 確保始終為指定供應商設定快取金鑰。 -您也可以設定 [本地模型](/docs/models#local)。 [了解更多](/docs/models)。 +您還可以設定[本地模型](/docs/models#local)。[了解更多](/docs/models)。 --- -#### 特定於供應商的選項 +#### 供應商特定選項 -某些供應商支援除通用 `timeout` 和 `apiKey` 設定之外的其他設定選項。 +一些供應商支援除通用 `timeout` 和 `apiKey` 設定之外的額外設定選項。 ##### Amazon Bedrock @@ -283,21 +283,21 @@ Amazon Bedrock 支援 AWS 特定設定: } ``` -- `region` - Bedrock 的 AWS 區域(預設為 `AWS_REGION` env var 或 `us-east-1`) -- `profile` - 來自 `~/.aws/credentials` 的 AWS 命名設定檔(預設為 `AWS_PROFILE` env var) -- `endpoint` - VPC 終端節點的自定義終端節點 URL。這是使用 AWS 特定術語的通用 `baseURL` 選項的別名。如果兩者都指定,`endpoint` 優先。 +- `region` - Bedrock 的 AWS 區域(預設為 `AWS_REGION` 環境變數或 `us-east-1`) +- `profile` - 來自 `~/.aws/credentials` 的 AWS 命名設定檔(預設為 `AWS_PROFILE` 環境變數) +- `endpoint` - VPC 端點的自訂端點 URL。這是通用 `baseURL` 選項使用 AWS 特定術語的別名。如果兩者都指定,`endpoint` 優先。 :::note -Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定檔的身分驗證。詳情請參閱 [認證優先級](/docs/providers#authentication-precedence)。 +Bearer Token(`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`)優先於基於設定檔的身分驗證。詳情請參見[認證優先級](/docs/providers#authentication-precedence)。 ::: -[了解有關 Amazon Bedrock 設定的更多資訊](/docs/providers#amazon-bedrock)。 +[了解更多關於 Amazon Bedrock 設定的資訊](/docs/providers#amazon-bedrock)。 --- ### 主題 -您可以透過 `theme` 選項在 opencode 設定中設定要使用的主題。 +您可以透過 OpenCode 設定中的 `theme` 選項設定要使用的主題。 ```json title="opencode.json" { @@ -306,7 +306,7 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -[在這裡了解更多](/docs/themes)。 +[在此了解更多](/docs/themes)。 --- @@ -332,13 +332,13 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -您也可以使用 `~/.config/opencode/agents/` 或 `.opencode/agents/` 中的 markdown 檔案定義代理。 [在這裡了解更多](/docs/agents)。 +您還可以使用 `~/.config/opencode/agents/` 或 `.opencode/agents/` 中的 Markdown 檔案定義代理。[在此了解更多](/docs/agents)。 --- ### 預設代理 -您可以使用 `default_agent` 選項設定預設代理。當沒有明確指定時,這將決定使用哪個代理。 +您可以使用 `default_agent` 選項設定預設代理。當未明確指定代理時,將使用該預設代理。 ```json title="opencode.json" { @@ -347,15 +347,15 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -預設代理必須是主代理(而不是子代理)。這可以是內建代理,例如 `"build"` 或 `"plan"`,或者您定義的 [自定義代理](/docs/agents)。如果指定的代理不存在或者是子代理,opencode 將退回到 `"build"` 並發出警告。 +預設代理必須是主代理(不能是子代理)。可以是內建代理(如 `"build"` 或 `"plan"`),也可以是您定義的[自訂代理](/docs/agents)。如果指定的代理不存在或是子代理,OpenCode 將回退到 `"build"` 並發出警告。 -此設定適用於所有介面:TUI、CLI (`opencode run`)、桌面應用程式和 GitHub Action。 +此設定適用於所有介面:TUI、CLI(`opencode run`)、桌面應用程式和 GitHub Action。 --- ### 分享 -您可以透過 `share` 選項設定 [分享](/docs/share) 功能。 +您可以透過 `share` 選項設定[分享](/docs/share)功能。 ```json title="opencode.json" { @@ -364,19 +364,19 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -這需要: +該選項接受: - `"manual"` - 允許透過指令手動分享(預設) -- `"auto"` - 自動分享新對話 -- `"disabled"` - 完全禁用分享 +- `"auto"` - 自動分享新工作階段 +- `"disabled"` - 完全停用分享 -預設情況下,分享設定為手動模式,您需要使用 `/share` 指令明確分享對話。 +預設情況下,分享設定為手動模式,您需要使用 `/share` 指令明確分享工作階段。 --- ### 指令 -您可以透過 `command` 選項為重複任務設定自定義指令。 +您可以透過 `command` 選項為重複任務設定自訂指令。 ```jsonc title="opencode.jsonc" { @@ -396,13 +396,13 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -您也可以使用 `~/.config/opencode/commands/` 或 `.opencode/commands/` 中的 Markdown 檔案定義指令。 [在這裡了解更多](/docs/commands)。 +您還可以使用 `~/.config/opencode/commands/` 或 `.opencode/commands/` 中的 Markdown 檔案定義指令。[在此了解更多](/docs/commands)。 --- -### 按鍵綁定 +### 快捷鍵 -您可以透過 `keybinds` 選項自定義您的按鍵綁定。 +您可以透過 `keybinds` 選項自訂快捷鍵。 ```json title="opencode.json" { @@ -411,13 +411,13 @@ Bearer Token (`AWS_BEARER_TOKEN_BEDROCK` 或 `/connect`) 優先於基於設定 } ``` -[在這裡了解更多](/docs/keybinds)。 +[在此了解更多](/docs/keybinds)。 --- ### 自動更新 -opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoupdate` 選項禁用此功能。 +OpenCode 啟動時會自動下載新版本。您可以使用 `autoupdate` 選項停用此功能。 ```json title="opencode.json" { @@ -426,14 +426,14 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -如果您不想更新但希望在新版本可用時收到通知,請將 `autoupdate` 設定為 `"notify"`。 -請注意,這僅在未使用 Homebrew 等套件管理器安裝時才有效。 +如果您不想自動更新但希望在新版本可用時收到通知,可將 `autoupdate` 設定為 `"notify"`。 +請注意,此功能僅在未透過 Homebrew 等套件管理器安裝時有效。 --- -### 格式化程式 +### 格式化器 -您可以透過 `formatter` 選項設定程式碼格式化程式。 +您可以透過 `formatter` 選項設定程式碼格式化器。 ```json title="opencode.json" { @@ -453,15 +453,15 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -[在此處了解有關格式化程式的更多資訊](/docs/formatters)。 +[在此了解更多關於格式化器的資訊](/docs/formatters)。 --- ### 權限 -預設情況下,opencode **允許所有操作**,無需明確批准。您可以使用 `permission` 選項更改此設定。 +預設情況下,OpenCode **允許所有操作**,無需明確批准。您可以使用 `permission` 選項變更此行為。 -例如,要確保 `edit` 和 `bash` 工具需要使用者批准: +例如,要讓 `edit` 和 `bash` 工具需要使用者確認: ```json title="opencode.json" { @@ -473,7 +473,7 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -[在此處了解有關權限的更多資訊](/docs/permissions)。 +[在此了解更多關於權限的資訊](/docs/permissions)。 --- @@ -486,19 +486,21 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup "$schema": "https://opencode.ai/config.json", "compaction": { "auto": true, - "prune": true + "prune": true, + "reserved": 10000 } } ``` - `auto` - 當上下文已滿時自動壓縮工作階段(預設值:`true`)。 -- `prune` - 刪除舊工具輸出以節省 tokens(預設值:`true`)。 +- `prune` - 刪除舊的工具輸出以節省 Token(預設值:`true`)。 +- `reserved` - 壓縮時的 Token 緩衝區。保留足夠的窗口以避免壓縮過程中溢出。 --- -### 觀察者 (Watcher) +### 檔案監看器 -您可以透過 `watcher` 選項設定檔案觀察器忽略模式。 +您可以透過 `watcher` 選項設定檔案監看器的忽略模式。 ```json title="opencode.json" { @@ -509,7 +511,7 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -模式遵循 glob 語法。使用它可以從檔案監視中排除嘈雜的目錄。 +模式遵循 glob 語法。使用此選項可以從檔案監看中排除頻繁變動的目錄。 --- @@ -524,15 +526,15 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -[在這裡了解更多](/docs/mcp-servers)。 +[在此了解更多](/docs/mcp-servers)。 --- -### 外掛 +### 外掛程式 -[外掛](/docs/plugins) 使用自定義工具、掛鉤和整合擴展 opencode。 +[外掛程式](/docs/plugins)透過自訂工具、掛鉤和整合來擴展 OpenCode。 -將外掛檔案放置在 `.opencode/plugins/` 或 `~/.config/opencode/plugins/` 中。您也可以透過 `plugin` 選項從 npm 載入外掛。 +將外掛程式檔案放置在 `.opencode/plugins/` 或 `~/.config/opencode/plugins/` 中。您還可以透過 `plugin` 選項從 npm 載入外掛程式。 ```json title="opencode.json" { @@ -541,13 +543,13 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -[在這裡了解更多](/docs/plugins)。 +[在此了解更多](/docs/plugins)。 --- -### 指示 (Instructions) +### 指示 -您可以透過 `instructions` 選項設定您正在使用的模型的指示。 +您可以透過 `instructions` 選項為所使用的模型設定指示。 ```json title="opencode.json" { @@ -556,13 +558,13 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -這需要指示檔案的路徑和全域模式陣列。 [了解更多關於規則在這裡](/docs/rules)。 +該選項接受指示檔案路徑和 glob 模式的陣列。[在此了解更多關於規則的資訊](/docs/rules)。 --- -### 禁用供應商 +### 停用供應商 -您可以透過 `disabled_providers` 選項禁用自動載入的供應商。當您想要阻止載入某些供應商(即使其憑證可用)時,這非常有用。 +您可以透過 `disabled_providers` 選項停用自動載入的供應商。當您希望阻止某些供應商被載入(即使其憑證可用)時,此選項非常有用。 ```json title="opencode.json" { @@ -575,17 +577,17 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup `disabled_providers` 優先於 `enabled_providers`。 ::: -`disabled_providers` 選項接受供應商 ID 陣列。當供應商被禁用時: +`disabled_providers` 選項接受供應商 ID 的陣列。當某個供應商被停用時: -- 即使設定了環境變數也不會載入。 -- 即使透過 `/connect` 指令設定 API 金鑰,也不會載入它。 -- 供應商的模型不會出現在模型選擇列表中。 +- 即使設定了環境變數,也不會被載入。 +- 即使透過 `/connect` 指令設定了 API 金鑰,也不會被載入。 +- 該供應商的模型不會出現在模型選擇列表中。 --- -### 啟用的供應商 +### 啟用供應商 -您可以透過 `enabled_providers` 選項指定供應商的允許清單。設定後,僅啟用指定的供應商,所有其他供應商將被忽略。 +您可以透過 `enabled_providers` 選項指定允許使用的供應商白名單。設定後,僅啟用指定的供應商,所有其他供應商將被忽略。 ```json title="opencode.json" { @@ -594,19 +596,19 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -當您想要限制 opencode 僅使用特定的供應商而不是一一禁用它們時,這非常有用。 +當您希望限制 OpenCode 僅使用特定供應商,而不是逐一停用其他供應商時,此選項非常有用。 :::note `disabled_providers` 優先於 `enabled_providers`。 ::: -如果某個供應商同時出現在 `enabled_providers` 和 `disabled_providers` 中,則 `disabled_providers` 優先考慮向後相容性。 +如果某個供應商同時出現在 `enabled_providers` 和 `disabled_providers` 中,為了向後相容,`disabled_providers` 優先。 --- -### 實驗性的 +### 實驗性功能 -`experimental` 鍵包含正在積極開發的選項。 +`experimental` 鍵包含正在積極開發中的選項。 ```json title="opencode.json" { @@ -616,20 +618,20 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup ``` :::caution -實驗選項不穩定。它們可能會更改或被刪除,恕不另行通知。 +實驗性選項不穩定。它們可能會在不另行通知的情況下被變更或移除。 ::: --- ## 變數 -您可以在設定檔中使用變數替換來引用環境變數和檔案內容。 +您可以在設定檔中使用變數替換來參照環境變數和檔案內容。 --- ### 環境變數 -使用 `{env:VARIABLE_NAME}` 替換環境變數: +使用 `{env:VARIABLE_NAME}` 來替換環境變數: ```json title="opencode.json" { @@ -646,13 +648,13 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup } ``` -如果未設定環境變數,它將被替換為空字串。 +如果環境變數未設定,它將被替換為空字串。 --- ### 檔案 -使用 `{file:path/to/file}` 替換檔案的內容: +使用 `{file:path/to/file}` 來替換檔案內容: ```json title="opencode.json" { @@ -670,11 +672,11 @@ opencode 將在啟動時自動下載任何新的更新。您可以使用 `autoup 檔案路徑可以是: -- 相對於設定檔目錄 -- 或者以 `/` 或 `~` 開頭的絕對路徑 +- 相對於設定檔所在目錄的路徑 +- 以 `/` 或 `~` 開頭的絕對路徑 -這些對於: +這些功能適用於: -- 將 API 金鑰等敏感數據保存在單獨的檔案中。 -- 包含大型指示檔案,而不會弄亂您的設定。 -- 跨多個設定檔共享通用設定片段。 +- 將 API 金鑰等敏感資料保存在單獨的檔案中。 +- 引入大型指示檔案而不會使設定變得雜亂。 +- 在多個設定檔之間共享通用設定片段。 diff --git a/packages/web/src/content/docs/zh-tw/custom-tools.mdx b/packages/web/src/content/docs/zh-tw/custom-tools.mdx index 29db4675e2f0..3229c6fddfac 100644 --- a/packages/web/src/content/docs/zh-tw/custom-tools.mdx +++ b/packages/web/src/content/docs/zh-tw/custom-tools.mdx @@ -1,30 +1,30 @@ --- -title: 自定義工具 -description: 建立 LLM 可以在 opencode 中呼叫的工具。 +title: 自訂工具 +description: 建立 LLM 可在 OpenCode 中呼叫的工具。 --- -自定義工具是您建立的函式,LLM 可以在對話期間呼叫。它們與 opencode 的 [內建工具](/docs/tools) 一起工作,例如 `read`、`write` 和 `bash`。 +自訂工具是您建立的函式,LLM 可以在對話過程中呼叫它們。它們與 OpenCode 的[內建工具](/docs/tools)(如 `read`、`write` 和 `bash`)協同工作。 --- ## 建立工具 -工具定義為 **TypeScript** 或 **JavaScript** 檔案。但是,工具定義可以呼叫用 **任何語言** 編寫的指令碼 - TypeScript 或 JavaScript 僅用於工具定義本身。 +工具以 **TypeScript** 或 **JavaScript** 檔案的形式定義。不過,工具定義可以呼叫**任何語言**編寫的指令碼——TypeScript 或 JavaScript 僅用於工具定義本身。 --- ### 位置 -它們可以定義為: +工具可以在以下位置定義: -- 透過將它們放在專案的 `.opencode/tools/` 目錄中來本地進行。 -- 或者在全域範圍內,將它們放置在 `~/.config/opencode/tools/` 中。 +- 本地定義:將工具檔案放在專案的 `.opencode/tools/` 目錄中。 +- 全域定義:將工具檔案放在 `~/.config/opencode/tools/` 中。 --- ### 結構 -建立工具的最簡單方法是使用 `tool()` 輔助程式,它提供類型安全和驗證。 +建立工具最簡單的方式是使用 `tool()` 輔助函式,它提供型別安全和參數校驗。 ```ts title=".opencode/tools/database.ts" {1} import { tool } from "@opencode-ai/plugin" @@ -41,13 +41,13 @@ export default tool({ }) ``` -**檔名**成為**工具名稱**。以上建立了一個 `database` 工具。 +**檔案名稱**即為**工具名稱**。上面的範例建立了一個名為 `database` 的工具。 --- -#### 每個檔案多個工具 +#### 單檔案多工具 -您也可以從單個檔案匯出多個工具。每個匯出都會成為**一個單獨的工具**,名稱為 **`_`**: +您也可以從單個檔案中匯出多個工具。每個匯出都會成為**一個獨立的工具**,命名格式為 **`_`**: ```ts title=".opencode/tools/math.ts" import { tool } from "@opencode-ai/plugin" @@ -75,13 +75,13 @@ export const multiply = tool({ }) ``` -這將建立兩個工具:`math_add` 和 `math_multiply`。 +這會建立兩個工具:`math_add` 和 `math_multiply`。 --- -### 參數 (Arguments) +### 參數 -您可以使用 `tool.schema`(即 [Zod](https://zod.dev))來定義參數類型。 +您可以使用 `tool.schema`(即 [Zod](https://zod.dev))來定義參數型別。 ```ts "tool.schema" args: { @@ -89,7 +89,7 @@ args: { } ``` -您也可以直接匯入 [Zod](https://zod.dev) 並返回一個一般物件: +您也可以直接匯入 [Zod](https://zod.dev) 並回傳一個普通物件: ```ts {6} import { z } from "zod" @@ -108,9 +108,9 @@ export default { --- -### 上下文 (Context) +### 上下文 -工具接收有關當前工作階段的上下文: +工具會接收當前工作階段的上下文資訊: ```ts title=".opencode/tools/project.ts" {8} import { tool } from "@opencode-ai/plugin" @@ -126,18 +126,18 @@ export default tool({ }) ``` -使用 `context.directory` 作為工作階段工作目錄。 -使用 `context.worktree` 作為 git 工作樹根。 +使用 `context.directory` 取得工作階段的工作目錄。 +使用 `context.worktree` 取得 git worktree 根目錄。 --- ## 範例 -### 用 Python 編寫一個工具 +### 用 Python 編寫工具 -您可以用任何您想要的語言編寫工具。下面是一個使用 Python 將兩個數字相加的範例。 +您可以使用任何語言編寫工具。以下範例展示了如何用 Python 實作兩數相加。 -首先,將該工具建立為 Python 指令碼: +首先,建立一個 Python 指令碼作為工具: ```python title=".opencode/tools/add.py" import sys @@ -147,7 +147,7 @@ b = int(sys.argv[2]) print(a + b) ``` -然後建立呼叫它的工具定義: +然後建立呼叫該指令碼的工具定義: ```ts title=".opencode/tools/python-add.ts" {10} import { tool } from "@opencode-ai/plugin" @@ -167,4 +167,4 @@ export default tool({ }) ``` -這裡我們使用 [`Bun.$`](https://bun.com/docs/runtime/shell) 公用程式來執行 Python 指令碼。 +這裡我們使用 [`Bun.$`](https://bun.com/docs/runtime/shell) 工具函式來執行 Python 指令碼。 diff --git a/packages/web/src/content/docs/zh-tw/ecosystem.mdx b/packages/web/src/content/docs/zh-tw/ecosystem.mdx index ca0d8bf15d60..3a867d14305b 100644 --- a/packages/web/src/content/docs/zh-tw/ecosystem.mdx +++ b/packages/web/src/content/docs/zh-tw/ecosystem.mdx @@ -1,76 +1,76 @@ --- title: 生態系統 -description: 使用 opencode 構建的專案和整合。 +description: 基於 OpenCode 建置的專案與整合。 --- -基於 opencode 的社群專案集合。 +基於 OpenCode 建置的社群專案合集。 :::note -想要將您的 opencode 相關專案添加到此列表中嗎?提交 PR。 +想將您的 OpenCode 相關專案新增到此列表中?歡迎提交 PR。 ::: -您也可以查看 [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) 和 [opencode.cafe](https://opencode.cafe),這是一個聚合生態系統和社群的社群。 +您還可以查看 [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) 和 [opencode.cafe](https://opencode.cafe),這是一個聚合生態系統與社群資源的社群。 --- -## 外掛 +## 外掛程式 -| 名稱 | 描述 | -| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| [opencode-daytona](https://github.com/jamesmurdza/daytona/blob/main/guides/typescript/opencode/README.md) | 使用 git 同步和即時預覽在隔離的 Daytona 沙箱中自動執行 opencode 工作階段 | -| [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) | 自動注入 Helicone 工作階段標頭以進行請求分組 | -| [opencode-type-inject](https://github.com/nick-vi/opencode-type-inject) | 使用搜尋工具將 TypeScript/Svelte 類型自動注入到檔案讀取中 | -| [opencode-openai-codex-auth](https://github.com/numman-ali/opencode-openai-codex-auth) | 使用您的 ChatGPT Plus/Pro 訂閱而不是 API 額度 | -| [opencode-gemini-auth](https://github.com/jenslys/opencode-gemini-auth) | 使用您現有的 Gemini 計畫而不是 API 計費 | -| [opencode-antigravity-auth](https://github.com/NoeFabris/opencode-antigravity-auth) | 使用 Antigravity 的免費模型代替 API 計費 | -| [opencode-devcontainers](https://github.com/athal7/opencode-devcontainers) | 具有淺層複製和自動分配連接埠的多分支開發容器隔離 | -| [opencode-google-antigravity-auth](https://github.com/shekohex/opencode-google-antigravity-auth) | Google Antigravity OAuth 外掛,支援 Google 搜尋和更強大的 API 處理 | -| [opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) | 透過修剪過時的工具輸出來最佳化代幣使用 | -| [opencode-websearch-cited](https://github.com/ghoulr/opencode-websearch-cited.git) | 為具有 Google Grounding 風格的受支援供應商添加原生網路搜尋支援 | -| [opencode-pty](https://github.com/shekohex/opencode-pty.git) | 使 AI 代理能夠在 PTY 中執行背景處理程序,並向其發送互動式輸入。 | -| [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy) | 非互動式 shell 指令說明 - 防止依賴 TTY 的操作卡住 | -| [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | 使用 Wakatime 追蹤 opencode 使用情況 | -| [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | 清理 LLM 生成的 Markdown 表格 | -| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 使用 Morph Fast Apply API 和惰性編輯標記將程式碼編輯速度提高 10 倍 | -| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | 背景代理、預先建置的 LSP/AST/MCP 工具、精選代理、相容 Claude Code | -| [opencode-notificator](https://github.com/panta82/opencode-notificator) | opencode 工作階段的桌面通知和聲音警報 | -| [opencode-notifier](https://github.com/mohak34/opencode-notifier) | 針對權限、完成和錯誤事件的桌面通知和聲音警報 | -| [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | 基於 opencode 上下文的 AI 支援的自動 Zellij 工作階段命名 | -| [opencode-skillful](https://github.com/zenobi-us/opencode-skillful) | 允許 opencode 代理透過技能發現和注入按需延遲載入提示 | -| [opencode-supermemory](https://github.com/supermemoryai/opencode-supermemory) | 使用超級記憶體跨工作階段持久記憶體 | -| [@plannotator/opencode](https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin) | 具有視覺註釋和私人/離線分享的互動式計畫審查 | -| [@openspoon/subtask2](https://github.com/spoons-and-mirrors/subtask2) | 將 opencode/指令擴展為具有精細流程控制的強大編排系統 | -| [opencode-scheduler](https://github.com/different-ai/opencode-scheduler) | 使用帶有 cron 語法的 launchd (Mac) 或 systemd (Linux) 安排重複作業 | -| [micode](https://github.com/vtemian/micode) | 結構化腦力激盪 → 計畫 → 實作具有會議連續性的工作流程 | -| [octto](https://github.com/vtemian/octto) | 用於透過多問題形式進行 AI 腦力激盪的互動式瀏覽器 UI | -| [opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents) | 具有非同步委託和上下文持久性的 Claude Code 風格背景代理 | -| [opencode-notify](https://github.com/kdcokenny/opencode-notify) | opencode 的原生作業系統通知 – 了解任務何時完成 | -| [opencode-workspace](https://github.com/kdcokenny/opencode-workspace) | 捆綁的多代理編排工具 – 16 個組件,一次安裝 | -| [opencode-worktree](https://github.com/kdcokenny/opencode-worktree) | opencode 的零摩擦 git 工作樹 | +| 名稱 | 說明 | +| --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| [opencode-daytona](https://github.com/jamesmurdza/daytona/blob/main/guides/typescript/opencode/README.md) | 在隔離的 Daytona 沙箱中自動執行 OpenCode 工作階段,支援 git 同步和即時預覽 | +| [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) | 自動注入 Helicone 工作階段標頭資訊,用於請求分組 | +| [opencode-type-inject](https://github.com/nick-vi/opencode-type-inject) | 透過搜尋工具自動將 TypeScript/Svelte 型別注入到檔案讀取中 | +| [opencode-openai-codex-auth](https://github.com/numman-ali/opencode-openai-codex-auth) | 使用您的 ChatGPT Plus/Pro 訂閱替代 API 額度 | +| [opencode-gemini-auth](https://github.com/jenslys/opencode-gemini-auth) | 使用您現有的 Gemini 方案替代 API 計費 | +| [opencode-antigravity-auth](https://github.com/NoeFabris/opencode-antigravity-auth) | 使用 Antigravity 的免費模型替代 API 計費 | +| [opencode-devcontainers](https://github.com/athal7/opencode-devcontainers) | 多分支開發容器隔離,支援淺層複製和自動分配連接埠 | +| [opencode-google-antigravity-auth](https://github.com/shekohex/opencode-google-antigravity-auth) | Google Antigravity OAuth 外掛程式,支援 Google 搜尋及更強健的 API 處理 | +| [opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) | 透過修剪過時的工具輸出來最佳化 Token 使用 | +| [opencode-websearch-cited](https://github.com/ghoulr/opencode-websearch-cited.git) | 為受支援的供應商新增原生網頁搜尋支援,採用 Google grounded 風格 | +| [opencode-pty](https://github.com/shekohex/opencode-pty.git) | 使 AI 代理能夠在 PTY 中執行背景處理程序,並向其傳送互動式輸入 | +| [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy) | 非互動式 shell 指令說明——防止依賴 TTY 的操作導致卡住 | +| [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | 使用 Wakatime 追蹤 OpenCode 的使用情況 | +| [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | 清理 LLM 生成的 Markdown 表格 | +| [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 透過 Morph Fast Apply API 和惰性編輯標記實現 10 倍更快的程式碼編輯 | +| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | 背景代理、預建置的 LSP/AST/MCP 工具、精選代理,相容 Claude Code | +| [opencode-notificator](https://github.com/panta82/opencode-notificator) | OpenCode 工作階段的桌面通知和聲音提醒 | +| [opencode-notifier](https://github.com/mohak34/opencode-notifier) | 針對權限請求、任務完成和錯誤事件的桌面通知與聲音提醒 | +| [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | 基於 OpenCode 上下文的 AI 驅動自動 Zellij 工作階段命名 | +| [opencode-skillful](https://github.com/zenobi-us/opencode-skillful) | 允許 OpenCode 代理透過技能發現和注入按需延遲載入提示詞 | +| [opencode-supermemory](https://github.com/supermemoryai/opencode-supermemory) | 使用 Supermemory 實現跨工作階段的持久記憶 | +| [@plannotator/opencode](https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin) | 支援視覺化標註和私有/離線分享的互動式計畫審查 | +| [@openspoon/subtask2](https://github.com/spoons-and-mirrors/subtask2) | 將 OpenCode /commands 擴展為具有精細流程控制的強大編排系統 | +| [opencode-scheduler](https://github.com/different-ai/opencode-scheduler) | 使用 cron 語法透過 launchd (Mac) 或 systemd (Linux) 排程週期性任務 | +| [micode](https://github.com/vtemian/micode) | 結構化的腦力激盪 → 計畫 → 實作工作流程,支援工作階段連續性 | +| [octto](https://github.com/vtemian/octto) | 用於 AI 腦力激盪的互動式瀏覽器 UI,支援多問題表單 | +| [opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents) | Claude Code 風格的背景代理,支援非同步委派和上下文持久化 | +| [opencode-notify](https://github.com/kdcokenny/opencode-notify) | OpenCode 的原生作業系統通知——隨時了解任務完成情況 | +| [opencode-workspace](https://github.com/kdcokenny/opencode-workspace) | 捆綁式多代理編排套件——16 個元件,一次安裝 | +| [opencode-worktree](https://github.com/kdcokenny/opencode-worktree) | OpenCode 的零摩擦 git worktree 管理 | --- ## 專案 -| 名稱 | 描述 | +| 名稱 | 說明 | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- | -| [kimaki](https://github.com/remorses/kimaki) | 用於控制 opencode 工作階段的 Discord 機器人,基於 SDK 構建 | -| [opencode.nvim](https://github.com/NickvanDyke/opencode.nvim) | Neovim 外掛,用於編輯器感知提示,基於 API 構建 | -| [portal](https://github.com/hosenur/portal) | 透過 Tailscale/VPN 實現 opencode 的行動優先 Web UI | -| [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | 用於構建 opencode 外掛的範本 | -| [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | Neovim opencode 前端 - 基於終端機的 AI 程式碼代理 | -| [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK 供應商,用於透過 @opencode-ai/sdk 使用 opencode | -| [OpenChamber](https://github.com/btriapitsyn/openchamber) | opencode 的 Web/桌面應用程式和 VS Code 擴充功能 | -| [OpenCode-Obsidian](https://github.com/mtymek/opencode-obsidian) | 在 Obsidian 的 UI 中嵌入 opencode 的 Obsidian 外掛 | -| [OpenWork](https://github.com/different-ai/openwork) | Claude Cowork 的開源替代方案,由 opencode 提供支援 | -| [ocx](https://github.com/kdcokenny/ocx) | opencode 擴充功能管理器具有可攜式、隔離的設定檔。 | -| [CodeNomad](https://github.com/NeuralNomadsAI/CodeNomad) | opencode 的桌面、Web、行動和遠端客戶端應用程式 | +| [kimaki](https://github.com/remorses/kimaki) | 用於控制 OpenCode 工作階段的 Discord 機器人,基於 SDK 建置 | +| [opencode.nvim](https://github.com/NickvanDyke/opencode.nvim) | Neovim 外掛程式,提供編輯器感知的提示詞,基於 API 建置 | +| [portal](https://github.com/hosenur/portal) | 透過 Tailscale/VPN 使用的行動優先 OpenCode Web UI | +| [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) | 用於建置 OpenCode 外掛程式的範本 | +| [opencode.nvim](https://github.com/sudo-tee/opencode.nvim) | OpenCode 的 Neovim 前端——基於終端機的 AI 編碼代理 | +| [ai-sdk-provider-opencode-sdk](https://github.com/ben-vargas/ai-sdk-provider-opencode-sdk) | Vercel AI SDK 供應商,用於透過 @opencode-ai/sdk 使用 OpenCode | +| [OpenChamber](https://github.com/btriapitsyn/openchamber) | OpenCode 的 Web / 桌面應用程式和 VS Code 擴充功能 | +| [OpenCode-Obsidian](https://github.com/mtymek/opencode-obsidian) | 將 OpenCode 嵌入 Obsidian UI 的 Obsidian 外掛程式 | +| [OpenWork](https://github.com/different-ai/openwork) | Claude Cowork 的開源替代方案,由 OpenCode 驅動 | +| [ocx](https://github.com/kdcokenny/ocx) | OpenCode 擴充功能管理器,支援可攜式的隔離設定 | +| [CodeNomad](https://github.com/NeuralNomadsAI/CodeNomad) | OpenCode 的桌面、Web、行動和遠端用戶端應用程式 | --- ## 代理 -| 名稱 | 描述 | -| ----------------------------------------------------------------- | ---------------------------------------- | -| [Agentic](https://github.com/Cluster444/agentic) | 用於結構化開發的模組化 AI 代理和指令 | -| [opencode-agents](https://github.com/darrenhinde/opencode-agents) | 用於增強工作流程的設定、提示、代理和外掛 | +| 名稱 | 說明 | +| ----------------------------------------------------------------- | ---------------------------------------------- | +| [Agentic](https://github.com/Cluster444/agentic) | 用於結構化開發的模組化 AI 代理和指令 | +| [opencode-agents](https://github.com/darrenhinde/opencode-agents) | 用於增強工作流程的設定、提示詞、代理和外掛程式 | diff --git a/packages/web/src/content/docs/zh-tw/enterprise.mdx b/packages/web/src/content/docs/zh-tw/enterprise.mdx index c4e5451b5ef0..386cae21c55c 100644 --- a/packages/web/src/content/docs/zh-tw/enterprise.mdx +++ b/packages/web/src/content/docs/zh-tw/enterprise.mdx @@ -1,47 +1,47 @@ --- title: 企業版 -description: 在您的組織中安全地使用 opencode。 +description: 在您的組織中安全地使用 OpenCode。 --- import config from "../../../../config.mjs" export const email = `mailto:${config.email}` -opencode Enterprise 適用於希望確保其程式碼和資料永遠不會離開其基礎架構的組織。它可以透過使用與 SSO 和內部 AI 閘道整合的集中式設定來實現此目的。 +OpenCode 企業版面向希望確保程式碼和資料始終留在自有基礎架構內的組織。它透過集中式設定與您的 SSO 和內部 AI 閘道整合來實現這一目標。 :::note -opencode 不儲存您的任何程式碼或上下文資料。 +OpenCode 不會儲存您的任何程式碼或上下文資料。 ::: -要開始使用 opencode Enterprise: +開始使用 OpenCode 企業版: -1. 與您的團隊進行內部試用。 -2. **聯絡我們**討論定價和實作選項。 +1. 在團隊內部進行試用。 +2. **聯絡我們**,討論定價和實作方案。 --- ## 試用 -opencode 是開源的,不儲存您的任何程式碼或上下文資料,因此您的開發人員只需 [開始使用](/docs/) 並進行試用。 +OpenCode 是開源的,不會儲存您的任何程式碼或上下文資料,因此您的開發人員可以直接[開始使用](/docs/)並進行試用。 --- ### 資料處理 -**opencode 不會儲存您的程式碼或上下文資料。** 所有處理都在本地進行或透過直接 API 呼叫您的 AI 供應商。 +**OpenCode 不會儲存您的程式碼或上下文資料。**所有處理均在本地完成,或透過直接 API 呼叫傳送至您的 AI 供應商。 -這意味著只要您使用您信任的供應商或內部 AI 閘道,您就可以安全使用 opencode。 +這意味著,只要您使用的是信任的供應商或內部 AI 閘道,就可以安全地使用 OpenCode。 -這裡唯一需要注意的是可選的 `/share` 功能。 +唯一需要注意的是可選的 `/share` 功能。 --- #### 分享對話 -如果使用者啟用 `/share` 功能,對話和與之關聯的資料將被發送到我們用於在 opencode.ai 上託管這些分享頁面的服務。 +如果使用者啟用了 `/share` 功能,對話及其關聯資料將被傳送到我們用於在 opencode.ai 上託管共享頁面的服務。 -資料當前透過我們的 CDN 邊緣網路提供服務,並快取在使用者附近的邊緣。 +資料目前透過我們 CDN 的邊緣網路提供服務,並快取在靠近使用者的邊緣節點上。 -我們建議您在試用時禁用此功能。 +我們建議您在試用期間停用此功能。 ```json title="opencode.json" { @@ -56,112 +56,110 @@ opencode 是開源的,不儲存您的任何程式碼或上下文資料,因 ### 程式碼所有權 -**您擁有 opencode 生成的所有程式碼。** 沒有授權限制或所有權聲明。 +**您擁有 OpenCode 生成的所有程式碼。**不存在任何授權限制或所有權聲明。 --- ## 定價 -我們對 opencode Enterprise 使用按席位計費模式。如果您有自己的 LLM 閘道,我們不會對使用的 Tokens 收取費用。有關定價和實作選項的更多詳細資訊,請**聯絡我們**。 +OpenCode 企業版採用按席位定價模型。如果您擁有自己的 LLM 閘道,我們不會對使用的 Token 收取費用。有關定價和實作方案的更多詳情,請**聯絡我們**。 --- ## 部署 -完成試用並準備好使用 opencode 後,請訪問: -您的組織,您可以**聯絡我們**進行討論 -定價和實作選項。 +完成試用並準備好在組織中使用 OpenCode 後,您可以**聯絡我們**,討論定價和實作方案。 --- -### 中央設定 +### 集中式設定 -我們可以將 opencode 設定為為您的整個組織使用單一的中央設定。 +我們可以為您的整個組織設定 OpenCode 的統一集中式設定。 -這種集中式設定可以與您的 SSO 供應商整合,並確保所有使用者僅存取您的內部 AI 閘道。 +該集中式設定可與您的 SSO 供應商整合,確保所有使用者僅存取您的內部 AI 閘道。 --- -### 單一登入整合 +### SSO 整合 -透過中央設定,opencode 可以與您組織的 SSO 供應商整合以進行身分驗證。 +透過集中式設定,OpenCode 可以與您組織的 SSO 供應商整合進行身分驗證。 -這使得 opencode 能夠透過現有的身分管理系統取得內部 AI 閘道的憑證。 +這使得 OpenCode 能夠透過您現有的身分管理系統取得內部 AI 閘道的憑證。 --- ### 內部 AI 閘道 -透過中央設定,opencode 還可以設定為僅使用您的內部 AI 閘道。 +透過集中式設定,OpenCode 還可以被設定為僅使用您的內部 AI 閘道。 -您還可以禁用所有其他 AI 供應商,確保所有請求都通過組織核准的基礎架構。 +您還可以停用所有其他 AI 供應商,確保所有請求都經過組織核准的基礎架構。 --- ### 自行託管 -雖然我們建議禁用分享頁面以確保您的資料永遠不會離開您的組織,我們還可以幫助您在您的基礎架構上自行託管它們。 +雖然我們建議停用共享頁面以確保您的資料始終不會離開組織,但我們也可以幫助您在自己的基礎架構上自行託管這些頁面。 -目前這已在我們的路線圖上。如果您有興趣,**讓我們知道**。 +此功能目前已列入我們的路線圖。如果您感興趣,請**告訴我們**。 --- ## 常見問題

-什麼是 opencode Enterprise? +什麼是 OpenCode 企業版? -opencode Enterprise 適用於希望確保其程式碼和資料永遠不會離開其基礎架構的組織。它可以透過使用與 SSO 和內部 AI 閘道整合的集中式設定來實現此目的。 +OpenCode 企業版面向希望確保程式碼和資料始終留在自有基礎架構內的組織。它透過集中式設定與您的 SSO 和內部 AI 閘道整合來實現這一目標。
-如何開始使用 opencode Enterprise? +如何開始使用 OpenCode 企業版? -只需與您的團隊進行內部試用即可。 opencode 預設情況下不儲存您的程式碼或上下文資料,因此可以輕鬆上手。 +只需在團隊內部開始試用即可。OpenCode 預設不會儲存您的程式碼或上下文資料,因此可以輕鬆上手。 -然後**聯絡我們**討論定價和實作選項。 +然後**聯絡我們**,討論定價和實作方案。
-企業定價如何運作? +企業版定價如何運作? -我們提供按席位企業定價。如果您有自己的 LLM 閘道,我們不會對使用的 Tokens 收取費用。如需了解更多詳情,請**聯絡我們**,獲取根據您組織的需求客製化的報價。 +我們提供按席位的企業版定價。如果您擁有自己的 LLM 閘道,我們不會對使用的 Token 收取費用。如需了解更多詳情,請**聯絡我們**,取得根據您組織需求訂製的報價。
-opencode Enterprise 保證我的資料安全嗎? +我的資料在 OpenCode 企業版中是否安全? -是的。 opencode 不儲存您的程式碼或上下文資料。所有處理都在本地進行或透過直接 API 呼叫您的 AI 供應商。透過中央設定和 SSO 整合,您的資料在組織的基礎架構中保持安全。 +是的。OpenCode 不會儲存您的程式碼或上下文資料。所有處理均在本地完成,或透過直接 API 呼叫傳送至您的 AI 供應商。透過集中式設定和 SSO 整合,您的資料將安全地保留在組織的基礎架構內。
-我們可以使用自己的私有 NPM Registry 嗎? +我們可以使用自己的私有 NPM 註冊表嗎? -opencode 透過 Bun 的原生 `.npmrc` 檔案支援來支援私有 npm Registry。如果您的組織使用私有 Registry,例如 JFrog Artifactory、Nexus 或類似的 Registry,請確保開發人員在執行 opencode 之前經過身分驗證。 +OpenCode 透過 Bun 原生的 `.npmrc` 檔案支援來支援私有 npm 註冊表。如果您的組織使用私有註冊表(例如 JFrog Artifactory、Nexus 或類似產品),請確保開發人員在執行 OpenCode 之前已完成身分驗證。 -要使用您的私有 Registry 設定身分驗證: +要設定私有註冊表的身分驗證: ```bash npm login --registry=https://your-company.jfrog.io/api/npm/npm-virtual/ ``` -這將建立帶有身分驗證詳細資訊的 `~/.npmrc`。 opencode 會自動讀取這個。 +這會建立包含身分驗證資訊的 `~/.npmrc` 檔案。OpenCode 會自動識別並使用它。 :::caution -在執行 opencode 之前,您必須登入私有 Registry。 +在執行 OpenCode 之前,您必須先登入私有註冊表。 ::: -或者,您可以手動設定 `.npmrc` 檔案: +或者,您也可以手動設定 `.npmrc` 檔案: ```bash title="~/.npmrc" registry=https://your-company.jfrog.io/api/npm/npm-virtual/ //your-company.jfrog.io/api/npm/npm-virtual/:_authToken=${NPM_AUTH_TOKEN} ``` -開發人員必須在執行 opencode 之前登入私有 Registry,以確保可以從企業 Registry 安裝套件。 +開發人員必須在執行 OpenCode 之前登入私有註冊表,以確保能夠從您的企業註冊表安裝套件。
diff --git a/packages/web/src/content/docs/zh-tw/formatters.mdx b/packages/web/src/content/docs/zh-tw/formatters.mdx index 45a7c6cd7189..bb462a66d3e4 100644 --- a/packages/web/src/content/docs/zh-tw/formatters.mdx +++ b/packages/web/src/content/docs/zh-tw/formatters.mdx @@ -1,63 +1,64 @@ --- -title: 格式化程式 -description: opencode 使用特定於語言的格式化程式。 +title: 格式化器 +description: OpenCode 使用特定語言的格式化器。 --- -使用特定於語言的格式化程式編寫或編輯檔案後,opencode 會自動格式化檔案。這可確保生成的程式碼遵循專案的程式碼風格。 +OpenCode 會在檔案寫入或編輯後,自動使用特定語言的格式化器對其進行格式化。這確保了生成的程式碼遵循您專案的程式碼風格。 --- -## 內建 - -opencode 附帶了多個適用於流行語言和框架的內建格式化程式。下面是格式化程式、支援的檔案副檔名以及所需的指令或設定選項的列表。 - -| 格式化程式 | 副檔名 | 要求 | -| -------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| gofmt | .go | `gofmt` 指令可用 | -| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` 指令可用 | -| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, 和 [更多](https://prettier.io/docs/en/index.html) | `prettier` 中有 `package.json` 相依套件 | -| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, 和 [更多](https://biomejs.dev/) | `biome.json(c)` 設定檔 | -| zig | .zig, .zon | `zig` 指令可用 | -| clang-format | .c, .cpp, .h, .hpp, .ino, 和 [更多](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` 設定檔 | -| ktlint | .kt, .kts | `ktlint` 指令可用 | -| ruff | .py, .pyi | `ruff` 指令可用並設定完成 | -| rustfmt | .rs | `rustfmt` 指令可用 | -| cargofmt | .rs | `cargo fmt` 指令可用 | -| uv | .py, .pyi | `uv` 指令可用 | -| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` 指令可用 | -| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` 指令可用 | -| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` 指令可用 | -| air | .R | `air` 指令可用 | -| dart | .dart | `dart` 指令可用 | -| dfmt | .d | `dfmt` 指令可用 | -| ocamlformat | .ml, .mli | `ocamlformat` 指令可用,且存在 `.ocamlformat` 設定檔 | -| terraform | .tf, .tfvars | `terraform` 指令可用 | -| gleam | .gleam | `gleam` 指令可用 | -| nixfmt | .nix | `nixfmt` 指令可用 | -| shfmt | .sh, .bash | `shfmt` 指令可用 | -| pint | .php | `laravel/pint` 中有 `composer.json` 相依套件 | -| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `oxfmt` 中有 `package.json` 相依套件且啟用[實驗環境變數旗標](/docs/cli/#experimental) | -| ormolu | .hs | `ormolu` 指令可用 | - -因此,如果您的專案的 `package.json` 中有 `prettier`,opencode 將自動使用它。 +## 內建格式化器 + +OpenCode 內建了多種適用於主流語言和框架的格式化器。下表列出了各格式化器、支援的副檔名以及所需的指令或設定選項。 + +| 格式化器 | 副檔名 | 要求 | +| -------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| air | .R | `air` 指令可用 | +| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml 及[更多](https://biomejs.dev/) | `biome.json(c)` 設定檔 | +| cargofmt | .rs | `cargo fmt` 指令可用 | +| clang-format | .c, .cpp, .h, .hpp, .ino 及[更多](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` 設定檔 | +| cljfmt | .clj, .cljs, .cljc, .edn | `cljfmt` 指令可用 | +| dart | .dart | `dart` 指令可用 | +| dfmt | .d | `dfmt` 指令可用 | +| gleam | .gleam | `gleam` 指令可用 | +| gofmt | .go | `gofmt` 指令可用 | +| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` 指令可用 | +| ktlint | .kt, .kts | `ktlint` 指令可用 | +| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` 指令可用 | +| nixfmt | .nix | `nixfmt` 指令可用 | +| ocamlformat | .ml, .mli | `ocamlformat` 指令可用且存在 `.ocamlformat` 設定檔 | +| ormolu | .hs | `ormolu` 指令可用 | +| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `package.json` 中有 `oxfmt` 相依套件,且設定了[實驗性環境變數旗標](/docs/cli/#experimental) | +| pint | .php | `composer.json` 中有 `laravel/pint` 相依套件 | +| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml 及[更多](https://prettier.io/docs/en/index.html) | `package.json` 中有 `prettier` 相依套件 | +| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` 指令可用 | +| ruff | .py, .pyi | `ruff` 指令可用且有相應設定 | +| rustfmt | .rs | `rustfmt` 指令可用 | +| shfmt | .sh, .bash | `shfmt` 指令可用 | +| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` 指令可用 | +| terraform | .tf, .tfvars | `terraform` 指令可用 | +| uv | .py, .pyi | `uv` 指令可用 | +| zig | .zig, .zon | `zig` 指令可用 | + +因此,如果您的專案 `package.json` 中包含 `prettier`,OpenCode 會自動使用它進行格式化。 --- -## 它是如何運作的 +## 工作原理 -當 opencode 寫入或編輯檔案時,它: +當 OpenCode 寫入或編輯檔案時,它會: -1. 根據所有啟用的格式化程式檢查檔案副檔名。 -2. 對檔案執行適當的格式化程式指令。 -3. 自動套用格式變更。 +1. 根據所有已啟用的格式化器檢查副檔名。 +2. 對檔案執行相應的格式化指令。 +3. 自動套用格式化變更。 -此過程在背景進行,確保無需任何手動步驟即可維護您的程式碼樣式。 +整個過程在背景完成,無需任何手動操作即可保持程式碼風格的一致性。 --- ## 設定 -您可以透過 opencode 設定中的 `formatter` 部分自定義格式化程式。 +您可以透過 OpenCode 設定中的 `formatter` 部分自訂格式化器。 ```json title="opencode.json" { @@ -66,22 +67,22 @@ opencode 附帶了多個適用於流行語言和框架的內建格式化程式 } ``` -每個格式化程式設定支援以下內容: +每個格式化器的設定支援以下屬性: -| 屬性 | 類型 | 描述 | -| ------------- | ------ | ---------------------------------- | -| `disabled` | 布林值 | 將其設定為 `true` 以禁用格式化程式 | -| `command` | 字串[] | 格式化執行的指令 | -| `environment` | 物件 | 執行格式化程式時要設定的環境變數 | -| `extensions` | 字串[] | 此格式化程式應處理的檔案副檔名 | +| 屬性 | 型別 | 說明 | +| ------------- | -------- | ---------------------------- | +| `disabled` | boolean | 設為 `true` 可停用該格式化器 | +| `command` | string[] | 執行格式化的指令 | +| `environment` | object | 執行格式化器時設定的環境變數 | +| `extensions` | string[] | 該格式化器處理的副檔名 | -讓我們看一些例子。 +下面來看一些範例。 --- -### 禁用格式化程式 +### 停用格式化器 -要全域禁用**所有**格式化程式,請將 `formatter` 設定為 `false`: +要全域停用**所有**格式化器,將 `formatter` 設為 `false`: ```json title="opencode.json" {3} { @@ -90,7 +91,7 @@ opencode 附帶了多個適用於流行語言和框架的內建格式化程式 } ``` -要禁用**特定**格式化程式,請將 `disabled` 設定為 `true`: +要停用**特定**格式化器,將 `disabled` 設為 `true`: ```json title="opencode.json" {5} { @@ -105,9 +106,9 @@ opencode 附帶了多個適用於流行語言和框架的內建格式化程式 --- -### 自定義格式化程式 +### 自訂格式化器 -您可以覆寫內建格式化程式或透過指定指令、環境變數和檔案副檔名新增新格式化程式: +您可以透過指定指令、環境變數和副檔名來覆寫內建格式化器或新增新的格式化器: ```json title="opencode.json" {4-14} { @@ -128,4 +129,4 @@ opencode 附帶了多個適用於流行語言和框架的內建格式化程式 } ``` -指令中的 **`$FILE` 預留位置** 將替換為正在格式化的檔案的路徑。 +指令中的 **`$FILE` 佔位符**會被替換為待格式化檔案的路徑。 diff --git a/packages/web/src/content/docs/zh-tw/github.mdx b/packages/web/src/content/docs/zh-tw/github.mdx index 32d3f005e55b..f12de2907755 100644 --- a/packages/web/src/content/docs/zh-tw/github.mdx +++ b/packages/web/src/content/docs/zh-tw/github.mdx @@ -1,43 +1,43 @@ --- title: GitHub -description: 在 GitHub Issues 和拉取請求中使用 opencode。 +description: 在 GitHub Issue 和 Pull Request 中使用 OpenCode。 --- -opencode 與您的 GitHub 工作流程整合。在評論中提及 `/opencode` 或 `/oc`,opencode 將在您的 GitHub Actions Runner 中執行任務。 +OpenCode 可以與您的 GitHub 工作流程整合。在評論中提及 `/opencode` 或 `/oc`,OpenCode 就會在您的 GitHub Actions Runner 中執行任務。 --- -## 功能 +## 功能特性 -- **分類問題**:要求 opencode 調查問題並向您解釋。 -- **修復和實作**:要求 opencode 修復問題或實作功能。它將在一個新分支中工作並提交包含所有變更的 PR。 -- **安全**:opencode 在 GitHub 的 Runner 中執行。 +- **Issue 分類**:讓 OpenCode 調查某個 Issue 並為您做出解釋。 +- **修復與實作**:讓 OpenCode 修復 Issue 或實作某個功能。它會在新分支中工作,並提交包含所有變更的 PR。 +- **安全可靠**:OpenCode 在您自己的 GitHub Runner 中執行。 --- ## 安裝 -在 GitHub 儲存庫中的專案中執行以下指令: +在一個位於 GitHub 儲存庫中的專案裡執行以下指令: ```bash opencode github install ``` -這將引導您完成安裝 GitHub 應用程式、建立工作流程和設定 Secrets。 +該指令會引導您完成 GitHub App 的安裝、工作流程的建立以及密鑰的設定。 --- ### 手動設定 -或者您可以手動設定。 +您也可以手動進行設定。 -1. **安裝 GitHub 應用程式** +1. **安裝 GitHub App** - 前往 [**github.com/apps/opencode-agent**](https://github.com/apps/opencode-agent)。確保它已安裝在目標儲存庫上。 + 前往 [**github.com/apps/opencode-agent**](https://github.com/apps/opencode-agent),確保已在目標儲存庫中安裝該應用程式。 2. **新增工作流程** - 將以下工作流程檔案新增到儲存庫中的 `.github/workflows/opencode.yml` 中。確保在 `env` 中設定適當的 `model` 和所需的 API 金鑰。 + 將以下工作流程檔案新增到儲存庫的 `.github/workflows/opencode.yml` 中。請確保在 `env` 中設定合適的 `model` 及所需的 API 金鑰。 ```yml title=".github/workflows/opencode.yml" {24,26} name: opencode @@ -73,21 +73,21 @@ opencode github install # github_token: xxxx ``` -3. **設定 Secrets** +3. **將 API 金鑰儲存到 Secrets 中** - 在您的組織或專案的 **Settings** 中,展開左側的 **Secrets and variables**,然後選擇 **Actions**。並新增所需的 API 金鑰。 + 在您的組織或專案的 **Settings** 中,展開左側的 **Secrets and variables**,然後選擇 **Actions**,新增所需的 API 金鑰。 --- ## 設定 -- `model`:與 opencode 一起使用的模型。採用 `provider/model` 格式。這是**必需的**。 -- `agent`: 使用的代理。必須是主要代理。如果未找到,則從設定退回到 `default_agent` 或 `"build"`。 -- `share`:是否分享 opencode 工作階段。對於公共儲存庫,預設為 **true**。 -- `prompt`:可選的自定義提示以覆寫預設行為。使用它來自定義 opencode 處理請求的方式。 -- `token`:可選的 GitHub 存取權杖,用於執行建立評論、提交變更和打開拉取請求等操作。預設情況下,opencode 使用來自 opencode GitHub 應用程式的安裝存取權杖,因此提交、評論和拉取請求顯示為來自應用程式。 +- `model`:OpenCode 使用的模型,格式為 `provider/model`。此項為**必填**。 +- `agent`:要使用的代理,必須是主代理。如果未找到,則回退到設定中的 `default_agent`,若仍未找到則使用 `"build"`。 +- `share`:是否共享 OpenCode 工作階段。對於公開儲存庫,預設為 **true**。 +- `prompt`:可選的自訂提示詞,用於覆寫預設行為。可透過此項自訂 OpenCode 處理請求的方式。 +- `token`:可選的 GitHub 存取權杖,用於執行建立評論、提交變更和建立 Pull Request 等操作。預設情況下,OpenCode 使用 OpenCode GitHub App 的安裝存取權杖,因此提交、評論和 Pull Request 會顯示為來自該應用程式。 - 或者,您可以使用 GitHub Action Runner 的 [內建 `GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token),而無需安裝 opencode GitHub 應用程式。只需確保在您的工作流程中授予所需的權限: + 您也可以使用 GitHub Action Runner 內建的 [`GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token),而無需安裝 OpenCode GitHub App。只需確保在工作流程中授予所需的權限: ```yaml permissions: @@ -97,26 +97,26 @@ opencode github install issues: write ``` - **注意**:使用 `GITHUB_TOKEN` 時,opencode 執行的操作(如提交和評論)將不會觸發其他工作流程。 + 如果您願意,也可以使用[個人存取權杖](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)(PAT)。 --- ## 支援的事件 -opencode 可以由以下 GitHub 事件觸發: +OpenCode 可以由以下 GitHub 事件觸發: -| 事件類型 | 觸發條件 | 詳情 | -| ----------------------------- | ------------------------------ | --------------------------------------------------------------------------------------- | -| `issue_comment` | 對問題或 PR 發表評論 | 在評論中提及 `/opencode` 或 `/oc`。 opencode 讀取上下文並可以建立分支、打開 PR 或回覆。 | -| `pull_request_review_comment` | 對 PR 中的特定程式碼行進行評論 | 在檢查程式碼時提及 `/opencode` 或 `/oc`。 opencode 接收檔案路徑、行號和 diff 上下文。 | -| `issues` | 問題已打開或已編輯 | 建立或修改問題時自動觸發 opencode。需要 `prompt` 輸入。 | -| `pull_request` | PR 已開啟或已更新 | 當 PR 被開啟、同步或重新開啟時自動觸發 opencode。對於自動評論很有用。 | -| `schedule` | 基於 Cron 的排程 | 按排程執行 opencode。需要 `prompt` 輸入。輸出進入日誌和 PR(沒有可評論的問題)。 | -| `workflow_dispatch` | 從 GitHub UI 手動觸發 | 透過「Actions」標籤按需觸發 opencode。需要 `prompt` 輸入。輸出進入日誌和 PR。 | +| 事件類型 | 觸發方式 | 詳情 | +| ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------- | +| `issue_comment` | 在 Issue 或 PR 上發表評論 | 在評論中提及 `/opencode` 或 `/oc`。OpenCode 會讀取上下文,並可建立分支、提交 PR 或回覆。 | +| `pull_request_review_comment` | 在 PR 中對特定程式碼行發表評論 | 在程式碼審查時提及 `/opencode` 或 `/oc`。OpenCode 會接收檔案路徑、行號和 diff 上下文。 | +| `issues` | Issue 被建立或編輯 | 在 Issue 建立或修改時自動觸發 OpenCode。需要提供 `prompt` 輸入。 | +| `pull_request` | PR 被建立或更新 | 在 PR 被開啟、同步或重新開啟時自動觸發 OpenCode。適用於自動化審查情境。 | +| `schedule` | 基於 Cron 的定時任務 | 按排程執行 OpenCode。需要提供 `prompt` 輸入。輸出會寫入日誌和 PR(沒有 Issue 可供評論)。 | +| `workflow_dispatch` | 從 GitHub UI 手動觸發 | 透過 Actions 分頁按需觸發 OpenCode。需要提供 `prompt` 輸入。輸出會寫入日誌和 PR。 | -### 排程範例 +### 定時任務範例 -按排程執行 opencode 以執行自動化任務: +按排程執行 OpenCode 以執行自動化任務: ```yaml title=".github/workflows/opencode-scheduled.yml" name: Scheduled OpenCode Task @@ -150,13 +150,13 @@ jobs: If you find issues worth addressing, open an issue to track them. ``` -對於排程事件,`prompt` 輸入是**必需的**,因為沒有註釋可從中提取指令。排程工作流程在沒有使用者上下文的情況下執行以進行權限檢查,因此如果您希望 opencode 建立分支或 PR,工作流程必須授予 `contents: write` 和 `pull-requests: write`。 +對於定時事件,`prompt` 輸入為**必填**,因為沒有評論可供提取指令。定時工作流程在執行時沒有使用者上下文來進行權限檢查,因此如果您希望 OpenCode 建立分支或 PR,工作流程必須授予 `contents: write` 和 `pull-requests: write` 權限。 --- -### 拉取請求範例 +### Pull Request 範例 -打開或更新 PR 時自動審閱: +在 PR 被建立或更新時自動進行審查: ```yaml title=".github/workflows/opencode-review.yml" name: opencode-review @@ -191,13 +191,13 @@ jobs: - Suggest improvements ``` -對於 `pull_request` 事件,如果未提供 `prompt`,opencode 將預設審閱拉取請求。 +對於 `pull_request` 事件,如果未提供 `prompt`,OpenCode 將預設對該 Pull Request 進行審查。 --- -### 問題分類範例 +### Issue 分類範例 -自動分類新問題。此範例過濾超過 30 天的帳號以減少垃圾訊息: +自動分類新建的 Issue。以下範例會過濾掉註冊不滿 30 天的帳戶以減少垃圾訊息: ```yaml title=".github/workflows/opencode-triage.yml" name: Issue Triage @@ -246,13 +246,13 @@ jobs: Otherwise, do not comment. ``` -對於 `issues` 事件,`prompt` 輸入是**必需的**,因為沒有評論可從中提取指令。 +對於 `issues` 事件,`prompt` 輸入為**必填**,因為沒有評論可供提取指令。 --- -## 自定義提示 +## 自訂提示詞 -覆寫預設提示,為您的工作流程自定義 opencode 的行為。 +覆寫預設提示詞,以便為您的工作流程自訂 OpenCode 的行為。 ```yaml title=".github/workflows/opencode.yml" - uses: anomalyco/opencode/github@latest @@ -265,57 +265,57 @@ jobs: - Suggest improvements ``` -這對於執行與您的專案相關的特定審閱標準、編碼標準或重點領域非常有用。 +這對於在專案中實施特定的審查標準、編碼規範或關注重點非常有用。 --- ## 範例 -以下是如何在 GitHub 中使用 opencode 的一些範例。 +以下是在 GitHub 中使用 OpenCode 的一些範例。 -- **解釋一個問題** +- **解釋 Issue** - 在 GitHub Issue 中添加此評論。 + 在 GitHub Issue 中新增以下評論: ``` /opencode explain this issue ``` - opencode 將閱讀整個討論串,包括所有評論,並回覆並提供清晰的解釋。 + OpenCode 會閱讀整個討論串(包括所有評論),並回覆一份清晰的解釋。 -- **解決問題** +- **修復 Issue** - 在 GitHub Issue 中,說: + 在 GitHub Issue 中輸入: ``` /opencode fix this ``` - opencode 將建立一個新分支,實作變更,並使用變更打開 PR。 + OpenCode 會建立一個新分支,實作變更,並提交一個包含所有修改的 PR。 -- **審閱 PR 並進行變更** +- **審查 PR 並進行修改** - 在 GitHub PR 上留下以下評論。 + 在 GitHub PR 上留下以下評論: ``` Delete the attachment from S3 when the note is removed /oc ``` - opencode 將實作請求的變更並將其推送到分支。 + OpenCode 會實作所請求的變更並將其提交到同一個 PR 中。 -- **查看特定程式碼行** +- **審查特定程式碼行** - 直接在 PR 的「Files」標籤中的程式碼行上留下評論。 opencode 自動檢測檔案、行號和 diff 上下文以提供精確的回應。 + 在 PR 的「Files」分頁中直接對程式碼行留下評論。OpenCode 會自動偵測檔案、行號和 diff 上下文,從而提供精準的回應。 ``` [Comment on specific lines in Files tab] /oc add error handling here ``` - opencode 將查看: - - 正在審閱的確切檔案 - - 具體程式碼行 + 當您對特定程式碼行發表評論時,OpenCode 會接收到: + - 正在審查的具體檔案 + - 特定的程式碼行 - 周圍的 diff 上下文 - 行號資訊 - 這允許更有針對性的請求,而無需手動指定檔案路徑或行號。 + 這樣您就可以提出更有針對性的請求,而無需手動指定檔案路徑或行號。 diff --git a/packages/web/src/content/docs/zh-tw/gitlab.mdx b/packages/web/src/content/docs/zh-tw/gitlab.mdx index a96618fd1c04..d1ebccfebe34 100644 --- a/packages/web/src/content/docs/zh-tw/gitlab.mdx +++ b/packages/web/src/content/docs/zh-tw/gitlab.mdx @@ -1,33 +1,33 @@ --- title: GitLab -description: 在 GitLab 問題和合併請求中使用 opencode。 +description: 在 GitLab Issue 和合併請求中使用 OpenCode。 --- -opencode 透過 GitLab CI/CD 管線或與 GitLab Duo 與您的 GitLab 工作流程整合。 +OpenCode 透過 GitLab CI/CD 管線或 GitLab Duo 與您的 GitLab 工作流程整合。 -在這兩種情況下,opencode 都將在您的 GitLab Runner 上執行。 +在這兩種情況下,OpenCode 都將在您的 GitLab Runner 上執行。 --- -## GitLab +## GitLab CI -opencode 在常規 GitLab 管線中工作。您可以將其構建為管線作為 [CI 組件](https://docs.gitlab.com/ee/ci/components/) +OpenCode 可以在常規的 GitLab 管線中執行。您可以將其作為 [CI 元件](https://docs.gitlab.com/ee/ci/components/) 整合到管線中。 -在這裡,我們使用社群建立的 opencode CI/CD 組件 — [nagyv/gitlab-opencode](https://gitlab.com/nagyv/gitlab-opencode)。 +這裡我們使用的是社群建立的 OpenCode CI/CD 元件 — [nagyv/gitlab-opencode](https://gitlab.com/nagyv/gitlab-opencode)。 --- -### 功能 +### 功能特性 -- **每個作業使用自定義設定**:使用自定義設定目錄設定 opencode,例如 `./config/#custom-directory` 以啟用或禁用每個 opencode 呼叫的功能。 -- **無狀態**:opencode 的狀態(訊息歷史記錄等)儲存在合併請求評論線程中,使其完全無狀態。 -- **靈活**:CI 組件支援多種輸入來自定義其行為 +- **按任務自訂設定**:使用自訂設定目錄來設定 OpenCode,例如 `./config/#custom-directory`,以便為每次 OpenCode 呼叫啟用或停用特定功能。 +- **最小化設定**:CI 元件會在背景完成 OpenCode 的設定,您只需建立 OpenCode 設定和初始提示詞即可。 +- **靈活可自訂**:CI 元件支援多種輸入參數來自訂其行為。 --- ### 設定 -1. 將 opencode 身分驗證 JSON 作為檔案類型 CI 環境變數儲存在 **Settings** > **CI/CD** > **Variables** 下。確保將它們標記為「Masked and hidden」。 +1. 將您的 OpenCode 身分驗證 JSON 作為檔案類型的 CI 環境變數儲存在 **Settings** > **CI/CD** > **Variables** 下。請確保將其標記為「Masked and hidden」。 2. 將以下內容新增到您的 `.gitlab-ci.yml` 檔案中。 ```yaml title=".gitlab-ci.yml" @@ -40,40 +40,39 @@ opencode 在常規 GitLab 管線中工作。您可以將其構建為管線作為 message: "Your prompt here" ``` -有關此組件的更多輸入和使用案例[查看文件](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode)。 +有關更多輸入參數和使用情境,請[查看該元件的文件](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode)。 --- ## GitLab Duo -opencode 與您的 GitLab 工作流程整合。 -在評論中提及 `@opencode`,opencode 將在您的 GitLab CI 管線中執行任務。 +OpenCode 與您的 GitLab 工作流程整合。 +在評論中提及 `@opencode`,OpenCode 將在您的 GitLab CI 管線中執行任務。 --- -### 功能 +### 功能特性 -- **分類問題**:要求 opencode 調查問題並向您解釋。 -- **修復和實作**:要求 opencode 修復問題或實作功能。 - 它將建立一個新分支並提出包含變更的合併請求。 -- **安全**:opencode 在您的 GitLab Runner 上執行。 +- **Issue 分類**:讓 OpenCode 調查某個 Issue 並為您解釋。 +- **修復與實作**:讓 OpenCode 修復 Issue 或實作某個功能。它會建立一個新分支,並提交包含變更的合併請求。 +- **安全可靠**:OpenCode 在您的 GitLab Runner 上執行。 --- ### 設定 -opencode 在您的 GitLab CI/CD 管線中執行,您需要進行以下設定: +OpenCode 在您的 GitLab CI/CD 管線中執行,以下是設定所需的步驟: :::tip -查看 [**GitLab 文件**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) 以獲取最新說明。 +請查看 [**GitLab 文件**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) 取得最新說明。 ::: 1. 設定您的 GitLab 環境 2. 設定 CI/CD -3. 取得 AI 模型供應商 API 金鑰 -4. 建立服務帳號 +3. 取得 AI 模型供應商的 API 金鑰 +4. 建立服務帳戶 5. 設定 CI/CD 變數 -6. 建立一個流程設定檔,這是一個範例: +6. 建立流程設定檔,以下是一個範例:
@@ -152,44 +151,44 @@ opencode 在您的 GitLab CI/CD 管線中執行,您需要進行以下設定:
-詳細說明可以參考 [GitLab CLI 代理文件](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/)。 +詳細說明請參考 [GitLab CLI agents 文件](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/)。 --- ### 範例 -以下是如何在 GitLab 中使用 opencode 的一些範例。 +以下是在 GitLab 中使用 OpenCode 的一些範例。 :::tip -您可以設定使用與 `@opencode` 不同的觸發短語。 +您可以設定使用不同於 `@opencode` 的觸發詞。 ::: -- **解釋一個問題** +- **解釋 Issue** - 在 GitLab 問題中添加此評論。 + 在 GitLab Issue 中新增以下評論。 ``` @opencode explain this issue ``` - opencode 將閱讀該問題並回覆並提供清晰的解釋。 + OpenCode 會閱讀該 Issue 並回覆清晰的解釋。 -- **解決問題** +- **修復 Issue** - 在 GitLab 問題中,說: + 在 GitLab Issue 中輸入: ``` @opencode fix this ``` - opencode 將建立一個新分支,實作變更,並打開包含變更的合併請求。 + OpenCode 會建立一個新分支,實作變更,並提交包含變更的合併請求。 -- **審閱合併請求** +- **審查合併請求** - 對 GitLab 合併請求留下以下評論。 + 在 GitLab 合併請求中留下以下評論。 ``` @opencode review this merge request ``` - opencode 將審閱合併請求並提供回饋。 + OpenCode 會審查合併請求並提供回饋。 diff --git a/packages/web/src/content/docs/zh-tw/ide.mdx b/packages/web/src/content/docs/zh-tw/ide.mdx index 11275318f7fa..eee3e8c60902 100644 --- a/packages/web/src/content/docs/zh-tw/ide.mdx +++ b/packages/web/src/content/docs/zh-tw/ide.mdx @@ -1,48 +1,48 @@ --- title: IDE -description: VS Code、Cursor 和其他 IDE 的 OpenCode 擴充功能 +description: 適用於 VS Code、Cursor 及其他 IDE 的 OpenCode 擴充功能 --- -OpenCode 與 VS Code、Cursor 或任何支援終端機的 IDE 整合。只需在終端機中執行 `opencode` 即可開始。 +OpenCode 可與 VS Code、Cursor 或任何支援終端機的 IDE 整合。只需在終端機中執行 `opencode` 即可開始使用。 --- ## 用法 -- **快速啟動**:使用 `Cmd+Esc` (Mac) 或 `Ctrl+Esc` (Windows/Linux) 在分割終端機視圖中打開 OpenCode,或者聚焦現有終端機工作階段(如果已有終端機工作階段正在執行)。 -- **新工作階段**:使用 `Cmd+Shift+Esc` (Mac) 或 `Ctrl+Shift+Esc` (Windows/Linux) 啟動新的 OpenCode 終端機工作階段,即使該工作階段已打開。您還可以單擊 UI 中的 OpenCode 按鈕。 -- **上下文感知**:自動與 OpenCode 分享您當前的選擇或分頁。 -- **檔案引用快速鍵**:使用 `Cmd+Option+K` (Mac) 或 `Alt+Ctrl+K` (Linux/Windows) 插入檔案引用。例如,`@File#L37-42`。 +- **快速啟動**:使用 `Cmd+Esc`(Mac)或 `Ctrl+Esc`(Windows/Linux)在分割終端機視圖中開啟 OpenCode,如果已有終端機工作階段正在執行,則會自動聚焦到該工作階段。 +- **新建工作階段**:使用 `Cmd+Shift+Esc`(Mac)或 `Ctrl+Shift+Esc`(Windows/Linux)啟動新的 OpenCode 終端機工作階段,即使已有工作階段在執行也會新建。您也可以點選介面中的 OpenCode 按鈕。 +- **上下文感知**:自動將當前選取內容或分頁共享給 OpenCode。 +- **檔案參照快捷鍵**:使用 `Cmd+Option+K`(Mac)或 `Alt+Ctrl+K`(Linux/Windows)插入檔案參照。例如 `@File#L37-42`。 --- ## 安裝 -要在 VS Code 和 Cursor、Windsurf、VSCodium 等流行 Fork 上安裝 OpenCode: +在 VS Code 及其常見分支(如 Cursor、Windsurf、VSCodium)上安裝 OpenCode: -1. 打開 VS Code -2. 打開整合終端機 -3. 執行 `opencode` - 擴充功能會自動安裝 +1. 開啟 VS Code +2. 開啟整合終端機 +3. 執行 `opencode`——擴充功能將自動安裝 -另一方面,如果您想在從 TUI 執行 `/editor` 或 `/export` 時使用自己的 IDE,則需要設定 `export EDITOR="code --wait"`。 [了解更多](/docs/tui/#editor-setup)。 +如果您希望在 TUI 中執行 `/editor` 或 `/export` 時使用自己的 IDE,需要設定 `export EDITOR="code --wait"`。[了解更多](/docs/tui/#editor-setup)。 --- ### 手動安裝 -在擴充功能市集中搜尋 **OpenCode**,然後單擊 **安裝**。 +在擴充功能商店中搜尋 **OpenCode**,然後點選 **Install**。 --- ### 疑難排解 -如果擴充功能無法自動安裝: +如果擴充功能未能自動安裝: -- 確保您在整合終端機中執行 `opencode`。 -- 確認您的 IDE 的 CLI 已安裝: - - 對於 VS Code:`code` 指令 - - 對於 Cursor:`cursor` 指令 - - 對於 Windsurf:`windsurf` 指令 - - 對於 VSCodium:`codium` 指令 - - 如果沒有,請執行 `Cmd+Shift+P` (Mac) 或 `Ctrl+Shift+P` (Windows/Linux) 並搜尋「Shell Command: Install 'code' command in PATH」(或適用於您的 IDE 的等效指令) -- 確保 VS Code 有權安裝擴充功能 +- 確保您是在整合終端機中執行的 `opencode`。 +- 確認您的 IDE 對應的 CLI 指令已安裝: + - VS Code:`code` 指令 + - Cursor:`cursor` 指令 + - Windsurf:`windsurf` 指令 + - VSCodium:`codium` 指令 + - 如果未安裝,請按 `Cmd+Shift+P`(Mac)或 `Ctrl+Shift+P`(Windows/Linux),搜尋「Shell Command: Install 'code' command in PATH」(或您的 IDE 對應的指令) +- 確保 VS Code 有權限安裝擴充功能 diff --git a/packages/web/src/content/docs/zh-tw/index.mdx b/packages/web/src/content/docs/zh-tw/index.mdx index 5de780503cf5..89649c3dae1a 100644 --- a/packages/web/src/content/docs/zh-tw/index.mdx +++ b/packages/web/src/content/docs/zh-tw/index.mdx @@ -7,25 +7,25 @@ import { Tabs, TabItem } from "@astrojs/starlight/components" import config from "../../../../config.mjs" export const console = config.console -[**OpenCode**](/) 是一個開源 AI 程式碼代理。它可用作基於終端機的介面、桌面應用程式或 IDE 擴充功能。 +[**OpenCode**](/) 是一個開源的 AI 編碼代理。它提供終端機介面、桌面應用程式和 IDE 擴充功能等多種使用方式。 -![具有 OpenCode 主題的 OpenCode TUI](../../../assets/lander/screenshot.png) +![使用 OpenCode 主題的 OpenCode TUI](../../../assets/lander/screenshot.png) 讓我們開始吧。 --- -#### 先決條件 +#### 前提條件 要在終端機中使用 OpenCode,您需要: -1. 現代終端機模擬器,例如: +1. 一款現代終端機模擬器,例如: - [WezTerm](https://wezterm.org),跨平台 - [Alacritty](https://alacritty.org),跨平台 - [Ghostty](https://ghostty.org),Linux 和 macOS - [Kitty](https://sw.kovidgoyal.net/kitty/),Linux 和 macOS -2. 您想要使用的 LLM 供應商的 API 金鑰。 +2. 您想使用的 LLM 供應商的 API 金鑰。 --- @@ -37,7 +37,7 @@ export const console = config.console curl -fsSL https://opencode.ai/install | bash ``` -您也可以使用以下指令安裝: +您也可以使用以下方式安裝: - **使用 Node.js** @@ -79,9 +79,9 @@ curl -fsSL https://opencode.ai/install | bash brew install anomalyco/tap/opencode ``` - > 我們建議使用 OpenCode tap 來獲取最新版本。官方 `brew install opencode` formula 由 Homebrew 團隊維護,更新頻率較低。 + > 我們推薦使用 OpenCode tap 以取得最新版本。官方的 `brew install opencode` formula 由 Homebrew 團隊維護,更新頻率較低。 -- **在 Arch Linux 上使用 Paru** +- **在 Arch Linux 上安裝** ```bash sudo pacman -S opencode # Arch Linux (Stable) @@ -90,8 +90,8 @@ curl -fsSL https://opencode.ai/install | bash #### Windows -:::tip[建議:使用 WSL] -為了在 Windows 上獲得最佳體驗,我們建議使用[適用於 Linux 的 Windows 子系統 (WSL)](/docs/windows-wsl)。它提供了更好的效能並與 OpenCode 的功能完全相容。 +:::tip[推薦:使用 WSL] +為了在 Windows 上獲得最佳體驗,我們推薦使用 [Windows Subsystem for Linux (WSL)](/docs/windows-wsl)。它提供更好的效能,並完全相容 OpenCode 的所有功能。 ::: - **使用 Chocolatey** @@ -124,18 +124,17 @@ curl -fsSL https://opencode.ai/install | bash docker run -it --rm ghcr.io/anomalyco/opencode ``` -目前正在支援使用 Bun 在 Windows 上安裝 OpenCode。 +在 Windows 上透過 Bun 安裝 OpenCode 的支援目前正在開發中。 -您還可以從 [Releases](https://github.com/anomalyco/opencode/releases) 獲取二進位檔案。 +您也可以從 [Releases](https://github.com/anomalyco/opencode/releases) 頁面直接下載二進位檔案。 --- ## 設定 -借助 OpenCode,您可以透過設定 API 金鑰來使用任何 LLM 供應商。 +透過 OpenCode,您可以設定 API 金鑰來使用任意 LLM 供應商。 -如果您不熟悉使用 LLM 供應商,我們建議使用 [OpenCode Zen](/docs/zen)。 -這是經過 OpenCode 團隊測試和驗證的精選模型列表。 +如果您剛開始接觸 LLM 供應商,我們推薦使用 [OpenCode Zen](/docs/zen)。這是一組經過 OpenCode 團隊測試和驗證的精選模型。 1. 在 TUI 中執行 `/connect` 指令,選擇 opencode,然後前往 [opencode.ai/auth](https://opencode.ai/auth)。 @@ -143,7 +142,7 @@ curl -fsSL https://opencode.ai/install | bash /connect ``` -2. 登入,新增您的帳單詳細資訊,然後複製您的 API 金鑰。 +2. 登入並新增帳單資訊,然後複製您的 API 金鑰。 3. 貼上您的 API 金鑰。 @@ -154,79 +153,79 @@ curl -fsSL https://opencode.ai/install | bash └ enter ``` -或者,您可以選擇其他供應商之一。[了解更多](/docs/providers#directory)。 +您也可以選擇其他供應商。[了解更多](/docs/providers#directory)。 --- ## 初始化 -現在您已經設定了供應商,您可以導航到一個您想繼續工作的專案。 +設定好供應商後,導覽到您想要處理的專案目錄。 ```bash cd /path/to/project ``` -並執行 OpenCode。 +然後執行 OpenCode。 ```bash opencode ``` -接下來,透過執行以下指令來初始化專案的 OpenCode。 +接下來,執行以下指令為專案初始化 OpenCode。 ```bash frame="none" /init ``` -這將使 OpenCode 分析您的專案並在專案根目錄下建立 `AGENTS.md` 檔案。 +OpenCode 會分析您的專案並在專案根目錄建立一個 `AGENTS.md` 檔案。 :::tip 您應該將專案的 `AGENTS.md` 檔案提交到 Git。 ::: -這有助於 OpenCode 理解專案結構和使用的編碼模式。 +這有助於 OpenCode 理解專案結構和編碼規範。 --- -## 用法 +## 使用 -您現在已準備好使用 OpenCode 來處理您的專案。請隨意詢問任何事物! +現在您已經準備好使用 OpenCode 來處理專案了,儘管提問吧! -如果您不熟悉使用 AI 程式碼代理,以下是一些可能會有所幫助的範例。 +如果您是第一次使用 AI 編碼代理,以下範例可能會對您有所幫助。 --- -### 提出問題 +### 提問 -您可以用 OpenCode 向您解釋程式碼庫。 +您可以讓 OpenCode 為您講解程式碼庫。 :::tip -使用`@`鍵模糊搜尋專案中的檔案。 +使用 `@` 鍵可以模糊搜尋專案中的檔案。 ::: ```txt frame="none" "@packages/functions/src/api/index.ts" How is authentication handled in @packages/functions/src/api/index.ts ``` -如果您沒有處理程式碼庫的一部分,這會很有幫助。 +當您遇到不熟悉的程式碼時,這個功能非常有用。 --- ### 新增功能 -您可以用 OpenCode 向您的專案新增新功能。不過我們首先建議要求它制定一個計畫。 +您可以讓 OpenCode 為專案新增新功能。不過我們建議先讓它制定一個計畫。 1. **制定計畫** - OpenCode 有一個*計畫模式*,該模式禁用其進行變更,而是建議*如何*實作該功能。 + OpenCode 有一個*計畫模式*,該模式下它不會進行任何修改,而是建議*如何*實作該功能。 - 使用 **Tab** 鍵切換到它。您會在右下角看到一個指示符。 + 使用 **Tab** 鍵切換到計畫模式。您會在右下角看到模式指示器。 ```bash frame="none" title="Switch to Plan mode" ``` - 現在讓我們描述一下我們想要它做什麼。 + 接下來描述您希望它做什麼。 ```txt frame="none" When a user deletes a note, we'd like to flag it as deleted in the database. @@ -234,15 +233,15 @@ How is authentication handled in @packages/functions/src/api/index.ts From this screen, the user can undelete a note or permanently delete it. ``` - 您需要為 OpenCode 提供足夠的詳細資訊以了解您想要的內容。這有幫助,就像與團隊中的初級開發人員交談一樣與它交談。 + 您需要提供足夠的細節,讓 OpenCode 理解您的需求。可以把它當作團隊中的一名初級開發者來溝通。 :::tip - 為 OpenCode 提供大量上下文和範例,以幫助其理解您的想法。 + 為 OpenCode 提供充足的上下文和範例,幫助它理解您的需求。 ::: 2. **迭代計畫** - 一旦它為您提供了計畫,您可以提供回饋或新增更多詳細資訊。 + 當它給出計畫後,您可以提供回饋或補充更多細節。 ```txt frame="none" We'd like to design this new screen using a design I've used before. @@ -250,20 +249,20 @@ How is authentication handled in @packages/functions/src/api/index.ts ``` :::tip - 將影像拖放到終端機中以將其新增到提示中。 + 將圖片拖放到終端機中即可將其新增到提示詞中。 ::: - OpenCode 可以掃描您提供的任何影像並將其新增到提示中。您可以透過將影像拖放到終端機中來完成此操作。 + OpenCode 可以掃描您提供的圖片並將其新增到提示詞中。只需將圖片拖放到終端機視窗即可。 3. **建置功能** - 一旦您對計畫感到滿意,請切換回*建置模式*,再次按 **Tab** 鍵。 + 當您對計畫滿意後,再次按 **Tab** 鍵切換回*建置模式*。 ```bash frame="none" ``` - 並要求它做出改變。 + 然後讓它開始實施。 ```bash frame="none" Sounds good! Go ahead and make the changes. @@ -271,9 +270,9 @@ How is authentication handled in @packages/functions/src/api/index.ts --- -### 做出改變 +### 直接修改 -對於更直接的變更,您可以要求 OpenCode 直接建置它,無需先審閱計畫。 +對於比較簡單的修改,您可以直接讓 OpenCode 實施,無需先審查計畫。 ```txt frame="none" "@packages/functions/src/settings.ts" "@packages/functions/src/notes.ts" We need to add authentication to the /settings route. Take a look at how this is @@ -281,37 +280,37 @@ handled in the /notes route in @packages/functions/src/notes.ts and implement the same logic in @packages/functions/src/settings.ts ``` -您需要確保提供大量詳細資訊,以便 OpenCode 做出正確的變更。 +請確保提供足夠的細節,以便 OpenCode 做出正確的修改。 --- -### 撤銷變更 +### 復原修改 -假設您要求 OpenCode 進行一些變更。 +假設您讓 OpenCode 做了一些修改。 ```txt frame="none" "@packages/functions/src/api/index.ts" Can you refactor the function in @packages/functions/src/api/index.ts? ``` -但你意識到這不是你想要的。您**可以撤銷**變更,使用 `/undo` 指令。 +但您發現結果不是您想要的。您**可以使用** `/undo` 指令來復原修改。 ```bash frame="none" /undo ``` -OpenCode 現在將復原您所做的變更並再次顯示您的原始訊息。 +OpenCode 會還原所做的修改,並重新顯示您之前的訊息。 ```txt frame="none" "@packages/functions/src/api/index.ts" Can you refactor the function in @packages/functions/src/api/index.ts? ``` -從這裡您可以調整提示並要求 OpenCode 重試。 +您可以調整提示詞,讓 OpenCode 重新嘗試。 :::tip -您可以多次執行 `/undo` 以撤銷多項變更。 +您可以多次執行 `/undo` 來復原多次修改。 ::: -或者您**可以使用 `/redo` 指令重做**變更。 +您也**可以使用** `/redo` 指令來重做修改。 ```bash frame="none" /redo @@ -321,24 +320,24 @@ Can you refactor the function in @packages/functions/src/api/index.ts? ## 分享 -您與 OpenCode 的對話可以[與您的團隊分享](/docs/share)。 +您與 OpenCode 的對話可以[與團隊分享](/docs/share)。 ```bash frame="none" /share ``` -這將建立目前對話的連結並將其複製到剪貼簿。 +這會生成當前對話的連結並複製到剪貼簿。 :::note -預設情況下不分享對話。 +對話預設不會被分享。 ::: -這是帶有 OpenCode 的[範例對話](https://opencode.ai/s/4XP1fce5)。 +這是一個與 OpenCode 的[範例對話](https://opencode.ai/s/4XP1fce5)。 --- -## 自訂 +## 個人化 -就是這樣!您現在已經是使用 OpenCode 的專家了。 +以上就是全部內容!您現在已經是 OpenCode 的使用高手了。 -要使其成為您自己的,我們建議 [選擇一個主題](/docs/themes)、[自訂按鍵綁定](/docs/keybinds)、[設定程式碼格式化程式](/docs/formatters)、[建立自定義指令](/docs/commands) 或使用 [OpenCode 設定](/docs/config)。 +要讓它更符合您的習慣,我們推薦[選擇一個主題](/docs/themes)、[自訂快捷鍵](/docs/keybinds)、[設定程式碼格式化器](/docs/formatters)、[建立自訂指令](/docs/commands),或者探索 [OpenCode 設定](/docs/config)。 diff --git a/packages/web/src/content/docs/zh-tw/keybinds.mdx b/packages/web/src/content/docs/zh-tw/keybinds.mdx index de3d6882f273..d1458dfe8c0c 100644 --- a/packages/web/src/content/docs/zh-tw/keybinds.mdx +++ b/packages/web/src/content/docs/zh-tw/keybinds.mdx @@ -1,9 +1,9 @@ --- -title: 按鍵綁定 -description: 自定義您的按鍵綁定。 +title: 快捷鍵 +description: 自訂您的快捷鍵。 --- -opencode 有一個按鍵綁定列表,您可以透過 opencode 設定進行自定義。 +OpenCode 提供了一系列快捷鍵,您可以透過 OpenCode 設定進行自訂。 ```json title="opencode.json" { @@ -105,19 +105,19 @@ opencode 有一個按鍵綁定列表,您可以透過 opencode 設定進行自 --- -## Leader 鍵 +## 前導鍵 -opencode 對大多數按鍵綁定使用 `leader` 鍵。這可以避免終端機中的衝突。 +OpenCode 的大多數快捷鍵使用 `leader`(前導鍵)。這可以避免與終端機中的其他快捷鍵衝突。 -預設情況下,`ctrl+x` 是 Leader 鍵,大多數操作要求您先按 Leader 鍵,然後再按快速鍵。例如,要開始新工作階段,請先按 `ctrl+x`,然後按 `n`。 +預設情況下,`ctrl+x` 是前導鍵,大多數操作需要您先按下前導鍵,然後再按對應的快捷鍵。例如,要新建一個工作階段,請先按 `ctrl+x`,然後按 `n`。 -您不需要為鍵綁定使用 Leader 鍵,但我們建議您這樣做。 +您不一定需要使用前導鍵來設定快捷鍵,但我們建議您這樣做。 --- -## 禁用按鍵綁定 +## 停用快捷鍵 -您可以透過將鍵添加到您的設定中並使用值「none」來禁用鍵綁定。 +您可以透過在設定中將對應的鍵值設定為 "none" 來停用某個快捷鍵。 ```json title="opencode.json" { @@ -130,41 +130,41 @@ opencode 對大多數按鍵綁定使用 `leader` 鍵。這可以避免終端機 --- -## 桌面提示快速鍵 - -opencode 桌面應用程式提示輸入支援常見的 Readline/Emacs 風格的文字編輯快速鍵。這些是內建的,目前無法透過 `opencode.json` 進行設定。 - -| 快速鍵 | 動作 | -| -------- | ------------------------- | -| `ctrl+a` | 移至當前行開頭 | -| `ctrl+e` | 移至當前行尾 | -| `ctrl+b` | 將游標向後移動一個字元 | -| `ctrl+f` | 將游標向前移動一個字元 | -| `alt+b` | 將游標向後移動一個字 | -| `alt+f` | 將游標向前移動一個字 | -| `ctrl+d` | 刪除游標下的字元 | -| `ctrl+k` | 刪除至行尾 | -| `ctrl+u` | 刪除至行首 | -| `ctrl+w` | 刪除前一個單字 | -| `alt+d` | 刪除下一個單字 | -| `ctrl+t` | 調換字元 | -| `ctrl+g` | 取消彈出視窗/中止執行回應 | +## 桌面版提示詞輸入快捷鍵 + +OpenCode 桌面應用程式的提示詞輸入框支援常見的 Readline/Emacs 風格文字編輯快捷鍵。這些快捷鍵為內建功能,目前無法透過 `opencode.json` 進行設定。 + +| 快捷鍵 | 操作 | +| -------- | --------------------------------- | +| `ctrl+a` | 移動到當前行的開頭 | +| `ctrl+e` | 移動到當前行的末尾 | +| `ctrl+b` | 游標向後移動一個字元 | +| `ctrl+f` | 游標向前移動一個字元 | +| `alt+b` | 游標向後移動一個單詞 | +| `alt+f` | 游標向前移動一個單詞 | +| `ctrl+d` | 刪除游標所在位置的字元 | +| `ctrl+k` | 刪除從游標到行尾的內容 | +| `ctrl+u` | 刪除從游標到行首的內容 | +| `ctrl+w` | 刪除前一個單詞 | +| `alt+d` | 刪除後一個單詞 | +| `ctrl+t` | 交換游標前後的字元 | +| `ctrl+g` | 取消彈出視窗 / 中止正在執行的回應 | --- ## Shift+Enter -預設情況下,某些終端機不發送帶有 Enter 的輔助鍵。您可能需要將終端機設定為發送 `Shift+Enter` 作為跳脫序列。 +某些終端機預設不會發送帶修飾鍵的 Enter 鍵。您可能需要設定終端機將 `Shift+Enter` 作為跳脫序列發送。 ### Windows Terminal -打開您的 `settings.json`: +開啟您的 `settings.json` 檔案,路徑為: ``` %LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json ``` -將其添加到根級 `actions` 陣列: +將以下內容新增到根級 `actions` 陣列中: ```json "actions": [ @@ -178,7 +178,7 @@ opencode 桌面應用程式提示輸入支援常見的 Readline/Emacs 風格的 ] ``` -將其添加到根級 `keybindings` 陣列: +將以下內容新增到根級 `keybindings` 陣列中: ```json "keybindings": [ @@ -189,4 +189,4 @@ opencode 桌面應用程式提示輸入支援常見的 Readline/Emacs 風格的 ] ``` -儲存檔案並重新啟動 Windows Terminal 或打開新分頁。 +儲存檔案並重新啟動 Windows Terminal,或開啟一個新分頁。 diff --git a/packages/web/src/content/docs/zh-tw/lsp.mdx b/packages/web/src/content/docs/zh-tw/lsp.mdx index 54bc37c26351..ae419261fff3 100644 --- a/packages/web/src/content/docs/zh-tw/lsp.mdx +++ b/packages/web/src/content/docs/zh-tw/lsp.mdx @@ -1,71 +1,71 @@ --- title: LSP 伺服器 -description: opencode 與您的 LSP 伺服器整合。 +description: OpenCode 與您的 LSP 伺服器整合。 --- -opencode 與您的語言伺服器協定 (LSP) 整合,以幫助 LLM 與您的程式碼庫互動。它使用診斷向 LLM 提供回饋。 +OpenCode 與您的語言伺服器協定(LSP)整合,協助 LLM 與您的程式碼庫進行互動。它利用診斷資訊向 LLM 提供回饋。 --- -## 內建 - -opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: - -| LSP 伺服器 | 副檔名 | 要求 | -| ------------------ | ------------------------------------------------------------------- | ------------------------------------------------ | -| astro | .astro | Astro 專案自動安裝 | -| bash | .sh、.bash、.zsh、.ksh | 自動安裝 bash-language-server | -| clangd | .c、.cpp、.cc、.cxx、.c++、.h、.hpp、.hh、.hxx、.h++ | 自動安裝 C/C++ 專案 | -| csharp | .cs | `.NET SDK` 已安裝 | -| clojure-lsp | .clj、.cljs、.cljc、.edn | `clojure-lsp` 指令可用 | -| dart | .dart | `dart` 指令可用 | -| deno | .ts、.tsx、.js、.jsx、.mjs | `deno` 指令可用(自動檢測 deno.json/deno.jsonc) | -| elixir-ls | .ex、.exs | `elixir` 指令可用 | -| eslint | .ts、.tsx、.js、.jsx、.mjs、.cjs、.mts、.cts、.vue | `eslint` 專案中的相依套件 | -| fsharp | .fs、.fsi、.fsx、.fsscript | `.NET SDK` 已安裝 | -| gleam | .gleam | `gleam` 指令可用 | -| gopls | .go | `go` 指令可用 | -| hls | .hs、.lhs | `haskell-language-server-wrapper` 指令可用 | -| jdtls | .java | `Java SDK (version 21+)` 已安裝 | -| kotlin-ls | .kt、.kts | Kotlin 專案的自動安裝 | -| lua-ls | .lua | 自動安裝 Lua 專案 | -| nil | .nix | `nixd` 指令可用 | -| ocaml-lsp | .ml、.mli | `ocamllsp` 指令可用 | -| oxlint | .ts、.tsx、.js、.jsx、.mjs、.cjs、.mts、.cts、.vue、.astro、.svelte | `oxlint` 專案中的相依套件 | -| php intelephense | .php | PHP 專案的自動安裝 | -| prisma | .prisma | `prisma` 指令可用 | -| pyright | .py, .pyi | `pyright` 相依套件已安裝 | -| ruby-lsp (rubocop) | .rb、.rake、.gemspec、.ru | `ruby` 和 `gem` 指令可用 | -| rust-analyzer | .rs | `rust-analyzer` 指令可用 | -| sourcekit-lsp | .swift、.objc、.objcpp | `swift` 已安裝(`xcode` 在 macOS 上) | -| svelte | .svelte | Svelte 專案的自動安裝 | -| terraform-ls | .tf、.tfvars | 從 GitHub Releases 自動安裝 | -| tinymist | .typ、.typc | 從 GitHub Releases 自動安裝 | -| typescript | .ts、.tsx、.js、.jsx、.mjs、.cjs、.mts、.cts | `typescript` 專案中的相依套件 | -| vue | .vue | Vue 專案自動安裝 | -| yaml-ls | .yaml、.yml | 自動安裝 Red Hat yaml-language-server | -| zls | .zig、.zon | `zig` 指令可用 | - -當檢測到上述檔案副檔名之一且滿足要求時,LSP 伺服器將自動啟用。 +## 內建支援 + +OpenCode 內建了多種適用於主流語言的 LSP 伺服器: + +| LSP 伺服器 | 副檔名 | 要求 | +| ------------------ | ------------------------------------------------------------------- | ----------------------------------------------------- | +| astro | .astro | 為 Astro 專案自動安裝 | +| bash | .sh, .bash, .zsh, .ksh | 自動安裝 bash-language-server | +| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | 為 C/C++ 專案自動安裝 | +| csharp | .cs | 需要已安裝 `.NET SDK` | +| clojure-lsp | .clj, .cljs, .cljc, .edn | 需要 `clojure-lsp` 指令可用 | +| dart | .dart | 需要 `dart` 指令可用 | +| deno | .ts, .tsx, .js, .jsx, .mjs | 需要 `deno` 指令可用(自動偵測 deno.json/deno.jsonc) | +| elixir-ls | .ex, .exs | 需要 `elixir` 指令可用 | +| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue | 專案中需要 `eslint` 相依套件 | +| fsharp | .fs, .fsi, .fsx, .fsscript | 需要已安裝 `.NET SDK` | +| gleam | .gleam | 需要 `gleam` 指令可用 | +| gopls | .go | 需要 `go` 指令可用 | +| hls | .hs, .lhs | 需要 `haskell-language-server-wrapper` 指令可用 | +| jdtls | .java | 需要已安裝 `Java SDK (version 21+)` | +| kotlin-ls | .kt, .kts | 為 Kotlin 專案自動安裝 | +| lua-ls | .lua | 為 Lua 專案自動安裝 | +| nixd | .nix | 需要 `nixd` 指令可用 | +| ocaml-lsp | .ml, .mli | 需要 `ocamllsp` 指令可用 | +| oxlint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue, .astro, .svelte | 專案中需要 `oxlint` 相依套件 | +| php intelephense | .php | 為 PHP 專案自動安裝 | +| prisma | .prisma | 需要 `prisma` 指令可用 | +| pyright | .py, .pyi | 需要已安裝 `pyright` 相依套件 | +| ruby-lsp (rubocop) | .rb, .rake, .gemspec, .ru | 需要 `ruby` 和 `gem` 指令可用 | +| rust | .rs | 需要 `rust-analyzer` 指令可用 | +| sourcekit-lsp | .swift, .objc, .objcpp | 需要已安裝 `swift`(macOS 上為 `xcode`) | +| svelte | .svelte | 為 Svelte 專案自動安裝 | +| terraform | .tf, .tfvars | 從 GitHub releases 自動安裝 | +| tinymist | .typ, .typc | 從 GitHub releases 自動安裝 | +| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | 專案中需要 `typescript` 相依套件 | +| vue | .vue | 為 Vue 專案自動安裝 | +| yaml-ls | .yaml, .yml | 自動安裝 Red Hat yaml-language-server | +| zls | .zig, .zon | 需要 `zig` 指令可用 | + +當偵測到上述檔案副檔名且滿足相應要求時,LSP 伺服器會自動啟用。 :::note -您可以透過將 `OPENCODE_DISABLE_LSP_DOWNLOAD` 環境變數設定為 `true` 來禁用自動 LSP 伺服器下載。 +您可以將 `OPENCODE_DISABLE_LSP_DOWNLOAD` 環境變數設定為 `true` 來停用 LSP 伺服器的自動下載。 ::: --- -## 它是如何運作的 +## 工作原理 -當 opencode 打開一個檔案時,它: +當 OpenCode 開啟一個檔案時,它會: -1. 根據所有啟用的 LSP 伺服器檢查檔案副檔名。 -2. 如果尚未執行,則啟動相應的 LSP 伺服器。 +1. 將檔案副檔名與所有已啟用的 LSP 伺服器進行比對。 +2. 如果對應的 LSP 伺服器尚未執行,則自動啟動它。 --- ## 設定 -您可以透過 opencode 設定中的 `lsp` 部分自定義 LSP 伺服器。 +您可以透過 OpenCode 設定檔中的 `lsp` 部分來自訂 LSP 伺服器。 ```json title="opencode.json" { @@ -74,23 +74,23 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: } ``` -每個 LSP 伺服器支援以下功能: +每個 LSP 伺服器支援以下設定項: -| 屬性 | 類型 | 描述 | -| ---------------- | ------ | ----------------------------------- | -| `disabled` | 布林值 | 將其設定為 `true` 以禁用 LSP 伺服器 | -| `command` | 字串[] | 啟動 LSP 伺服器的指令 | -| `extensions` | 字串[] | 此 LSP 伺服器應處理的檔案副檔名 | -| `env` | 物件 | 啟動伺服器時設定的環境變數 | -| `initialization` | 物件 | 發送到 LSP 伺服器的初始化選項 | +| 屬性 | 類型 | 描述 | +| ---------------- | -------- | --------------------------------- | +| `disabled` | boolean | 設定為 `true` 可停用該 LSP 伺服器 | +| `command` | string[] | 啟動 LSP 伺服器的指令 | +| `extensions` | string[] | 該 LSP 伺服器需要處理的檔案副檔名 | +| `env` | object | 啟動伺服器時設定的環境變數 | +| `initialization` | object | 傳送給 LSP 伺服器的初始化選項 | -讓我們看一些例子。 +下面來看一些範例。 --- ### 環境變數 -啟動 LSP 伺服器時使用 `env` 屬性設定環境變數: +使用 `env` 屬性在啟動 LSP 伺服器時設定環境變數: ```json title="opencode.json" {5-7} { @@ -109,7 +109,7 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: ### 初始化選項 -使用 `initialization` 屬性將初始化選項傳遞給 LSP 伺服器。這些是在 LSP `initialize` 請求期間發送的伺服器特定設定: +使用 `initialization` 屬性向 LSP 伺服器傳遞初始化選項。這些是在 LSP `initialize` 請求期間傳送的伺服器特定設定: ```json title="opencode.json" {5-9} { @@ -127,14 +127,14 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: ``` :::note -初始化選項因 LSP 伺服器而異。檢查 LSP 伺服器的文件以獲取可用選項。 +初始化選項因 LSP 伺服器而異。請查閱您所使用的 LSP 伺服器的文件以了解可用選項。 ::: --- -### 禁用 LSP 伺服器 +### 停用 LSP 伺服器 -要全域禁用**所有** LSP 伺服器,請將 `lsp` 設定為 `false`: +要全域停用**所有** LSP 伺服器,將 `lsp` 設定為 `false`: ```json title="opencode.json" {3} { @@ -143,7 +143,7 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: } ``` -要禁用**特定** LSP 伺服器,請將 `disabled` 設定為 `true`: +要停用**特定的** LSP 伺服器,將 `disabled` 設定為 `true`: ```json title="opencode.json" {5} { @@ -158,9 +158,9 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: --- -### 自定義 LSP 伺服器 +### 自訂 LSP 伺服器 -您可以透過指定指令和檔案副檔名來添加自定義 LSP 伺服器: +您可以透過指定指令和檔案副檔名來新增自訂 LSP 伺服器: ```json title="opencode.json" {4-7} { @@ -176,13 +176,13 @@ opencode 附帶了多種適用於流行語言的內建 LSP 伺服器: --- -## 附加資訊 +## 補充資訊 ### PHP Intelephense -PHP Intelephense 透過授權金鑰提供高級功能。您可以透過將(僅)金鑰放入位於以下位置的文字檔案中來提供授權金鑰: +PHP Intelephense 透過授權金鑰提供進階功能。您可以將授權金鑰單獨放在以下路徑的文字檔案中: -- 在 macOS/Linux 上:`$HOME/intelephense/license.txt` -- 在 Windows 上:`%USERPROFILE%/intelephense/license.txt` +- macOS/Linux:`$HOME/intelephense/license.txt` +- Windows:`%USERPROFILE%/intelephense/license.txt` -該檔案應僅包含授權金鑰,不包含其他內容。 +該檔案應僅包含授權金鑰,不要新增其他任何內容。 diff --git a/packages/web/src/content/docs/zh-tw/mcp-servers.mdx b/packages/web/src/content/docs/zh-tw/mcp-servers.mdx index 829e696e9245..971b48d356b9 100644 --- a/packages/web/src/content/docs/zh-tw/mcp-servers.mdx +++ b/packages/web/src/content/docs/zh-tw/mcp-servers.mdx @@ -1,29 +1,29 @@ --- title: MCP 伺服器 -description: 添加本地和遠端 MCP 工具。 +description: 新增本地和遠端 MCP 工具。 --- -您可以使用「模型上下文協定」或 MCP 將外部工具添加到 opencode。 opencode 支援本地和遠端伺服器。 +您可以透過 _Model Context Protocol_(MCP)為 OpenCode 新增外部工具。OpenCode 同時支援本地和遠端伺服器。 -添加後,MCP 工具將自動與內建工具一起供 LLM 使用。 +新增後,MCP 工具會自動與內建工具一起提供給 LLM 使用。 --- #### 注意事項 -當您使用 MCP 伺服器時,它會添加到上下文中。如果您有很多工具,這會很快增加。因此,我們建議您謹慎選擇使用哪些 MCP 伺服器。 +使用 MCP 伺服器時,它會佔用上下文空間。如果您啟用了大量工具,上下文消耗會迅速增加。因此,我們建議謹慎選擇要使用的 MCP 伺服器。 :::tip -MCP 伺服器會添加到您的上下文中,因此您需要小心啟用哪些伺服器。 +MCP 伺服器會佔用您的上下文空間,所以請謹慎選擇啟用哪些伺服器。 ::: -某些 MCP 伺服器(例如 GitHub MCP 伺服器)往往會添加大量 tokens,並且很容易超出上下文限制。 +某些 MCP 伺服器(例如 GitHub MCP 伺服器)往往會消耗大量 Token,很容易超出上下文限制。 --- ## 啟用 -您可以在 `mcp` 下的 [opencode 設定](https://opencode.ai/docs/config/) 中定義 MCP 伺服器。為每個 MCP 添加唯一的名稱。當提示 LLM 時,您可以透過名稱引用該 MCP。 +您可以在 [OpenCode 設定](https://opencode.ai/docs/config/)的 `mcp` 欄位下定義 MCP 伺服器。為每個 MCP 指定一個唯一的名稱,在提示詞中可以透過該名稱來參照對應的 MCP。 ```jsonc title="opencode.jsonc" {6} { @@ -40,15 +40,15 @@ MCP 伺服器會添加到您的上下文中,因此您需要小心啟用哪些 } ``` -您還可以透過將 `enabled` 設定為 `false` 來禁用伺服器。如果您想暫時禁用伺服器而不將其從設定中刪除,這非常有用。 +您也可以將 `enabled` 設定為 `false` 來停用某個伺服器。當您想臨時停用某個伺服器而不將其從設定中移除時,這個選項非常有用。 --- ### 覆寫遠端預設值 -組織可以透過其 `.well-known/opencode` 端點提供預設 MCP 伺服器。這些伺服器可能預設被禁用,允許使用者選擇他們需要的伺服器。 +組織可以透過其 `.well-known/opencode` 端點提供預設的 MCP 伺服器。這些伺服器可能預設處於停用狀態,允許使用者按需啟用。 -要從組織的遠端設定啟用特定伺服器,請使用 `enabled: true` 將其添加到本地設定: +要啟用組織遠端設定中的某個伺服器,請在本地設定中新增該伺服器並設定 `enabled: true`: ```json title="opencode.json" { @@ -63,13 +63,13 @@ MCP 伺服器會添加到您的上下文中,因此您需要小心啟用哪些 } ``` -您的本地設定值會覆寫遠端預設值。有關更多詳細資訊,請參閱 [設定優先級](/docs/config#precedence-order)。 +本地設定值會覆寫遠端預設值。詳情請參閱[設定優先順序](/docs/config#precedence-order)。 --- ## 本地 -使用 `type` 將本地 MCP 伺服器添加到 MCP 物件中的 `"local"`。 +透過在 MCP 物件中將 `type` 設定為 `"local"` 來新增本地 MCP 伺服器。 ```jsonc title="opencode.jsonc" {15} { @@ -88,9 +88,9 @@ MCP 伺服器會添加到您的上下文中,因此您需要小心啟用哪些 } ``` -該指令是本地 MCP 伺服器的啟動方式。您還可以傳入環境變數列表。 +`command` 用於指定本地 MCP 伺服器的啟動指令。您還可以傳入一組環境變數。 -例如,以下是添加測試 [`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) MCP 伺服器的方法。 +例如,以下是新增測試用的 [`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) MCP 伺服器的方法。 ```jsonc title="opencode.jsonc" { @@ -104,7 +104,7 @@ MCP 伺服器會添加到您的上下文中,因此您需要小心啟用哪些 } ``` -要使用它,我可以將 `use the mcp_everything tool` 添加到我的提示中。 +要使用它,可以在提示詞中新增 `use the mcp_everything tool`。 ```txt "mcp_everything" use the mcp_everything tool to add the number 3 and 4 @@ -116,19 +116,19 @@ use the mcp_everything tool to add the number 3 and 4 以下是設定本地 MCP 伺服器的所有選項。 -| 選項 | 類型 | 必填 | 描述 | -| ------------- | ------ | ---- | ------------------------------------------------------------------ | -| `type` | 字串 | 是 | MCP 伺服器連接類型,必須是 `"local"`。 | -| `command` | 陣列 | 是 | 執行 MCP 伺服器的指令和參數。 | -| `environment` | 物件 | | 執行伺服器時設定的環境變數。 | -| `enabled` | 布林值 | | 在啟動時啟用或禁用 MCP 伺服器。 | -| `timeout` | 數量 | | 從 MCP 伺服器獲取工具的超時(以毫秒為單位)。預設為 5000(5 秒)。 | +| 選項 | 類型 | 必填 | 描述 | +| ------------- | ------ | ---- | ----------------------------------------------------------------- | +| `type` | 字串 | 是 | MCP 伺服器連線類型,必須為 `"local"`。 | +| `command` | 陣列 | 是 | 執行 MCP 伺服器的指令及參數。 | +| `environment` | 物件 | | 執行伺服器時設定的環境變數。 | +| `enabled` | 布林值 | | 啟動時啟用或停用該 MCP 伺服器。 | +| `timeout` | 數字 | | 從 MCP 伺服器取得工具的逾時時間(毫秒)。預設為 5000(即 5 秒)。 | --- ## 遠端 -透過將 `type` 設定為 `"remote"` 添加遠端 MCP 伺服器。 +透過將 `type` 設定為 `"remote"` 來新增遠端 MCP 伺服器。 ```json title="opencode.json" { @@ -146,36 +146,36 @@ use the mcp_everything tool to add the number 3 and 4 } ``` -`url` 是遠端 MCP 伺服器的 URL,使用 `headers` 選項您可以傳入標頭列表。 +`url` 是遠端 MCP 伺服器的位址,透過 `headers` 選項可以傳入一組請求標頭。 --- #### 選項 -| 選項 | 類型 | 必填 | 描述 | -| --------- | ------ | ---- | ------------------------------------------------------------------ | -| `type` | 字串 | 是 | MCP 伺服器連接類型,必須是 `"remote"`。 | -| `url` | 字串 | 是 | 遠端 MCP 伺服器的 URL。 | -| `enabled` | 布林值 | | 在啟動時啟用或禁用 MCP 伺服器。 | -| `headers` | 物件 | | 隨請求一起發送的標頭。 | -| `oauth` | 物件 | | OAuth 身分驗證設定。請參閱下面的 [OAuth](#oauth) 部分。 | -| `timeout` | 數量 | | 從 MCP 伺服器獲取工具的超時(以毫秒為單位)。預設為 5000(5 秒)。 | +| 選項 | 類型 | 必填 | 描述 | +| --------- | ------ | ---- | ----------------------------------------------------------------- | +| `type` | 字串 | 是 | MCP 伺服器連線類型,必須為 `"remote"`。 | +| `url` | 字串 | 是 | 遠端 MCP 伺服器的 URL。 | +| `enabled` | 布林值 | | 啟動時啟用或停用該 MCP 伺服器。 | +| `headers` | 物件 | | 隨請求傳送的請求標頭。 | +| `oauth` | 物件 | | OAuth 身分驗證設定。詳見下方 [OAuth](#oauth) 部分。 | +| `timeout` | 數字 | | 從 MCP 伺服器取得工具的逾時時間(毫秒)。預設為 5000(即 5 秒)。 | --- ## OAuth -opencode 自動處理遠端 MCP 伺服器的 OAuth 身分驗證。當伺服器需要身分驗證時,opencode 將: +OpenCode 會自動處理遠端 MCP 伺服器的 OAuth 身分驗證。當伺服器需要身分驗證時,OpenCode 將: -1. 檢測 401 回應並啟動 OAuth 流程 -2. 如果伺服器支援,請使用**動態客戶端註冊 (RFC 7591)** -3. 安全地儲存權杖以供將來的請求 +1. 偵測 401 回應並啟動 OAuth 流程 +2. 在伺服器支援的情況下使用**動態用戶端註冊(RFC 7591)** +3. 安全地儲存 Token 以供後續請求使用 --- -### 自動 +### 自動認證 -對於大多數支援 OAuth 的 MCP 伺服器,不需要特殊設定。只需設定遠端伺服器: +對於大多數支援 OAuth 的 MCP 伺服器,無需特殊設定。只需設定遠端伺服器即可: ```json title="opencode.json" { @@ -189,13 +189,13 @@ opencode 自動處理遠端 MCP 伺服器的 OAuth 身分驗證。當伺服器 } ``` -如果伺服器需要身分驗證,opencode 將在您第一次嘗試使用它時提示您進行身分驗證。如果沒有,您可以使用 `opencode mcp auth ` [手動觸發流程](#authenticating)。 +如果伺服器需要身分驗證,OpenCode 會在您首次使用時提示您進行認證。您也可以使用 `opencode mcp auth ` [手動觸發認證流程](#authenticating)。 --- ### 預先註冊 -如果您有來自 MCP 伺服器供應商的客戶端憑證,則可以設定它們: +如果您已經從 MCP 伺服器提供商處取得了用戶端憑證,可以直接設定: ```json title="opencode.json" {7-11} { @@ -216,35 +216,35 @@ opencode 自動處理遠端 MCP 伺服器的 OAuth 身分驗證。當伺服器 --- -### 進行身分驗證 +### 身分驗證 您可以手動觸發身分驗證或管理憑證。 -使用特定 MCP 伺服器進行身分驗證: +對特定 MCP 伺服器進行身分驗證: ```bash opencode mcp auth my-oauth-server ``` -列出所有 MCP 伺服器及其身分驗證狀態: +列出所有 MCP 伺服器及其認證狀態: ```bash opencode mcp list ``` -刪除儲存的憑證: +刪除已儲存的憑證: ```bash opencode mcp logout my-oauth-server ``` -`mcp auth` 指令將打開您的瀏覽器進行授權。授權後,opencode 會將權杖安全地儲存在 `~/.local/share/opencode/mcp-auth.json` 中。 +`mcp auth` 指令會開啟瀏覽器進行授權。授權完成後,OpenCode 會將 Token 安全地儲存在 `~/.local/share/opencode/mcp-auth.json` 中。 --- -#### 禁用 OAuth +#### 停用 OAuth -如果要禁用伺服器的自動 OAuth(例如,對於使用 API 金鑰的伺服器),請將 `oauth` 設定為 `false`: +如果您想為某個伺服器停用自動 OAuth(例如,該伺服器使用 API 金鑰而非 OAuth),可以將 `oauth` 設定為 `false`: ```json title="opencode.json" {7} { @@ -266,38 +266,38 @@ opencode mcp logout my-oauth-server #### OAuth 選項 -| 選項 | 類型 | 描述 | -| -------------- | --------------- | --------------------------------------------------- | -| `oauth` | Object \| false | OAuth 設定物件,或 `false` 以禁用 OAuth 自動檢測。 | -| `clientId` | String | OAuth 客戶端 ID。如果未提供,將嘗試動態客戶端註冊。 | -| `clientSecret` | String | OAuth 客戶端密鑰(如果授權伺服器需要)。 | -| `scope` | String | 授權期間請求的 OAuth 範圍。 | +| 選項 | 類型 | 描述 | +| -------------- | --------------- | ------------------------------------------------------ | +| `oauth` | 物件 \| `false` | OAuth 設定物件,或設為 `false` 以停用 OAuth 自動偵測。 | +| `clientId` | 字串 | OAuth 用戶端 ID。如果未提供,將嘗試動態用戶端註冊。 | +| `clientSecret` | 字串 | OAuth 用戶端密鑰(如果授權伺服器要求提供)。 | +| `scope` | 字串 | 授權時請求的 OAuth 作用域。 | #### 偵錯 -如果遠端 MCP 伺服器無法進行身分驗證,您可以透過以下方式診斷問題: +如果遠端 MCP 伺服器身分驗證失敗,您可以透過以下方式診斷問題: ```bash -# View auth status for all OAuth-capable servers +# 查看所有支援 OAuth 的伺服器的認證狀態 opencode mcp auth list -# Debug connection and OAuth flow for a specific server +# 偵錯特定伺服器的連線和 OAuth 流程 opencode mcp debug my-oauth-server ``` -`mcp debug` 指令顯示當前身分驗證狀態、測試 HTTP 連接並嘗試 OAuth 發現流程。 +`mcp debug` 指令會顯示當前認證狀態、測試 HTTP 連線,並嘗試執行 OAuth 發現流程。 --- ## 管理 -您的 MCP 可作為 opencode 中的工具以及內建工具使用。因此,您可以像任何其他工具一樣透過 opencode 設定來管理它們。 +您的 MCP 在 OpenCode 中作為工具使用,與內建工具並列。因此,您可以像管理其他工具一樣,透過 OpenCode 設定來管理它們。 --- ### 全域 -這意味著您可以全域啟用或禁用它們。 +您可以全域啟用或停用 MCP 工具。 ```json title="opencode.json" {14} { @@ -318,7 +318,7 @@ opencode mcp debug my-oauth-server } ``` -我們也可以使用 glob 模式來禁用所有匹配的 MCP。 +也可以使用 glob 模式來停用所有符合條件的 MCP。 ```json title="opencode.json" {14} { @@ -339,16 +339,16 @@ opencode mcp debug my-oauth-server } ``` -這裡我們使用 glob 模式 `my-mcp*` 來禁用所有 MCP。 +這裡使用 glob 模式 `my-mcp*` 來停用所有 MCP。 --- -### 每個代理 +### 按代理設定 -如果您有大量 MCP 伺服器,您可能只想為每個代理啟用它們並全域禁用它們。為此: +如果您有大量 MCP 伺服器,可以選擇全域停用它們,然後僅在特定代理中啟用。具體做法: -1. 全局禁用它作為工具。 -2. 在您的 [代理設定](/docs/agents#tools) 中,啟用 MCP 伺服器作為工具。 +1. 全域停用該工具。 +2. 在[代理設定](/docs/agents#tools)中,將 MCP 伺服器作為工具啟用。 ```json title="opencode.json" {11, 14-18} { @@ -377,14 +377,14 @@ opencode mcp debug my-oauth-server #### Glob 模式 -glob 模式使用簡單的正規表示式 globbing 模式: +glob 模式使用簡單的正規表示式萬用字元規則: -- `*` 匹配零個或多個任意字元(例如,`"my-mcp*"` 匹配 `my-mcp_search`、`my-mcp_list` 等) -- `?` 恰好匹配一個字元 -- 所有其他字元均按字面意思匹配 +- `*` 比對零個或多個任意字元(例如,`"my-mcp*"` 比對 `my-mcp_search`、`my-mcp_list` 等) +- `?` 比對恰好一個字元 +- 其他字元按字面值比對 :::note -MCP 伺服器工具以伺服器名稱作為前綴進行註冊,因此要禁用伺服器的所有工具,只需使用: +MCP 伺服器工具在註冊時以伺服器名稱作為前綴,因此要停用某個伺服器的所有工具,只需使用: ``` "mymcpservername_*": false @@ -396,13 +396,13 @@ MCP 伺服器工具以伺服器名稱作為前綴進行註冊,因此要禁用 ## 範例 -以下是一些常見 MCP 伺服器的範例。如果您想記錄其他伺服器,您可以提交 PR。 +以下是一些常見 MCP 伺服器的設定範例。如果您想記錄其他伺服器的用法,歡迎提交 PR。 --- ### Sentry -添加 [Sentry MCP 伺服器](https://mcp.sentry.dev) 以與您的 Sentry 專案和問題進行互動。 +新增 [Sentry MCP 伺服器](https://mcp.sentry.dev) 以與您的 Sentry 專案和問題進行互動。 ```json title="opencode.json" {4-8} { @@ -417,15 +417,15 @@ MCP 伺服器工具以伺服器名稱作為前綴進行註冊,因此要禁用 } ``` -添加設定後,使用 Sentry 進行身分驗證: +新增設定後,使用 Sentry 進行身分驗證: ```bash opencode mcp auth sentry ``` -這將打開一個瀏覽器視窗以完成 OAuth 流程並將 opencode 連接到您的 Sentry 帳號。 +這會開啟瀏覽器視窗完成 OAuth 流程,將 OpenCode 連結到您的 Sentry 帳號。 -通過身分驗證後,您可以在提示中使用 Sentry 工具來查詢問題、專案和錯誤資料。 +認證完成後,您可以在提示詞中使用 Sentry 工具來查詢問題、專案和錯誤資料。 ```txt "use sentry" Show me the latest unresolved issues in my project. use sentry @@ -435,7 +435,7 @@ Show me the latest unresolved issues in my project. use sentry ### Context7 -添加 [Context7 MCP 伺服器](https://github.com/upstash/context7) 以搜尋文件。 +新增 [Context7 MCP 伺服器](https://github.com/upstash/context7) 以搜尋文件。 ```json title="opencode.json" {4-7} { @@ -449,7 +449,7 @@ Show me the latest unresolved issues in my project. use sentry } ``` -如果您註冊了免費帳號,則可以使用 API 金鑰並獲得更高的速率限制。 +如果您註冊了免費帳號,可以使用 API 金鑰來取得更高的速率限制。 ```json title="opencode.json" {7-9} { @@ -466,15 +466,15 @@ Show me the latest unresolved issues in my project. use sentry } ``` -這裡我們假設您設定了 `CONTEXT7_API_KEY` 環境變數。 +這裡假設您已經設定了 `CONTEXT7_API_KEY` 環境變數。 -將 `use context7` 添加到提示中以使用 Context7 MCP 伺服器。 +在提示詞中新增 `use context7` 即可使用 Context7 MCP 伺服器。 ```txt "use context7" Configure a Cloudflare Worker script to cache JSON API responses for five minutes. use context7 ``` -或者,您可以將類似的內容添加到您的 [AGENTS.md](/docs/rules/)。 +您也可以在 [AGENTS.md](/docs/rules/) 中新增類似的規則。 ```md title="AGENTS.md" When you need to search docs, use `context7` tools. @@ -482,9 +482,9 @@ When you need to search docs, use `context7` tools. --- -### Vercel 的 Grep +### Grep by Vercel -添加 [Vercel 的 Grep](https://grep.app) MCP 伺服器以搜尋 GitHub 上的程式碼片段。 +新增 [Grep by Vercel](https://grep.app) MCP 伺服器以搜尋 GitHub 上的程式碼片段。 ```json title="opencode.json" {4-7} { @@ -498,13 +498,13 @@ When you need to search docs, use `context7` tools. } ``` -由於我們將 MCP 伺服器命名為 `gh_grep`,因此您可以將 `use the gh_grep tool` 添加到提示中以使代理使用它。 +由於我們將 MCP 伺服器命名為 `gh_grep`,您可以在提示詞中新增 `use the gh_grep tool` 來讓代理使用它。 ```txt "use the gh_grep tool" What's the right way to set a custom domain in an SST Astro component? use the gh_grep tool ``` -或者,您可以將類似的內容添加到您的 [AGENTS.md](/docs/rules/)。 +您也可以在 [AGENTS.md](/docs/rules/) 中新增類似的規則。 ```md title="AGENTS.md" If you are unsure how to do something, use `gh_grep` to search code examples from GitHub. diff --git a/packages/web/src/content/docs/zh-tw/models.mdx b/packages/web/src/content/docs/zh-tw/models.mdx index 082f8ba4d291..5c945463aa1d 100644 --- a/packages/web/src/content/docs/zh-tw/models.mdx +++ b/packages/web/src/content/docs/zh-tw/models.mdx @@ -1,23 +1,23 @@ --- title: 模型 -description: 配置 LLM 供應商和模型。 +description: 設定 LLM 提供商和模型。 --- -OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev) 來支援 **75+ LLM 供應商**,並且它支援執行本地模型。 +OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev) 支援 **75+ LLM 提供商**,並支援執行本地模型。 --- -## 供應商 +## 提供商 -預設情況下會預先載入大多數流行的供應商。如果您已透過 `/connect` 指令添加了供應商的憑證,那麼它們將在您啟動 OpenCode 時可用。 +大多數熱門提供商已預設預先載入。如果您透過 `/connect` 指令新增了提供商的憑證,它們將在您啟動 OpenCode 時自動可用。 -了解有關 [供應商](/docs/providers) 的更多資訊。 +了解更多關於[提供商](/docs/providers)的資訊。 --- ## 選擇模型 -配置完供應商後,您可以透過輸入以下內容來選擇您想要的模型: +設定好提供商後,您可以透過輸入以下指令來選擇想要使用的模型: ```bash frame="none" /models @@ -27,15 +27,15 @@ OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de ## 推薦模型 -那裡有很多模型,每週都會有新模型問世。 +市面上有非常多的模型,每週都有新模型發布。 :::tip -考慮使用我們推薦的模型之一。 +建議使用我們推薦的模型。 ::: -然而,既擅長生成程式碼又擅長工具呼叫的只有少數。 +然而,真正擅長程式碼生成和工具呼叫的模型只有少數幾個。 -以下是與 OpenCode 配合良好的幾個模型,排名不分先後。 (這不是詳盡的列表,也不一定是最新的): +以下是與 OpenCode 配合良好的幾個模型,排名不分先後(此列表並非詳盡無遺,也不一定是最新的): - GPT 5.2 - GPT 5.1 Codex @@ -46,10 +46,9 @@ OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de --- -## 設定預設值 +## 設定預設模型 -要將其中之一設定為預設模型,您可以在您的 -OpenCode 配置。 +要將某個模型設為預設模型,可以在 OpenCode 設定中設定 `model` 欄位。 ```json title="opencode.json" {3} { @@ -58,15 +57,15 @@ OpenCode 配置。 } ``` -這裡完整的 ID 是 `provider_id/model_id`。例如,如果您使用 [OpenCode Zen](/docs/zen),則您將使用 `opencode/gpt-5.1-codex` 來表示 GPT 5.1 Codex。 +這裡完整的 ID 格式為 `provider_id/model_id`。例如,如果您使用 [OpenCode Zen](/docs/zen),則 GPT 5.1 Codex 對應的值為 `opencode/gpt-5.1-codex`。 -如果您配置了 [自定義供應商](/docs/providers#custom),則 `provider_id` 是配置中 `provider` 部分的鍵,`model_id` 是 `provider.models` 中的鍵。 +如果您設定了[自訂提供商](/docs/providers#custom),`provider_id` 是設定中 `provider` 部分的鍵名,`model_id` 是 `provider.models` 中的鍵名。 --- -## 配置模型 +## 設定模型 -您可以透過 config.json 全局配置模型的選項。 +您可以透過設定檔全域設定模型的選項。 ```jsonc title="opencode.jsonc" {7-12,19-24} { @@ -100,12 +99,12 @@ OpenCode 配置。 } ``` -在這裡,我們為兩個內建模型配置全局設定:`gpt-5`(透過 `openai` 供應商存取時)和 `claude-sonnet-4-20250514`(透過 `anthropic` 供應商存取時)。 -內建供應商和模型名稱可以在 [Models.dev](https://models.dev) 上找到。 +這裡我們為兩個內建模型設定了全域選項:透過 `openai` 提供商存取的 `gpt-5`,以及透過 `anthropic` 提供商存取的 `claude-sonnet-4-20250514`。 +內建的提供商和模型名稱可以在 [Models.dev](https://models.dev) 上查閱。 -您還可以為您正在使用的任何代理配置這些選項。代理配置會覆寫此處的所有全局選項。 [了解更多](/docs/agents/#additional)。 +您還可以為使用中的任何代理設定這些選項。代理設定會覆寫此處的全域選項。[了解更多](/docs/agents/#additional)。 -您還可以定義擴展內建變體的自定義變體。變體允許您為同一模型配置不同的設定,而無需建立重複的項目: +您也可以定義擴展內建變體的自訂變體。變體允許您為同一個模型設定不同的選項,而無需建立重複的項目: ```jsonc title="opencode.jsonc" {6-21} { @@ -137,11 +136,11 @@ OpenCode 配置。 ## 變體 -許多模型支援具有不同配置的多種變體。OpenCode 附帶了流行供應商的內建預設變體。 +許多模型支援具有不同設定的多種變體。OpenCode 為熱門提供商內建了預設變體。 ### 內建變體 -OpenCode 附帶了許多供應商的預設變體: +OpenCode 為許多提供商提供了預設變體: **Anthropic**: @@ -152,25 +151,25 @@ OpenCode 附帶了許多供應商的預設變體: 因模型而異,但大致如下: -- `none` - 沒有推理 -- `minimal` - 最少的推理工作 -- `low` - 推理工作量低 -- `medium` - 中等推理工作量 -- `high` - 高推理能力 -- `xhigh` - 極高的推理能力 +- `none` - 無推理 +- `minimal` - 極少推理 +- `low` - 低推理 +- `medium` - 中等推理 +- `high` - 高推理 +- `xhigh` - 超高推理 -**Google**: +**Google**: -- `low` - 降低工作量/Tokens 預算 -- `high` - 更高的工作量/Tokens 預算 +- `low` - 較低推理/Token 預算 +- `high` - 較高推理/Token 預算 :::tip -該列表並不全面。許多其他供應商也有內建的預設值。 +此列表並不全面,許多其他提供商也有內建的預設變體。 ::: -### 自定義變體 +### 自訂變體 -您可以覆寫現有變體或添加您自己的變體: +您可以覆寫現有變體或新增自己的變體: ```jsonc title="opencode.jsonc" {7-18} { @@ -195,19 +194,19 @@ OpenCode 附帶了許多供應商的預設變體: } ``` -### 循環變體 +### 切換變體 -使用鍵綁定 `variant_cycle` 在變體之間快速切換。 [了解更多](/docs/keybinds)。 +使用快捷鍵 `variant_cycle` 可以快速在變體之間切換。[了解更多](/docs/keybinds)。 --- ## 載入模型 -當 OpenCode 啟動時,它會按以下優先順序檢查模型: +OpenCode 啟動時,會按以下優先順序載入模型: -1. `--model` 或 `-m` 命令列旗標。格式與設定檔中的相同:`provider_id/model_id`。 +1. `--model` 或 `-m` 命令列旗標。格式與設定檔中相同:`provider_id/model_id`。 -2. OpenCode 配置中的模型列表。 +2. OpenCode 設定中的 model 欄位。 ```json title="opencode.json" { @@ -216,8 +215,8 @@ OpenCode 附帶了許多供應商的預設變體: } ``` - 這裡的格式是 `provider/model`。 + 格式為 `provider/model`。 -3. 最後使用的模型。 +3. 上次使用的模型。 -4. 第一個模型使用內部優先順序。 +4. 按內部優先順序排列的第一個可用模型。 diff --git a/packages/web/src/content/docs/zh-tw/modes.mdx b/packages/web/src/content/docs/zh-tw/modes.mdx index 0d44dc2dd03f..73e6bd141cea 100644 --- a/packages/web/src/content/docs/zh-tw/modes.mdx +++ b/packages/web/src/content/docs/zh-tw/modes.mdx @@ -1,62 +1,60 @@ --- title: 模式 -description: 不同的模式適用於不同的使用案例。 +description: 不同模式適用於不同的使用情境。 --- :::caution -現在透過 opencode 設定中的 `agent` 選項配置模式。這 -`mode` 選項現已棄用。 [了解更多](/docs/agents)。 +模式現在透過 opencode 設定中的 `agent` 選項進行設定。`mode` 選項已廢棄。[了解更多](/docs/agents)。 ::: -opencode 中的模式允許您自定義不同使用案例的行為、工具和提示。 +opencode 中的模式允許您為不同的使用情境自訂行為、工具和提示詞。 -它具有兩種內建模式:**建置 (Build)**和**計畫 (Plan)**。您可以自定義 -這些或透過 opencode 設定配置您自己的。 +opencode 自帶兩種內建模式:**build** 和 **plan**。您可以自訂這些模式,也可以透過 opencode 設定建立自己的模式。 -您可以在工作階段期間在模式之間切換或在設定檔中配置它們。 +您可以在工作階段中切換模式,也可以在設定檔中進行設定。 --- -## 內建 +## 內建模式 -opencode 有兩種內建模式。 +opencode 自帶兩種內建模式。 --- -### 建置 (Build) +### Build -建置是啟用所有工具的**預設**模式。這是開發工作的標準模式,您需要完全存取檔案操作和系統指令。 +Build 是啟用了所有工具的**預設**模式。這是進行開發工作的標準模式,您可以完全存取檔案操作和系統指令。 --- -### 計畫 (Plan) +### Plan -專為規劃和分析而設計的受限模式。在計畫模式下,預設情況下禁用以下工具: +Plan 是一種為規劃和分析設計的受限模式。在 plan 模式下,以下工具預設被停用: - `write` - 無法建立新檔案 -- `edit` - 無法修改現有檔案,位於 `.opencode/plans/*.md` 的用於詳細說明計畫本身的檔案除外 -- `patch` - 無法套用 Patch +- `edit` - 無法修改現有檔案,但位於 `.opencode/plans/*.md` 的檔案除外,用於詳細說明計畫本身 +- `patch` - 無法套用補丁 - `bash` - 無法執行 shell 指令 -當您希望 AI 分析程式碼、建議變更或建立計畫而不對程式碼庫進行任何實際修改時,此模式非常有用。 +當您希望 AI 分析程式碼、提出修改建議或制定計畫,而不對程式碼庫進行任何實際更改時,此模式非常有用。 --- ## 切換 -您可以在工作階段期間使用 _Tab_ 鍵在模式之間切換。或者您配置的 `switch_mode` 鍵綁定。 +您可以在工作階段中使用 _Tab_ 鍵切換模式,或者使用您設定的 `switch_mode` 快捷鍵。 -另請參閱:[格式化程式](/docs/formatters) 有關程式碼格式配置的資訊。 +另請參閱:[格式化工具](/docs/formatters)了解程式碼格式化設定的相關資訊。 --- ## 設定 -您可以自定義內建模式或透過配置建立自己的模式。可以透過兩種方式配置模式: +您可以自訂內建模式或透過設定建立自己的模式。模式可以透過兩種方式進行設定: -### JSON 配置 +### JSON 設定 -在 `opencode.json` 設定檔中配置模式: +在 `opencode.json` 設定檔中設定模式: ```json title="opencode.json" { @@ -83,9 +81,9 @@ opencode 有兩種內建模式。 } ``` -### Markdown 配置 +### Markdown 設定 -您還可以使用 Markdown 檔案定義模式。將它們放入: +您還可以使用 Markdown 檔案定義模式。將檔案放置在以下位置: - 全域:`~/.config/opencode/modes/` - 專案:`.opencode/modes/` @@ -110,15 +108,15 @@ You are in code review mode. Focus on: Provide constructive feedback without making direct changes. ``` -Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式)。 +Markdown 檔案名稱即為模式名稱(例如,`review.md` 建立一個名為 `review` 的模式)。 -讓我們詳細看看這些配置選項。 +下面讓我們詳細了解這些設定選項。 --- -### 模型 (Model) +### 模型 -使用 `model` 配置覆寫此模式的預設模型。對於使用針對不同任務最佳化的不同模型很有用。例如,更快的規劃模型、更強大的實作模型。 +使用 `model` 設定可以覆寫該模式的預設模型。這對於針對不同任務使用不同模型非常有用。例如,規劃時使用更快的模型,實作時使用更強大的模型。 ```json title="opencode.json" { @@ -132,9 +130,9 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 --- -### 溫度 (Temperature) +### 溫度 -使用 `temperature` 配置控制 AI 回應的隨機性和創造性。較低的值使回應更加集中和確定,而較高的值則增加創造力和可變性。 +使用 `temperature` 設定控制 AI 回應的隨機性和創造性。較低的值使回應更加集中和確定性,較高的值則增加創造性和多樣性。 ```json title="opencode.json" { @@ -151,9 +149,9 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 溫度值的範圍通常為 0.0 到 1.0: -- **0.0-0.2**:非常集中且確定的回應,非常適合程式碼分析和規劃 -- **0.3-0.5**:具有一定創造力的平衡回應,適合一般開發任務 -- **0.6-1.0**:更有創意和多樣化的反應,有助於腦力激盪和探索 +- **0.0-0.2**:非常集中且確定性高的回應,適合程式碼分析和規劃 +- **0.3-0.5**:兼顧穩定性與創造力的平衡型回應,適合一般開發任務 +- **0.6-1.0**:更具創造性和多樣性的回應,適合腦力激盪和探索性工作 ```json title="opencode.json" { @@ -173,13 +171,13 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 } ``` -如果未指定溫度,opencode 將使用特定於模型的預設值(大多數模型通常為 0,Qwen 模型為 0.55)。 +如果未指定溫度,opencode 將使用模型特定的預設值(大多數模型通常為 0,Qwen 模型為 0.55)。 --- -### 提示 (Prompt) +### 提示詞 -使用 `prompt` 配置為此模式指定自定義系統提示檔案。提示檔案應包含特定於該模式用途的指令。 +使用 `prompt` 設定為模式指定自訂系統提示詞檔案。提示詞檔案應包含針對該模式用途的具體指令。 ```json title="opencode.json" { @@ -191,14 +189,13 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 } ``` -該路徑是相對於設定檔所在位置的。所以這適用於 -全域 opencode 配置和專案特定配置。 +此路徑相對於設定檔所在位置。因此,全域 opencode 設定和專案特定設定均可使用。 --- -### 工具 (Tools) +### 工具 -使用 `tools` 配置控制在此模式下可用的工具。您可以透過將特定工具設定為 `true` 或 `false` 來啟用或禁用特定工具。 +使用 `tools` 設定控制該模式下可用的工具。您可以將特定工具設定為 `true` 或 `false` 來啟用或停用它們。 ```json { @@ -223,7 +220,7 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 #### 可用工具 -這裡是所有可以透過模式配置控制的工具。 +以下是所有可透過模式設定控制的工具。 | 工具 | 描述 | | ----------- | ---------------- | @@ -234,18 +231,18 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 | `grep` | 搜尋檔案內容 | | `glob` | 按模式尋找檔案 | | `list` | 列出目錄內容 | -| `patch` | 對檔案套用 Patch | +| `patch` | 對檔案套用補丁 | | `todowrite` | 管理待辦事項清單 | -| `todoread` | 閱讀待辦事項清單 | -| `webfetch` | 取得網頁內容 | +| `todoread` | 讀取待辦事項清單 | +| `webfetch` | 擷取網頁內容 | --- -## 自定義模式 +## 自訂模式 -您可以透過將自定義模式添加到配置來建立自己的自定義模式。以下是使用這兩種方法的範例: +您可以透過在設定中新增自訂模式來建立自己的模式。以下是兩種方式的範例: -### 使用 JSON 配置 +### 使用 JSON 設定 ```json title="opencode.json" {4-14} { @@ -268,7 +265,7 @@ Markdown 檔名成為模式名稱(例如,`review.md` 建立 `review` 模式 ### 使用 Markdown 檔案 -在 `.opencode/modes/` 中為專案特定模式建立模式檔案,在 `~/.config/opencode/modes/` 中為全域模式建立模式檔案: +在 `.opencode/modes/` 中建立專案特定的模式檔案,或在 `~/.config/opencode/modes/` 中建立全域模式檔案: ```markdown title=".opencode/modes/debug.md" --- @@ -318,14 +315,14 @@ Priorities: --- -### 使用案例 +### 使用情境 -以下是不同模式的一些常見使用案例。 +以下是不同模式的一些常見使用情境。 -- **建置模式**:啟用所有工具的完整開發工作 -- **計畫模式**:分析和計畫,無需變更 -- **審閱模式**:使用唯讀存取權限和文件工具進行程式碼審閱 -- **除錯模式**:專注於啟用 bash 和讀取工具的調查 -- **文件模式**:使用檔案操作但不使用系統指令的文件編寫 +- **Build 模式**:啟用所有工具的完整開發工作 +- **Plan 模式**:分析和規劃,不做任何更改 +- **Review 模式**:使用唯讀存取權限加文件工具進行程式碼審查 +- **Debug 模式**:啟用 bash 和讀取工具,專注於問題排查 +- **Docs 模式**:支援檔案操作但不支援系統指令的文件編寫 -您可能還會發現不同的模型適用於不同的使用案例。 +您可能還會發現不同的模型適用於不同的使用情境。 diff --git a/packages/web/src/content/docs/zh-tw/network.mdx b/packages/web/src/content/docs/zh-tw/network.mdx index 1bd900dbb13e..dcb0eb9fcfde 100644 --- a/packages/web/src/content/docs/zh-tw/network.mdx +++ b/packages/web/src/content/docs/zh-tw/network.mdx @@ -1,15 +1,15 @@ --- title: 網路 -description: 配置代理伺服器和自定義憑證。 +description: 設定代理伺服器和自訂憑證。 --- -opencode 支援企業網路環境的標準代理環境變數和自定義憑證。 +OpenCode 支援標準代理環境變數和自訂憑證,適用於企業網路環境。 --- ## 代理伺服器 -opencode 遵循標準代理環境變數。 +OpenCode 遵循標準代理環境變數。 ```bash # HTTPS proxy (recommended) @@ -23,10 +23,10 @@ export NO_PROXY=localhost,127.0.0.1 ``` :::caution -TUI 與本地 HTTP 伺服器通訊。您必須繞過此連接的代理以防止路由迴圈。 +TUI 與本地 HTTP 伺服器進行通訊。您必須為此連線繞過代理,以防止路由迴圈。 ::: -您可以使用 [CLI 旗標](/docs/cli#run) 配置伺服器的連接埠和主機名稱。 +您可以使用 [CLI 旗標](/docs/cli#run)來設定伺服器的連接埠和主機名稱。 --- @@ -39,19 +39,19 @@ export HTTPS_PROXY=http://username:password@proxy.example.com:8080 ``` :::caution -避免對密碼進行寫死。使用環境變數或安全憑證儲存區。 +避免將密碼寫死在程式碼中。請使用環境變數或安全的憑證儲存方式。 ::: -對於需要高級身分驗證(例如 NTLM 或 Kerberos)的代理,請考慮使用支援您的身分驗證方法的 LLM 閘道。 +對於需要進階身分驗證(如 NTLM 或 Kerberos)的代理,建議使用支援相應身分驗證方式的 LLM 閘道。 --- -## 自定義憑證 +## 自訂憑證 -如果您的企業使用自定義 CA 進行 HTTPS 連接,請配置 opencode 以信任它們。 +如果您的企業使用自訂 CA 進行 HTTPS 連線,請設定 OpenCode 以信任這些憑證。 ```bash export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem ``` -這適用於代理連接和直接 API 存取。 +此設定同時適用於代理連線和直接 API 存取。 diff --git a/packages/web/src/content/docs/zh-tw/permissions.mdx b/packages/web/src/content/docs/zh-tw/permissions.mdx index 7f14b9ebc063..b2b43a209460 100644 --- a/packages/web/src/content/docs/zh-tw/permissions.mdx +++ b/packages/web/src/content/docs/zh-tw/permissions.mdx @@ -1,27 +1,27 @@ --- title: 權限 -description: 控制哪些操作需要批准才能執行。 +description: 控制哪些操作需要審批才能執行。 --- -opencode 使用 `permission` 配置來決定給定的操作是否應自動執行、提示您或被阻止。 +OpenCode 使用 `permission` 設定來決定某個操作是否應自動執行、提示您審批,還是被阻止。 -從 `v1.1.1` 開始,舊版 `tools` 布林配置已被棄用,並已合併到 `permission` 中。仍支援舊的 `tools` 配置以實現向後相容性。 +從 `v1.1.1` 開始,舊版 `tools` 布林設定已被廢棄,並已合併到 `permission` 中。舊版 `tools` 設定仍然支援,以保持向後相容。 --- ## 操作 -每個權限規則解析為以下之一: +每條權限規則解析為以下之一: -- `"allow"` — 未經批准執行 -- `"ask"` — 提示批准 +- `"allow"` — 無需審批直接執行 +- `"ask"` — 提示審批 - `"deny"` — 阻止該操作 --- -## 配置 +## 設定 -您可以全局設定權限(使用 `*`),並覆寫特定工具。 +您可以全域設定權限(使用 `*`),並覆寫特定工具的權限。 ```json title="opencode.json" { @@ -34,7 +34,7 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 } ``` -您還可以一次設定所有權限: +您還可以一次性設定所有權限: ```json title="opencode.json" { @@ -45,9 +45,9 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 --- -## 精細規則(物件語法) +## 細粒度規則(物件語法) -對於大多數權限,您可以使用物件根據工具輸入應用不同的操作。 +對於大多數權限,您可以使用物件來根據工具輸入套用不同的操作。 ```json title="opencode.json" { @@ -68,19 +68,19 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 } ``` -規則透過模式匹配進行評估,**最後匹配的規則獲勝**。常見的模式是將通用的 `"*"` 規則放在前面,然後再放置更具體的規則。 +規則透過模式比對進行評估,**最後比對的規則優先**。常見做法是將萬用的 `"*"` 規則放在最前面,更具體的規則放在後面。 -### 通配符 +### 萬用字元 -權限模式使用簡單的通配符匹配: +權限模式使用簡單的萬用字元比對: -- `*` 匹配零個或多個任意字元 -- `?` 恰好匹配一個字元 -- 所有其他字元均按字面意思匹配 +- `*` 比對零個或多個任意字元 +- `?` 精確比對一個字元 +- 所有其他字元按字面值比對 ### 主目錄展開 -您可以在模式開頭使用 `~` 或 `$HOME` 來引用您的主目錄。這對於 [`external_directory`](#external-directories) 規則特別有用。 +您可以在模式開頭使用 `~` 或 `$HOME` 來參照您的主目錄。這對於 [`external_directory`](#外部目錄) 規則特別有用。 - `~/projects/*` -> `/Users/username/projects/*` - `$HOME/projects/*` -> `/Users/username/projects/*` @@ -88,11 +88,11 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 ### 外部目錄 -使用 `external_directory` 允許工具呼叫存取啟動 opencode 的工作目錄之外的路徑。這適用於任何採用路徑作為輸入的工具(例如 `read`、`edit`、`list`、`glob`、`grep` 和許多 `bash` 指令)。 +使用 `external_directory` 允許工具呼叫存取 OpenCode 啟動時工作目錄之外的路徑。這適用於任何接受路徑作為輸入的工具(例如 `read`、`edit`、`list`、`glob`、`grep` 以及許多 `bash` 指令)。 -主目錄展開(如 `~/...`)僅影響模式的編寫方式。它不會使外部路徑成為當前工作空間的一部分,因此仍必須透過 `external_directory` 允許工作目錄之外的路徑。 +主目錄展開(如 `~/...`)僅影響模式的書寫方式。它不會將外部路徑納入當前工作空間,因此工作目錄之外的路徑仍然必須透過 `external_directory` 來允許。 -例如,這允許存取 `~/projects/personal/` 下的所有內容: +例如,以下設定允許存取 `~/projects/personal/` 下的所有內容: ```json title="opencode.json" { @@ -105,7 +105,7 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 } ``` -此處允許的任何目錄都會繼承與當前工作空間相同的預設值。自 [`read` 預設為 `allow`](#defaults) 起,也允許讀取 `external_directory` 下的項目,除非被覆寫。當工具應限制在這些路徑中時添加顯式規則,例如在保留讀取的同時阻止編輯: +此處允許的任何目錄都會繼承與當前工作空間相同的預設值。由於 [`read` 預設為 `allow`](#預設值),`external_directory` 下的項目也允許讀取,除非另行覆寫。當需要在這些路徑中限制某個工具時,請新增顯式規則,例如在保留讀取的同時阻止編輯: ```json title="opencode.json" { @@ -121,38 +121,38 @@ opencode 使用 `permission` 配置來決定給定的操作是否應自動執行 } ``` -將列表重點放在受信任的路徑上,並根據其他工具的需要疊加額外的允許或拒絕規則(例如 `bash`)。 +請將列表限定在受信任的路徑上,並根據需要為其他工具(例如 `bash`)疊加額外的允許或拒絕規則。 --- ## 可用權限 -opencode 權限由工具名稱和一些安全防護措施決定: +OpenCode 的權限以工具名稱為鍵,外加幾個安全防護項: -- `read` — 讀取檔案(與檔案路徑匹配) +- `read` — 讀取檔案(比對檔案路徑) - `edit` — 所有檔案修改(涵蓋 `edit`、`write`、`patch`、`multiedit`) -- `glob` — 檔案通配符(匹配通配符模式) -- `grep` — 內容搜尋(匹配正規表示式模式) -- `list` — 列出目錄中的檔案(與目錄路徑匹配) -- `bash` — 執行 shell 指令(匹配 `git status --porcelain` 等解析指令) -- `task` — 啟動子代理(與子代理類型匹配) -- `skill` — 載入技能(與技能名稱匹配) -- `lsp` — 執行 LSP 查詢(當前非精細) +- `glob` — 檔案萬用字元比對(比對萬用字元模式) +- `grep` — 內容搜尋(比對正規表示式模式) +- `list` — 列出目錄中的檔案(比對目錄路徑) +- `bash` — 執行 shell 指令(比對解析後的指令,如 `git status --porcelain`) +- `task` — 啟動子代理(比對子代理類型) +- `skill` — 載入技能(比對技能名稱) +- `lsp` — 執行 LSP 查詢(目前不支援細粒度設定) - `todoread`、`todowrite` — 讀取/更新待辦事項清單 -- `webfetch` — 獲取 URL(與 URL 匹配) -- `websearch`、`codesearch` — 網頁/程式碼搜尋(與查詢匹配) +- `webfetch` — 擷取 URL(比對 URL) +- `websearch`、`codesearch` — 網頁/程式碼搜尋(比對查詢內容) - `external_directory` — 當工具存取專案工作目錄之外的路徑時觸發 -- `doom_loop` — 當相同的工具呼叫使用相同的輸入重複 3 次時觸發 +- `doom_loop` — 當同一工具呼叫以相同輸入重複 3 次時觸發 --- ## 預設值 -如果您不指定任何內容,opencode 將從許可的預設值開始: +如果您未指定任何設定,OpenCode 將使用寬鬆的預設值: - 大多數權限預設為 `"allow"`。 - `doom_loop` 和 `external_directory` 預設為 `"ask"`。 -- `read` 是 `"allow"`,但 `.env` 檔案預設被拒絕: +- `read` 為 `"allow"`,但 `.env` 檔案預設被拒絕: ```json title="opencode.json" { @@ -169,24 +169,24 @@ opencode 權限由工具名稱和一些安全防護措施決定: --- -## 「問」(Ask) 的作用是什麼 +## "Ask"的作用 -當 opencode 提示批准時,UI 會提供三種結果: +當 OpenCode 提示審批時,介面提供三種選擇: -- `once` — 僅批准此請求 -- `always` — 批准與建議模式匹配的未來請求(對於當前 opencode 工作階段的其餘部分) +- `once` — 僅批准本次請求 +- `always` — 批准與建議模式比對的後續請求(在當前 OpenCode 工作階段的剩餘時間內有效) - `reject` — 拒絕請求 -`always` 將批准的模式集由該工具提供(例如,bash 批准通常將安全指令前綴(如 `git status*`)列入白名單)。 +`always` 所批准的模式集合由工具提供(例如,bash 審批通常會將安全的指令前綴如 `git status*` 加入白名單)。 --- ## 代理 -您可以覆寫每個代理的權限。代理權限與全局配置合併,代理規則優先。 [了解更多](/docs/agents#permissions) 關於代理權限。 +您可以為每個代理單獨覆寫權限。代理權限會與全域設定合併,且代理規則優先。[了解更多](/docs/agents#permissions)關於代理權限的內容。 :::note -有關更詳細的模式匹配範例,請參閱上面的 [精細規則(物件語法)](#granular-rules-object-syntax) 部分。 +有關更詳細的模式比對範例,請參閱上方的[細粒度規則(物件語法)](#細粒度規則物件語法)部分。 ::: ```json title="opencode.json" @@ -217,7 +217,7 @@ opencode 權限由工具名稱和一些安全防護措施決定: } ``` -您還可以在 Markdown 中配置代理權限: +您還可以在 Markdown 中設定代理權限: ```markdown title="~/.config/opencode/agents/review.md" --- @@ -233,5 +233,5 @@ Only analyze code and suggest changes. ``` :::tip -對帶有參數的指令使用模式匹配。 `"grep *"` 允許 `grep pattern file.txt`,而 `"grep"` 單獨會阻止它。像 `git status` 這樣的指令適用於預設行為,但在傳遞參數時需要顯式許可(如 `"git status *"`)。 +對帶參數的指令使用模式比對。`"grep *"` 允許執行 `grep pattern file.txt`,而單獨的 `"grep"` 則會阻止它。像 `git status` 這樣的指令適用於預設行為,但在傳遞參數時需要顯式權限(如 `"git status *"`)。 ::: diff --git a/packages/web/src/content/docs/zh-tw/plugins.mdx b/packages/web/src/content/docs/zh-tw/plugins.mdx index 1b586c3df6be..5e84f3a35302 100644 --- a/packages/web/src/content/docs/zh-tw/plugins.mdx +++ b/packages/web/src/content/docs/zh-tw/plugins.mdx @@ -1,21 +1,21 @@ --- title: 外掛 -description: 編寫您自己的外掛來擴展 opencode。 +description: 編寫自己的外掛來擴展 OpenCode。 --- -外掛允許您透過掛鉤各種事件和自定義行為來擴展 opencode。您可以建立外掛來新增新功能、與外部服務整合或修改 opencode 的預設行為。 +外掛允許您透過掛鉤各種事件和自訂行為來擴展 OpenCode。您可以建立外掛來新增新功能、整合外部服務,或修改 OpenCode 的預設行為。 -例如,查看社群建立的[外掛](/docs/ecosystem#plugins)。 +如需了解範例,請查看社群建立的[外掛](/docs/ecosystem#plugins)。 --- ## 使用外掛 -有兩種載入外掛的方法。 +有兩種方式載入外掛。 --- -### 從本地檔案 +### 從本地檔案載入 將 JavaScript 或 TypeScript 檔案放置在外掛目錄中。 @@ -26,7 +26,7 @@ description: 編寫您自己的外掛來擴展 opencode。 --- -### 來自 npm +### 從 npm 載入 在設定檔中指定 npm 套件。 @@ -37,43 +37,42 @@ description: 編寫您自己的外掛來擴展 opencode。 } ``` -支援常規和範圍 npm 套件。 +支援常規和帶作用域的 npm 套件。 瀏覽[生態系統](/docs/ecosystem#plugins)中的可用外掛。 --- -### 外掛是如何安裝的 +### 外掛的安裝方式 -**npm 外掛** 在啟動時使用 Bun 自動安裝。套件及其相依套件快取在 `~/.cache/opencode/node_modules/` 中。 +**npm 外掛**在啟動時使用 Bun 自動安裝。套件及其相依套件會快取在 `~/.cache/opencode/node_modules/` 中。 -**本地外掛**直接從外掛目錄載入。要使用外部套件,您必須在設定目錄中建立 `package.json`(請參閱[相依性](#dependencies)),或將外掛發佈到 npm 和[將其添加到您的設定中](/docs/config#plugins)。 +**本地外掛**直接從外掛目錄載入。如果需要使用外部套件,您必須在設定目錄中建立 `package.json`(參見[相依套件](#dependencies)),或者將外掛發布到 npm 並[將其新增到設定中](/docs/config#plugins)。 --- ### 載入順序 -外掛從所有來源載入,所有掛鉤按順序執行。載入順序為: +外掛從所有來源載入,所有鉤子按順序執行。載入順序為: 1. 全域設定 (`~/.config/opencode/opencode.json`) -2. 專案設定(`opencode.json`) +2. 專案設定 (`opencode.json`) 3. 全域外掛目錄 (`~/.config/opencode/plugins/`) -4. 專案外掛目錄(`.opencode/plugins/`) +4. 專案外掛目錄 (`.opencode/plugins/`) -具有相同名稱和版本的重複 npm 套件將被載入一次。但是,本地外掛和名稱相似的 npm 外掛都是分開載入的。 +名稱和版本相同的重複 npm 套件只會載入一次。但本地外掛和名稱相似的 npm 外掛會分別獨立載入。 --- -## 建立一個外掛 +## 建立外掛 -外掛是一個 **JavaScript/TypeScript 模組**,它匯出一個或多個外掛 -函式。每個函式接收一個上下文物件並返回一個掛鉤物件。 +外掛是一個 **JavaScript/TypeScript 模組**,它匯出一個或多個外掛函式。每個函式接收一個上下文物件,並回傳一個鉤子物件。 --- -### 相依性 +### 相依套件 -本地外掛和自定義工具可以使用外部 npm 套件。將 `package.json` 添加到您的設定目錄,其中包含您需要的相依套件。 +本地外掛和自訂工具可以使用外部 npm 套件。在設定目錄中新增一個 `package.json`,列出所需的相依套件。 ```json title=".opencode/package.json" { @@ -83,7 +82,7 @@ description: 編寫您自己的外掛來擴展 opencode。 } ``` -opencode 在啟動時執行 `bun install` 來安裝這些。然後您的外掛和工具就可以匯入它們。 +OpenCode 會在啟動時執行 `bun install` 來安裝這些相依套件。之後您的外掛和工具就可以匯入它們了。 ```ts title=".opencode/plugins/my-plugin.ts" import { escape } from "shescape" @@ -113,13 +112,13 @@ export const MyPlugin = async ({ project, client, $, directory, worktree }) => { } ``` -外掛函式接收: +外掛函式接收以下參數: - `project`:當前專案資訊。 - `directory`:當前工作目錄。 - `worktree`:git 工作樹路徑。 -- `client`:用於與 AI 互動的 opencode SDK 客戶端。 -- `$`:Bun 的 [shell API](https://bun.com/docs/runtime/shell) 用於執行指令。 +- `client`:用於與 AI 互動的 OpenCode SDK 用戶端。 +- `$`:Bun 的 [Shell API](https://bun.com/docs/runtime/shell),用於執行指令。 --- @@ -141,7 +140,7 @@ export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree ### 事件 -外掛可以訂閱事件,如下面的範例部分所示。以下是可用的不同事件的列表。 +外掛可以訂閱事件,如下方範例部分所示。以下是所有可用事件的列表。 #### 指令事件 @@ -188,7 +187,7 @@ export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree - `session.status` - `session.updated` -#### Todo 事件 +#### 待辦事項事件 - `todo.updated` @@ -211,13 +210,13 @@ export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree ## 範例 -以下是一些可用於擴展 opencode 的外掛範例。 +以下是一些可用於擴展 OpenCode 的外掛範例。 --- -### 發送通知 +### 傳送通知 -當某些事件發生時發送通知: +在特定事件發生時傳送通知: ```js title=".opencode/plugins/notification.js" export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => { @@ -232,17 +231,17 @@ export const NotificationPlugin = async ({ project, client, $, directory, worktr } ``` -我們使用 `osascript` 在 macOS 上執行 AppleScript。這裡我們用它來發送通知。 +這裡使用 `osascript` 在 macOS 上執行 AppleScript 來傳送通知。 :::note -如果您使用 opencode 桌面應用程式,它可以在回應準備就緒或工作階段出錯時自動發送系統通知。 +如果您使用 OpenCode 桌面應用程式,它可以在回應就緒或工作階段出錯時自動傳送系統通知。 ::: --- ### .env 保護 -阻止 opencode 讀取 `.env` 檔案: +阻止 OpenCode 讀取 `.env` 檔案: ```javascript title=".opencode/plugins/env-protection.js" export const EnvProtection = async ({ project, client, $, directory, worktree }) => { @@ -260,7 +259,7 @@ export const EnvProtection = async ({ project, client, $, directory, worktree }) ### 注入環境變數 -將環境變數注入所有 shell 執行(AI 工具和使用者終端機): +將環境變數注入所有 Shell 執行(AI 工具和使用者終端機): ```javascript title=".opencode/plugins/inject-env.js" export const InjectEnvPlugin = async () => { @@ -275,9 +274,9 @@ export const InjectEnvPlugin = async () => { --- -### 自定義工具 +### 自訂工具 -外掛還可以向 opencode 添加自定義工具: +外掛還可以為 OpenCode 新增自訂工具: ```ts title=".opencode/plugins/custom-tools.ts" import { type Plugin, tool } from "@opencode-ai/plugin" @@ -300,19 +299,19 @@ export const CustomToolsPlugin: Plugin = async (ctx) => { } ``` -`tool` 輔助程式建立一個 opencode 可以呼叫的自定義工具。它採用 Zod 模式函式並返回一個工具定義: +`tool` 輔助函式用於建立 OpenCode 可呼叫的自訂工具。它接受一個 Zod schema 函式,並回傳一個工具定義,包含: -- `description`:該工具的作用 -- `args`:工具參數的 Zod 模式 -- `execute`:呼叫工具時執行的函式 +- `description`:工具的功能描述 +- `args`:工具參數的 Zod schema +- `execute`:工具被呼叫時執行的函式 -您的自定義工具將可與內建工具一起用於 opencode。 +您的自訂工具將與內建工具一起在 OpenCode 中可用。 --- -### 記錄 +### 日誌記錄 -使用 `client.app.log()` 而不是 `console.log` 進行結構化記錄: +使用 `client.app.log()` 代替 `console.log` 進行結構化日誌記錄: ```ts title=".opencode/plugins/my-plugin.ts" export const MyPlugin = async ({ client }) => { @@ -327,13 +326,13 @@ export const MyPlugin = async ({ client }) => { } ``` -等級:`debug`、`info`、`warn`、`error`。詳情請參閱 [SDK 文件](https://opencode.ai/docs/sdk)。 +日誌層級:`debug`、`info`、`warn`、`error`。詳情請參閱 [SDK 文件](https://opencode.ai/docs/sdk)。 --- -### 壓縮掛鉤 +### 壓縮鉤子 -自定義壓縮工作階段時包含的上下文: +自訂工作階段壓縮時包含的上下文: ```ts title=".opencode/plugins/compaction.ts" import type { Plugin } from "@opencode-ai/plugin" @@ -343,7 +342,7 @@ export const CompactionPlugin: Plugin = async (ctx) => { "experimental.session.compacting": async (input, output) => { // Inject additional context into the compaction prompt output.context.push(` -## 自定義上下文 +## Custom Context Include any state that should persist across compaction: - Current task status @@ -355,9 +354,9 @@ Include any state that should persist across compaction: } ``` -`experimental.session.compacting` 掛鉤在 LLM 生成延續摘要之前觸發。使用它來注入預設壓縮提示會錯過的特定於域的上下文。 +`experimental.session.compacting` 鉤子在 LLM 生成續接摘要之前觸發。使用它來注入預設壓縮提示詞可能遺漏的領域特定上下文。 -您還可以透過設定 `output.prompt` 來完全替換壓縮提示: +您還可以透過設定 `output.prompt` 來完全替換壓縮提示詞: ```ts title=".opencode/plugins/custom-compaction.ts" import type { Plugin } from "@opencode-ai/plugin" @@ -382,4 +381,4 @@ Format as a structured prompt that a new agent can use to resume work. } ``` -當設定 `output.prompt` 時,它完全取代預設的壓縮提示。在這種情況下,`output.context` 陣列將被忽略。 +當設定了 `output.prompt` 時,它會完全替換預設的壓縮提示詞。在這種情況下,`output.context` 陣列將被忽略。 diff --git a/packages/web/src/content/docs/zh-tw/providers.mdx b/packages/web/src/content/docs/zh-tw/providers.mdx index 44bf90d99be5..12c4ded4e368 100644 --- a/packages/web/src/content/docs/zh-tw/providers.mdx +++ b/packages/web/src/content/docs/zh-tw/providers.mdx @@ -1,36 +1,36 @@ --- -title: 供應商 -description: 使用 OpenCode 中的任何 LLM 供應商。 +title: 提供商 +description: 在 OpenCode 中使用任意 LLM 提供商。 --- import config from "../../../../config.mjs" export const console = config.console -OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev) 來支援 **75+ LLM 供應商**,並且它支援執行本地模型。 +OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.dev),支援 **75+ LLM 提供商**,同時也支援執行本地模型。 -要添加供應商,您需要: +要新增提供商,您需要: -1. 使用 `/connect` 指令添加供應商的 API 金鑰。 -2. 在 OpenCode 配置中配置供應商。 +1. 使用 `/connect` 指令新增提供商的 API 金鑰。 +2. 在 OpenCode 設定中設定該提供商。 --- ### 憑證 -當您使用 `/connect` 指令添加供應商的 API 金鑰時,它們會被儲存 -在 `~/.local/share/opencode/auth.json`。 +使用 `/connect` 指令新增提供商的 API 金鑰後,憑證會儲存在 +`~/.local/share/opencode/auth.json` 中。 --- -### 配置 +### 設定 -您可以在 OpenCode 配置的 `provider` 部分自定義供應商。 +您可以透過 OpenCode 設定中的 `provider` 部分來自訂提供商。 --- -#### 基本網址 (Base URL) +#### 自訂 Base URL -您可以透過設定 `baseURL` 選項來自定義任何供應商的基本 URL。這在使用代理服務或自定義端點時非常有用。 +您可以透過設定 `baseURL` 選項來自訂任何提供商的 Base URL。這在使用代理服務或自訂端點時非常有用。 ```json title="opencode.json" {6} { @@ -49,11 +49,10 @@ OpenCode 使用 [AI SDK](https://ai-sdk.dev/) 和 [Models.dev](https://models.de ## OpenCode Zen -OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 -經過測試和驗證,可以與 OpenCode 良好配合。 [了解更多](/docs/zen)。 +OpenCode Zen 是由 OpenCode 團隊提供的模型列表,這些模型已經過測試和驗證,能夠與 OpenCode 良好配合使用。[了解更多](/docs/zen)。 :::tip -如果您是新手,我們建議您從 OpenCode Zen 開始。 +如果您是新使用者,我們建議從 OpenCode Zen 開始。 ::: 1. 在 TUI 中執行 `/connect` 指令,選擇 opencode,然後前往 [opencode.ai/auth](https://opencode.ai/auth)。 @@ -62,7 +61,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 /connect ``` -2. 登入,添加您的帳單詳細資訊,然後複製您的 API 金鑰。 +2. 登入後新增帳單資訊,然後複製您的 API 金鑰。 3. 貼上您的 API 金鑰。 @@ -73,30 +72,29 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 └ enter ``` -4. 在 TUI 中執行 `/models` 以查看我們推薦的模型列表。 +4. 在 TUI 中執行 `/models` 查看我們推薦的模型列表。 ```txt /models ``` -它的工作方式與 OpenCode 中的任何其他供應商一樣,並且完全可以選擇使用。 +它的使用方式與 OpenCode 中的其他提供商完全相同,且完全可選。 --- ## 目錄 -讓我們詳細了解一些供應商。如果您想將供應商添加到 -列表,請隨時打開 PR。 +下面我們來詳細了解一些提供商。如果您想將某個提供商新增到列表中,歡迎提交 PR。 :::note -在這裡沒有看到供應商?提交 PR。 +沒有看到您想要的提供商?歡迎提交 PR。 ::: --- ### 302.AI -1. 前往 [302.AI 主控台](https://302.ai/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [302.AI 控制台](https://302.ai/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **302.AI**。 @@ -123,18 +121,17 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Amazon Bedrock -要將 Amazon Bedrock 與 OpenCode 結合使用: +要在 OpenCode 中使用 Amazon Bedrock: -1. 前往 Amazon Bedrock 主控台中的 **Model catalog** 並請求 - 存取您想要的模型。 +1. 前往 Amazon Bedrock 控制台中的**模型目錄**,申請存取您想要使用的模型。 :::tip - 您需要能夠在 Amazon Bedrock 中存取所需的模型。 + 您需要先在 Amazon Bedrock 中取得對目標模型的存取權限。 ::: -2. **使用以下方法之一配置身分驗證**: +2. 使用以下方法之一**設定身分驗證**: - #### 環境變數(快速啟動) + #### 環境變數(快速上手) 執行 opencode 時設定以下環境變數之一: @@ -149,7 +146,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 AWS_BEARER_TOKEN_BEDROCK=XXX opencode ``` - 或者將它們添加到您的 bash 設定檔中: + 或者將它們新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export AWS_PROFILE=my-dev-profile @@ -158,7 +155,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 #### 設定檔(推薦) - 對於特定於專案或持久的配置,請使用 `opencode.json`: + 如需專案級別或持久化的設定,請使用 `opencode.json`: ```json title="opencode.json" { @@ -176,14 +173,14 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 **可用選項:** - `region` - AWS 區域(例如 `us-east-1`、`eu-west-1`) - - `profile` - 來自 `~/.aws/credentials` 的 AWS 命名設定檔 - - `endpoint` - 用於 VPC 端點的自定義端點 URL(通用 `baseURL` 選項的別名) + - `profile` - `~/.aws/credentials` 中的 AWS 命名設定檔 + - `endpoint` - VPC 端點的自訂端點 URL(通用 `baseURL` 選項的別名) :::tip - 設定檔選項優先於環境變數。 + 設定檔中的選項優先順序高於環境變數。 ::: - #### 高級:VPC 端點 + #### 進階:VPC 端點 如果您使用 Bedrock 的 VPC 端點: @@ -203,33 +200,33 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ``` :::note - `endpoint` 選項是通用 `baseURL` 選項的別名,使用 AWS 特定術語。如果同時指定 `endpoint` 和 `baseURL`,則 `endpoint` 優先。 + `endpoint` 選項是通用 `baseURL` 選項的別名,使用了 AWS 特有的術語。如果同時指定了 `endpoint` 和 `baseURL`,則 `endpoint` 優先。 ::: #### 認證方式 - - **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**:建立 IAM 使用者並在 AWS Console 中生成存取金鑰 - - **`AWS_PROFILE`**:使用 `~/.aws/credentials` 中的命名設定檔。首先配置 `aws configure --profile my-profile` 或 `aws sso login` - - **`AWS_BEARER_TOKEN_BEDROCK`**:從 Amazon Bedrock 主控台生成長期 API 金鑰 - - **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**:適用於 EKS IRSA(服務帳戶的 IAM 角色)或具有 OIDC 聯合的其他 Kubernetes 環境。使用服務帳戶註解時,這些環境變數由 Kubernetes 自動注入。 + - **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**:在 AWS 控制台中建立 IAM 使用者並產生存取金鑰 + - **`AWS_PROFILE`**:使用 `~/.aws/credentials` 中的命名設定檔。需要先透過 `aws configure --profile my-profile` 或 `aws sso login` 進行設定 + - **`AWS_BEARER_TOKEN_BEDROCK`**:從 Amazon Bedrock 控制台產生長期 API 金鑰 + - **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**:適用於 EKS IRSA(服務帳號的 IAM 角色)或其他支援 OIDC 聯合的 Kubernetes 環境。使用服務帳號註解時,Kubernetes 會自動注入這些環境變數。 - #### 認證優先級 + #### 認證優先順序 - Amazon Bedrock 使用以下身分驗證優先級: - 1. **Bearer Token** - `AWS_BEARER_TOKEN_BEDROCK` 環境變數或來自 `/connect` 指令的令牌 - 2. **AWS Credential Chain** - 設定檔、存取金鑰、共享憑證、IAM 角色、Web 身分令牌 (EKS IRSA)、實例元數據 + Amazon Bedrock 使用以下認證優先順序: + 1. **Bearer Token** - `AWS_BEARER_TOKEN_BEDROCK` 環境變數或透過 `/connect` 指令取得的 Token + 2. **AWS 憑證鏈** - 設定檔、存取金鑰、共享憑證、IAM 角色、Web Identity Token(EKS IRSA)、執行個體中繼資料 :::note - 當設定 Bearer Token 時(透過 `/connect` 或 `AWS_BEARER_TOKEN_BEDROCK`),它優先於所有 AWS 憑證方法(包括配置的設定檔)。 + 當設定了 Bearer Token(透過 `/connect` 或 `AWS_BEARER_TOKEN_BEDROCK`)時,它的優先順序高於所有 AWS 憑證方式,包括已設定的設定檔。 ::: -3. 執行 `/models` 指令選擇所需的模型。 +3. 執行 `/models` 指令選擇您想要的模型。 ```txt /models ``` :::note -對於自定義推理設定檔,請在鍵中使用模型和供應商名稱,並將 `id` 屬性設定為 arn。這確保了正確的快取: +對於自訂推理設定檔,請在 key 中使用模型名稱和提供商名稱,並將 `id` 屬性設定為 ARN。這可以確保正確的快取行為: ```json title="opencode.json" { @@ -253,14 +250,13 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Anthropic -1. 註冊後,執行 `/connect` 指令並選擇 Anthropic。 +1. 註冊完成後,執行 `/connect` 指令並選擇 Anthropic。 ```txt /connect ``` -2. 在這裡您可以選擇 **Claude Pro/Max** 選項,它將打開您的瀏覽器 - 並要求您進行身分驗證。 +2. 您可以選擇 **Claude Pro/Max** 選項,瀏覽器會自動開啟並要求您進行身分驗證。 ```txt ┌ Select auth method @@ -271,21 +267,21 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 └ ``` -3. 現在,當您使用 `/models` 指令時,所有 Anthropic 模型都應該可用。 +3. 現在使用 `/models` 指令即可看到所有 Anthropic 模型。 ```txt /models ``` :::info -[Anthropic](https://anthropic.com) 不正式支援在 OpenCode 中使用您的 Claude Pro/Max 訂閱。 +在 OpenCode 中使用 Claude Pro/Max 訂閱不是 [Anthropic](https://anthropic.com) 官方支援的用法。 ::: ##### 使用 API 金鑰 -如果您沒有 Pro/Max 訂閱,您還可以選擇 **Create an API Key**。它還會打開您的瀏覽器並要求您登入 Anthropic 並為您提供一個可以貼上到終端機中的代碼。 +如果您沒有 Pro/Max 訂閱,也可以選擇 **Create an API Key**。瀏覽器會自動開啟並要求您登入 Anthropic,然後會提供一個代碼供您貼上到終端機中。 -或者,如果您已有 API 金鑰,則可以選擇 **Manually enter API Key** 並將其貼上到您的終端機中。 +如果您已經有 API 金鑰,可以選擇 **Manually enter API Key** 並將其貼上到終端機中。 --- @@ -295,14 +291,14 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 如果遇到 "I'm sorry, but I cannot assist with that request" 錯誤,請嘗試將 Azure 資源中的內容篩選器從 **DefaultV2** 更改為 **Default**。 ::: -1. 轉到 [Azure portal](https://portal.azure.com/) 並建立 **Azure OpenAI** 資源。您需要: - - **Resource name**:這將成為您的 API 端點 (`https://RESOURCE_NAME.openai.azure.com/`) 的一部分 - - **API key**:來自您的資源的 `KEY 1` 或 `KEY 2` +1. 前往 [Azure 入口網站](https://portal.azure.com/)並建立 **Azure OpenAI** 資源。您需要: + - **資源名稱**:這會成為您的 API 端點的一部分(`https://RESOURCE_NAME.openai.azure.com/`) + - **API 金鑰**:資源中的 `KEY 1` 或 `KEY 2` -2. 轉到 [Azure AI Foundry](https://ai.azure.com/) 並部署模型。 +2. 前往 [Azure AI Foundry](https://ai.azure.com/) 並部署一個模型。 :::note - 部署名稱必須與模型名稱匹配,OpenCode 才能正常工作。 + 部署名稱必須與模型名稱一致,OpenCode 才能正常運作。 ::: 3. 執行 `/connect` 指令並搜尋 **Azure**。 @@ -320,19 +316,19 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 └ enter ``` -5. 將您的資源名稱設定為環境變數: +5. 將資源名稱設定為環境變數: ```bash AZURE_RESOURCE_NAME=XXX opencode ``` - 或者將其添加到您的 bash 設定檔中: + 或者新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export AZURE_RESOURCE_NAME=XXX ``` -6. 執行 `/models` 指令以選擇您部署的模型。 +6. 執行 `/models` 指令選擇您已部署的模型。 ```txt /models @@ -342,14 +338,14 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Azure Cognitive Services -1. 轉到 [Azure portal](https://portal.azure.com/) 並建立 **Azure OpenAI** 資源。您需要: - - **Resource name**:這將成為您的 API 端點 (`https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/`) 的一部分 - - **API key**:來自您的資源的 `KEY 1` 或 `KEY 2` +1. 前往 [Azure 入口網站](https://portal.azure.com/)並建立 **Azure OpenAI** 資源。您需要: + - **資源名稱**:這會成為您的 API 端點的一部分(`https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/`) + - **API 金鑰**:資源中的 `KEY 1` 或 `KEY 2` -2. 轉到 [Azure AI Foundry](https://ai.azure.com/) 並部署模型。 +2. 前往 [Azure AI Foundry](https://ai.azure.com/) 並部署一個模型。 :::note - 部署名稱必須與模型名稱匹配,OpenCode 才能正常工作。 + 部署名稱必須與模型名稱一致,OpenCode 才能正常運作。 ::: 3. 執行 `/connect` 指令並搜尋 **Azure Cognitive Services**。 @@ -367,19 +363,19 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 └ enter ``` -5. 將您的資源名稱設定為環境變數: +5. 將資源名稱設定為環境變數: ```bash AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX opencode ``` - 或者將其添加到您的 bash 設定檔中: + 或者新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME=XXX ``` -6. 執行 `/models` 指令以選擇您部署的模型。 +6. 執行 `/models` 指令選擇您已部署的模型。 ```txt /models @@ -389,7 +385,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Baseten -1. 前往 [Baseten](https://app.baseten.co/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Baseten](https://app.baseten.co/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Baseten**。 @@ -416,7 +412,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Cerebras -1. 前往 [Cerebras console](https://inference.cerebras.ai/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Cerebras 控制台](https://inference.cerebras.ai/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Cerebras**。 @@ -433,7 +429,7 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _Qwen 3 Coder 480B_。 +4. 執行 `/models` 指令選擇模型,例如 _Qwen 3 Coder 480B_。 ```txt /models @@ -443,9 +439,9 @@ OpenCode Zen 是 OpenCode 團隊提供的模型列表,這些模型已被 ### Cloudflare AI Gateway -Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthropic、Workers AI 等的模型。使用 [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/),您不需要為每個供應商提供單獨的 API 金鑰。 +Cloudflare AI Gateway 允許您透過統一端點存取來自 OpenAI、Anthropic、Workers AI 等提供商的模型。透過 [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/),您無需為每個提供商單獨準備 API 金鑰。 -1. 前往 [Cloudflare dashboard](https://dash.cloudflare.com/),導覽至 **AI** > **AI Gateway**,然後建立一個新閘道。 +1. 前往 [Cloudflare 儀表板](https://dash.cloudflare.com/),導覽到 **AI** > **AI Gateway**,建立一個新的閘道。 2. 將您的 Account ID 和 Gateway ID 設定為環境變數。 @@ -460,7 +456,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro /connect ``` -4. 輸入您的 Cloudflare API 令牌。 +4. 輸入您的 Cloudflare API Token。 ```txt ┌ API key @@ -481,7 +477,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro /models ``` - 您還可以透過 OpenCode 配置添加模型。 + 您也可以透過 OpenCode 設定新增模型。 ```json title="opencode.json" { @@ -501,7 +497,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### Cortecs -1. 前往 [Cortecs console](https://cortecs.ai/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Cortecs 控制台](https://cortecs.ai/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Cortecs**。 @@ -518,7 +514,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro └ enter ``` -4. 執行 `/models` 指令選擇一個模型,如 _Kimi K2 Instruct_。 +4. 執行 `/models` 指令選擇模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -528,7 +524,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### DeepSeek -1. 前往 [DeepSeek console](https://platform.deepseek.com/),建立一個帳戶,然後單擊 **Create new API key**。 +1. 前往 [DeepSeek 控制台](https://platform.deepseek.com/),建立帳號並點擊 **Create new API key**。 2. 執行 `/connect` 指令並搜尋 **DeepSeek**。 @@ -545,7 +541,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro └ enter ``` -4. 執行 `/models` 指令以選擇 DeepSeek 模型,例如 _DeepSeek Reasoner_。 +4. 執行 `/models` 指令選擇 DeepSeek 模型,例如 _DeepSeek Reasoner_。 ```txt /models @@ -555,7 +551,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### Deep Infra -1. 前往 [Deep Infra dashboard](https://deepinfra.com/dash),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Deep Infra 儀表板](https://deepinfra.com/dash),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Deep Infra**。 @@ -582,7 +578,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### Firmware -1. 前往 [Firmware dashboard](https://app.firmware.ai/signup),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Firmware 儀表板](https://app.firmware.ai/signup),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Firmware**。 @@ -609,7 +605,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### Fireworks AI -1. 前往 [Fireworks AI console](https://app.fireworks.ai/),建立一個帳戶,然後單擊 **Create API Key**。 +1. 前往 [Fireworks AI 控制台](https://app.fireworks.ai/),建立帳號並點擊 **Create API Key**。 2. 執行 `/connect` 指令並搜尋 **Fireworks AI**。 @@ -626,7 +622,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro └ enter ``` -4. 執行 `/models` 指令選擇一個模型,如 _Kimi K2 Instruct_。 +4. 執行 `/models` 指令選擇模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -636,7 +632,7 @@ Cloudflare AI Gateway 讓您可以透過統一端點存取來自 OpenAI、Anthro ### GitLab Duo -GitLab Duo 透過 GitLab 的 Anthropic 代理提供具有本機工具呼叫功能的 AI 驅動的代理聊天。 +GitLab Duo 透過 GitLab 的 Anthropic 代理提供具有原生工具呼叫能力的 AI 驅動代理聊天。 1. 執行 `/connect` 指令並選擇 GitLab。 @@ -644,50 +640,46 @@ GitLab Duo 透過 GitLab 的 Anthropic 代理提供具有本機工具呼叫功 /connect ``` -2. 選擇您的身分驗證方法: +2. 選擇您的身分驗證方式: ```txt ┌ Select auth method │ │ OAuth (Recommended) │ Personal Access Token - │ └ ``` #### 使用 OAuth(推薦) - 選擇 **OAuth**,您的瀏覽器將打開以進行授權。 + 選擇 **OAuth**,瀏覽器會自動開啟進行授權。 - #### 使用 Personal Access Token - 1. 前往 [GitLab User Settings > Access Tokens](https://gitlab.com/-/user_settings/personal_access_tokens) - 2. 單擊 **Add new token** - 3. Name:`OpenCode`,Scopes:`api` - 4. 複製令牌(以 `glpat-` 開頭) - 5. 在終端機中輸入它 + #### 使用個人存取權杖 + 1. 前往 [GitLab 使用者設定 > Access Tokens](https://gitlab.com/-/user_settings/personal_access_tokens) + 2. 點擊 **Add new token** + 3. 名稱填寫 `OpenCode`,範圍選擇 `api` + 4. 複製權杖(以 `glpat-` 開頭) + 5. 在終端機中輸入該權杖 -3. 執行 `/models` 指令以查看可用模型。 +3. 執行 `/models` 指令查看可用模型。 ```txt /models ``` - 提供三種基於 Claude 的模型: - - **duo-chat-haiku-4-5**(Default)- 快速任務的快速回應 - - **duo-chat-sonnet-4-5** - 大多數工作流程的平衡性能 - - **duo-chat-opus-4-5** - 最有能力進行複雜分析 + 提供三個基於 Claude 的模型: + - **duo-chat-haiku-4-5**(預設)- 快速回應,適合簡單任務 + - **duo-chat-sonnet-4-5** - 效能均衡,適合大多數工作流程 + - **duo-chat-opus-4-5** - 最強大,適合複雜分析 :::note -如果您不想將令牌儲存在 OpenCode auth 儲存中,也可以指定 `GITLAB_TOKEN` 環境變數。 +您也可以透過指定 `GITLAB_TOKEN` 環境變數來避免將權杖儲存在 OpenCode 的認證儲存中。 ::: -##### 自託管 GitLab +##### 自架 GitLab -:::note[合規性注意事項] -OpenCode 使用小型模型來執行某些 AI 任務,例如生成工作階段標題。 -預設情況下,它配置為使用 gpt-5-nano,由 Zen 託管。若要鎖定 OpenCode -僅使用您自己的 GitLab 託管實例,請將以下內容添加到您的 -`opencode.json` 檔案。還建議禁用工作階段分享。 +:::note[合規說明] +OpenCode 會使用一個小模型來執行部分 AI 任務,例如產生工作階段標題。預設情況下使用由 Zen 託管的 gpt-5-nano。如果您需要讓 OpenCode 僅使用您自己的 GitLab 託管實例,請在 `opencode.json` 檔案中新增以下內容。同時建議停用工作階段分享。 ```json { @@ -699,20 +691,20 @@ OpenCode 使用小型模型來執行某些 AI 任務,例如生成工作階段 ::: -對於自託管的 GitLab 實例: +對於自架 GitLab 實例: ```bash export GITLAB_INSTANCE_URL=https://gitlab.company.com export GITLAB_TOKEN=glpat-... ``` -如果您的實例執行自定義 AI 閘道: +如果您的實例執行了自訂 AI Gateway: ```bash GITLAB_AI_GATEWAY_URL=https://ai-gateway.company.com ``` -或者添加到您的 bash 設定檔中: +或者新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export GITLAB_INSTANCE_URL=https://gitlab.company.com @@ -723,33 +715,31 @@ export GITLAB_TOKEN=glpat-... :::note 您的 GitLab 管理員必須啟用以下功能: -1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) 用於使用者、組或實例 -2. Feature flags(透過 Rails console): +1. 為使用者、群組或實例啟用 [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) +2. 功能旗標(透過 Rails 控制台): - `agent_platform_claude_code` - `third_party_agents_enabled` ::: -##### 自託管實例的 OAuth +##### 自架實例的 OAuth -為了使 OAuth 適用於您的自託管實例,您需要建立 -一個新的應用程式(Settings → Applications),使用 -回調 URL `http://127.0.0.1:8080/callback` 和以下範圍: +要在自架實例上使用 OAuth,您需要建立一個新應用程式(設定 → 應用程式),回呼 URL 設定為 `http://127.0.0.1:8080/callback`,並選擇以下範圍: - api(代表您存取 API) - read_user(讀取您的個人資訊) - read_repository(允許對儲存庫進行唯讀存取) -然後將應用程式 ID 公開為環境變數: +然後將應用程式 ID 匯出為環境變數: ```bash export GITLAB_OAUTH_CLIENT_ID=your_application_id_here ``` -更多文件請參見 [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 主頁。 +更多文件請參閱 [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 首頁。 -##### 配置 +##### 設定 -透過 `opencode.json` 定制: +透過 `opencode.json` 進行自訂設定: ```json title="opencode.json" { @@ -768,9 +758,9 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here } ``` -##### GitLab API Tools(可選,但強烈推薦) +##### GitLab API 工具(可選,但強烈推薦) -要存取 GitLab 工具(merge requests、issues、pipelines、CI/CD 等): +要存取 GitLab 工具(合併請求、Issue、流水線、CI/CD 等): ```json title="opencode.json" { @@ -779,17 +769,16 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here } ``` -該外掛提供全面的 GitLab 儲存庫管理功能,包括 MR 審閱、問題追蹤、管線監控等。 +該外掛提供全面的 GitLab 儲存庫管理功能,包括 MR 審查、Issue 追蹤、流水線監控等。 --- ### GitHub Copilot -要將 GitHub Copilot 訂閱與 OpenCode 結合使用: +要在 OpenCode 中使用您的 GitHub Copilot 訂閱: :::note -某些模型可能需要 [Pro+ -subscription](https://github.com/features/copilot/plans) 來使用。 +部分模型可能需要 [Pro+ 訂閱](https://github.com/features/copilot/plans)才能使用。 ::: 1. 執行 `/connect` 指令並搜尋 GitHub Copilot。 @@ -798,7 +787,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 /connect ``` -2. 導覽至 [github.com/login/device](https://github.com/login/device) 並輸入代碼。 +2. 前往 [github.com/login/device](https://github.com/login/device) 並輸入驗證碼。 ```txt ┌ Login with GitHub Copilot @@ -810,7 +799,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 └ Waiting for authorization... ``` -3. 現在執行 `/models` 指令來選擇您想要的模型。 +3. 現在執行 `/models` 指令選擇您想要的模型。 ```txt /models @@ -820,29 +809,28 @@ subscription](https://github.com/features/copilot/plans) 來使用。 ### Google Vertex AI -要將 Google Vertex AI 與 OpenCode 結合使用: +要在 OpenCode 中使用 Google Vertex AI: -1. 前往 Google Cloud Console 中的 **Model Garden** 並檢查 - 您所在地區提供的模型。 +1. 前往 Google Cloud Console 中的**模型花園**,查看您所在區域可用的模型。 :::note - 您需要有一個啟用了 Vertex AI API 的 Google Cloud 專案。 + 您需要一個啟用了 Vertex AI API 的 Google Cloud 專案。 ::: 2. 設定所需的環境變數: - `GOOGLE_CLOUD_PROJECT`:您的 Google Cloud 專案 ID - `VERTEX_LOCATION`(可選):Vertex AI 的區域(預設為 `global`) - - 身分驗證(選擇一項): - - `GOOGLE_APPLICATION_CREDENTIALS`:Path to your service account JSON key file - - Authenticate using gcloud CLI: `gcloud auth application-default login` + - 身分驗證(選擇其一): + - `GOOGLE_APPLICATION_CREDENTIALS`:服務帳號 JSON 金鑰檔案的路徑 + - 使用 gcloud CLI 進行身分驗證:`gcloud auth application-default login` - 在執行 opencode 時設定它們。 + 在執行 opencode 時設定: ```bash GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json GOOGLE_CLOUD_PROJECT=your-project-id opencode ``` - 或者將它們添加到您的 bash 設定檔中。 + 或者新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json @@ -851,10 +839,10 @@ subscription](https://github.com/features/copilot/plans) 來使用。 ``` :::tip -`global` 區域無需額外成本即可提高可用性並減少錯誤。使用區域端點(例如 `us-central1`)來滿足資料駐留要求。 [了解更多](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models#regional_and_global_endpoints) +`global` 區域可以提高可用性並減少錯誤,且不會產生額外費用。如果有資料駐留需求,請使用區域端點(例如 `us-central1`)。[了解更多](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models#regional_and_global_endpoints) ::: -3. 執行 `/models` 指令選擇所需的模型。 +3. 執行 `/models` 指令選擇您想要的模型。 ```txt /models @@ -864,7 +852,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 ### Groq -1. 前往 [Groq console](https://console.groq.com/),單擊 **Create API Key**,然後複製金鑰。 +1. 前往 [Groq 控制台](https://console.groq.com/),點擊 **Create API Key** 並複製金鑰。 2. 執行 `/connect` 指令並搜尋 Groq。 @@ -872,7 +860,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 /connect ``` -3. 輸入供應商的 API 金鑰。 +3. 輸入該提供商的 API 金鑰。 ```txt ┌ API key @@ -881,7 +869,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 └ enter ``` -4. 執行 `/models` 指令選擇您想要的。 +4. 執行 `/models` 指令選擇您想要的模型。 ```txt /models @@ -891,9 +879,9 @@ subscription](https://github.com/features/copilot/plans) 來使用。 ### Hugging Face -[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers) 提供對超過 17 個供應商支援的開放模型的存取。 +[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers) 提供對由 17+ 提供商支援的開放模型的存取。 -1. 前往 [Hugging Face settings](https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained) 建立一個具有呼叫 Inference Providers 權限的令牌。 +1. 前往 [Hugging Face 設定](https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained),建立一個具有呼叫 Inference Providers 權限的權杖。 2. 執行 `/connect` 指令並搜尋 **Hugging Face**。 @@ -901,7 +889,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 /connect ``` -3. 輸入您的 Hugging Face 令牌。 +3. 輸入您的 Hugging Face 權杖。 ```txt ┌ API key @@ -910,7 +898,7 @@ subscription](https://github.com/features/copilot/plans) 來使用。 └ enter ``` -4. 執行 `/models` 指令來選擇模型,如 _Kimi-K2-Instruct_ 或 _GLM-4.6_。 +4. 執行 `/models` 指令選擇模型,例如 _Kimi-K2-Instruct_ 或 _GLM-4.6_。 ```txt /models @@ -920,9 +908,9 @@ subscription](https://github.com/features/copilot/plans) 來使用。 ### Helicone -[Helicone](https://helicone.ai) 是一個 LLM 可觀測性平台,可為您的 AI 應用程式提供記錄、監控和分析。 The Helicone AI Gateway 根據模型自動將您的請求路由到適當的供應商。 +[Helicone](https://helicone.ai) 是一個 LLM 可觀測性平台,為您的 AI 應用程式提供日誌記錄、監控和分析功能。Helicone AI Gateway 會根據模型自動將請求路由到對應的提供商。 -1. 前往 [Helicone](https://helicone.ai),建立一個帳戶,並從您的儀表板生成 API 金鑰。 +1. 前往 [Helicone](https://helicone.ai),建立帳號並在儀表板中產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Helicone**。 @@ -945,13 +933,13 @@ subscription](https://github.com/features/copilot/plans) 來使用。 /models ``` -有關更多供應商和高級功能(例如快取和速率限制),請查看 [Helicone documentation](https://docs.helicone.ai)。 +如需了解更多提供商以及快取、速率限制等進階功能,請查閱 [Helicone 文件](https://docs.helicone.ai)。 -#### 可選配置 +#### 可選設定 -如果您發現 Helicone 的功能或模型未透過 OpenCode 自動配置,您始終可以自行配置。 +如果 Helicone 的某些功能或模型未透過 OpenCode 自動設定,您隨時可以手動設定。 -這是 [Helicone's Model Directory](https://helicone.ai/models),您將需要它來獲取要添加的模型的 ID。 +[Helicone 模型目錄](https://helicone.ai/models)中可以找到您需要新增的模型 ID。 ```jsonc title="~/.config/opencode/opencode.jsonc" { @@ -977,9 +965,9 @@ subscription](https://github.com/features/copilot/plans) 來使用。 } ``` -#### 自定義標頭 +#### 自訂請求標頭 -Helicone 支援快取、使用者追蹤和工作階段管理等功能的自定義標頭。使用 `options.headers` 將它們添加到您的供應商配置中: +Helicone 支援用於快取、使用者追蹤和工作階段管理等功能的自訂請求標頭。使用 `options.headers` 將它們新增到提供商設定中: ```jsonc title="~/.config/opencode/opencode.jsonc" { @@ -1002,13 +990,13 @@ Helicone 支援快取、使用者追蹤和工作階段管理等功能的自定 ##### 工作階段追蹤 -Helicone 的 [Sessions](https://docs.helicone.ai/features/sessions) 功能可讓您將相關的 LLM 請求分組在一起。使用 [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) 外掛自動將每個 OpenCode 對話記錄為 Helicone 中的工作階段。 +Helicone 的 [Sessions](https://docs.helicone.ai/features/sessions) 功能允許您將相關的 LLM 請求歸為一組。使用 [opencode-helicone-session](https://github.com/H2Shami/opencode-helicone-session) 外掛可以自動將每個 OpenCode 對話記錄為 Helicone 中的一個工作階段。 ```bash npm install -g opencode-helicone-session ``` -將其添加到您的配置中。 +將其新增到設定中。 ```json title="opencode.json" { @@ -1016,24 +1004,24 @@ npm install -g opencode-helicone-session } ``` -該外掛將 `Helicone-Session-Id` 和 `Helicone-Session-Name` 標頭注入您的請求中。在 Helicone 的 Sessions 頁面中,您將看到每個 OpenCode 對話都列為單獨的工作階段。 +該外掛會在您的請求中注入 `Helicone-Session-Id` 和 `Helicone-Session-Name` 請求標頭。在 Helicone 的 Sessions 頁面中,您可以看到每個 OpenCode 對話都作為獨立的工作階段列出。 -##### 常見 Helicone 標頭 +##### 常用 Helicone 請求標頭 -| Header | Description | -| -------------------------- | ------------------------------------------------------ | -| `Helicone-Cache-Enabled` | 啟用回應快取 (`true`/`false`) | -| `Helicone-User-Id` | 按使用者追蹤指標 | -| `Helicone-Property-[Name]` | 添加自定義屬性(例如 `Helicone-Property-Environment`) | -| `Helicone-Prompt-Id` | 將請求與提示版本相關聯 | +| 請求標頭 | 描述 | +| -------------------------- | ---------------------------------------------------- | +| `Helicone-Cache-Enabled` | 啟用回應快取(`true`/`false`) | +| `Helicone-User-Id` | 按使用者追蹤指標 | +| `Helicone-Property-[Name]` | 新增自訂屬性(例如 `Helicone-Property-Environment`) | +| `Helicone-Prompt-Id` | 將請求與提示詞版本關聯 | -有關所有可用標頭,請參閱 [Helicone Header Directory](https://docs.helicone.ai/helicone-headers/header-directory)。 +有關所有可用請求標頭,請參閱 [Helicone Header Directory](https://docs.helicone.ai/helicone-headers/header-directory)。 --- ### llama.cpp -您可以透過 [llama.cpp's](https://github.com/ggml-org/llama.cpp) llama-server 公用程式配置 OpenCode 以使用本地模型 +您可以透過 [llama.cpp](https://github.com/ggml-org/llama.cpp) 的 llama-server 工具設定 OpenCode 使用本地模型。 ```json title="opencode.json" "llama.cpp" {5, 6, 8, 10-15} { @@ -1059,21 +1047,21 @@ npm install -g opencode-helicone-session } ``` -在這個例子中: +在這個範例中: -- `llama.cpp` 是自定義供應商 ID。這可以是您想要的任何字串。 -- `npm` 指定用於此供應商的套件。這裡,`@ai-sdk/openai-compatible` 用於任何 OpenAI 相容的 API。 -- `name` 是 UI 中供應商的顯示名稱。 -- `options.baseURL` 是本地伺服器的端點。 -- `models` 是模型 ID 與其配置的對應。模型名稱將顯示在模型選擇列表中。 +- `llama.cpp` 是自訂的提供商 ID,可以是任意字串。 +- `npm` 指定該提供商使用的套件。這裡使用 `@ai-sdk/openai-compatible` 來相容任何 OpenAI 相容的 API。 +- `name` 是該提供商在 UI 中顯示的名稱。 +- `options.baseURL` 是本地伺服器的端點位址。 +- `models` 是模型 ID 到其設定的對應。模型名稱會顯示在模型選擇列表中。 --- ### IO.NET -IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: +IO.NET 提供 17 個針對不同使用情境最佳化的模型: -1. 前往 [IO.NET console](https://ai.io.net/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [IO.NET 控制台](https://ai.io.net/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **IO.NET**。 @@ -1100,7 +1088,7 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: ### LM Studio -您可以透過 LM Studio 配置 OpenCode 以使用本地模型。 +您可以透過 LM Studio 設定 OpenCode 使用本地模型。 ```json title="opencode.json" "lmstudio" {5, 6, 8, 10-14} { @@ -1122,21 +1110,21 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: } ``` -在這個例子中: +在這個範例中: -- `lmstudio` 是自定義供應商 ID。這可以是您想要的任何字串。 -- `npm` 指定用於此供應商的套件。這裡,`@ai-sdk/openai-compatible` 用於任何 OpenAI 相容的 API。 -- `name` 是 UI 中供應商的顯示名稱。 -- `options.baseURL` 是本地伺服器的端點。 -- `models` 是模型 ID 與其配置的對應。模型名稱將顯示在模型選擇列表中。 +- `lmstudio` 是自訂的提供商 ID,可以是任意字串。 +- `npm` 指定該提供商使用的套件。這裡使用 `@ai-sdk/openai-compatible` 來相容任何 OpenAI 相容的 API。 +- `name` 是該提供商在 UI 中顯示的名稱。 +- `options.baseURL` 是本地伺服器的端點位址。 +- `models` 是模型 ID 到其設定的對應。模型名稱會顯示在模型選擇列表中。 --- ### Moonshot AI -要使用 Moonshot AI 中的 Kimi K2: +要使用 Moonshot AI 的 Kimi K2: -1. 前往 [Moonshot AI console](https://platform.moonshot.ai/console),建立一個帳戶,然後單擊 **Create API key**。 +1. 前往 [Moonshot AI 控制台](https://platform.moonshot.ai/console),建立帳號並點擊 **Create API key**。 2. 執行 `/connect` 指令並搜尋 **Moonshot AI**。 @@ -1163,7 +1151,7 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: ### MiniMax -1. 前往 [MiniMax API Console](https://platform.minimax.io/login),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [MiniMax API 控制台](https://platform.minimax.io/login),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **MiniMax**。 @@ -1180,7 +1168,7 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _M2.1_。 +4. 執行 `/models` 指令選擇模型,例如 _M2.1_。 ```txt /models @@ -1190,7 +1178,7 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: ### Nebius Token Factory -1. 前往 [Nebius Token Factory console](https://tokenfactory.nebius.com/),建立一個帳戶,然後單擊 **Add Key**。 +1. 前往 [Nebius Token Factory 控制台](https://tokenfactory.nebius.com/),建立帳號並點擊 **Add Key**。 2. 執行 `/connect` 指令並搜尋 **Nebius Token Factory**。 @@ -1207,7 +1195,7 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: └ enter ``` -4. 執行 `/models` 指令選擇一個模型,如 _Kimi K2 Instruct_。 +4. 執行 `/models` 指令選擇模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -1217,10 +1205,10 @@ IO.NET 提供了 17 種針對各種使用案例進行最佳化的模型: ### Ollama -您可以透過 Ollama 配置 OpenCode 以使用本地模型。 +您可以透過 Ollama 設定 OpenCode 使用本地模型。 :::tip -Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integration docs](https://docs.ollama.com/integrations/opencode)。 +Ollama 可以自動為 OpenCode 進行設定。詳見 [Ollama 整合文件](https://docs.ollama.com/integrations/opencode)。 ::: ```json title="opencode.json" "ollama" {5, 6, 8, 10-14} @@ -1243,29 +1231,29 @@ Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integrati } ``` -在這個例子中: +在這個範例中: -- `ollama` 是自定義供應商 ID。這可以是您想要的任何字串。 -- `npm` 指定用於此供應商的套件。這裡,`@ai-sdk/openai-compatible` 用於任何 OpenAI 相容的 API。 -- `name` 是 UI 中供應商的顯示名稱。 -- `options.baseURL` 是本地伺服器的端點。 -- `models` 是模型 ID 與其配置的對應。模型名稱將顯示在模型選擇列表中。 +- `ollama` 是自訂的提供商 ID,可以是任意字串。 +- `npm` 指定該提供商使用的套件。這裡使用 `@ai-sdk/openai-compatible` 來相容任何 OpenAI 相容的 API。 +- `name` 是該提供商在 UI 中顯示的名稱。 +- `options.baseURL` 是本地伺服器的端點位址。 +- `models` 是模型 ID 到其設定的對應。模型名稱會顯示在模型選擇列表中。 :::tip -如果工具呼叫不起作用,請嘗試增加 Ollama 中的 `num_ctx`。從 16k - 32k 左右開始。 +如果工具呼叫不運作,請嘗試增大 Ollama 中的 `num_ctx` 值。建議從 16k - 32k 左右開始。 ::: --- ### Ollama Cloud -要將 Ollama Cloud 與 OpenCode 結合使用: +要在 OpenCode 中使用 Ollama Cloud: -1. 前往 [https://ollama.com/](https://ollama.com/) 並登入或建立帳戶。 +1. 前往 [https://ollama.com/](https://ollama.com/) 登入或建立帳號。 -2. 導覽到 **Settings** > **Keys**,然後單擊 **Add API Key** 以生成新的 API 金鑰。 +2. 導覽到 **Settings** > **Keys**,點擊 **Add API Key** 產生新的 API 金鑰。 -3. 複製 API 金鑰以在 OpenCode 中使用。 +3. 複製 API 金鑰以便在 OpenCode 中使用。 4. 執行 `/connect` 指令並搜尋 **Ollama Cloud**。 @@ -1282,13 +1270,13 @@ Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integrati └ enter ``` -6. **Important**:在 OpenCode 中使用雲端模型之前,您必須將模型資訊拉取到本地: +6. **重要**:在 OpenCode 中使用雲端模型之前,必須先將模型資訊拉取到本地: ```bash ollama pull gpt-oss:20b-cloud ``` -7. 執行 `/models` 指令以選擇您的 Ollama Cloud 模型。 +7. 執行 `/models` 指令選擇您的 Ollama Cloud 模型。 ```txt /models @@ -1298,16 +1286,15 @@ Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integrati ### OpenAI -我們建議註冊 [ChatGPT Plus or Pro](https://chatgpt.com/pricing)。 +我們建議註冊 [ChatGPT Plus 或 Pro](https://chatgpt.com/pricing)。 -1. 註冊後,執行 `/connect` 指令並選擇 OpenAI。 +1. 註冊完成後,執行 `/connect` 指令並選擇 OpenAI。 ```txt /connect ``` -2. 在這裡您可以選擇 **ChatGPT Plus/Pro** 選項,它將打開您的瀏覽器 - 並要求您進行身分驗證。 +2. 您可以選擇 **ChatGPT Plus/Pro** 選項,瀏覽器會自動開啟並要求您進行身分驗證。 ```txt ┌ Select auth method @@ -1317,7 +1304,7 @@ Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integrati └ ``` -3. 現在,當您使用 `/models` 指令時,所有 OpenAI 模型都應該可用。 +3. 現在使用 `/models` 指令即可看到所有 OpenAI 模型。 ```txt /models @@ -1325,15 +1312,15 @@ Ollama 可以自動為 OpenCode 配置自身。詳情請參閱 [Ollama integrati ##### 使用 API 金鑰 -如果您已有 API 金鑰,可以選擇 **Manually enter API Key** 並將其貼上到您的終端機中。 +如果您已經有 API 金鑰,可以選擇 **Manually enter API Key** 並將其貼上到終端機中。 --- ### OpenCode Zen -OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 [了解更多](/docs/zen)。 +OpenCode Zen 是由 OpenCode 團隊提供的經過測試和驗證的模型列表。[了解更多](/docs/zen)。 -1. 登入 **OpenCode Zen** 並單擊 **Create API Key**。 +1. 登入 **OpenCode Zen** 並點擊 **Create API Key**。 2. 執行 `/connect` 指令並搜尋 **OpenCode Zen**。 @@ -1350,7 +1337,7 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _Qwen 3 Coder 480B_。 +4. 執行 `/models` 指令選擇模型,例如 _Qwen 3 Coder 480B_。 ```txt /models @@ -1360,7 +1347,7 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 ### OpenRouter -1. 前往 [OpenRouter dashboard](https://openrouter.ai/settings/keys),單擊 **Create API Key**,然後複製金鑰。 +1. 前往 [OpenRouter 儀表板](https://openrouter.ai/settings/keys),點擊 **Create API Key** 並複製金鑰。 2. 執行 `/connect` 指令並搜尋 OpenRouter。 @@ -1368,7 +1355,7 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 /connect ``` -3. 輸入供應商的 API 金鑰。 +3. 輸入該提供商的 API 金鑰。 ```txt ┌ API key @@ -1377,13 +1364,13 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 └ enter ``` -4. 許多 OpenRouter 模型是預設預先載入的,執行 `/models` 指令選擇您想要的模型。 +4. 預設已預先載入了許多 OpenRouter 模型,執行 `/models` 指令選擇您想要的模型。 ```txt /models ``` - 您還可以透過 OpenCode 配置添加其他模型。 + 您也可以透過 OpenCode 設定新增更多模型。 ```json title="opencode.json" {6} { @@ -1398,7 +1385,7 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 } ``` -5. 您還可以透過 OpenCode 配置自定義它們。這是指定供應商的範例 +5. 您還可以透過 OpenCode 設定自訂模型。以下是指定提供商的範例: ```json title="opencode.json" { @@ -1424,9 +1411,9 @@ OpenCode Zen 是 OpenCode 團隊提供的經過測試和驗證的模型列表。 ### SAP AI Core -SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、Meta、Mistral 和 AI21 的 40 多個模型的存取。 +SAP AI Core 透過統一平台提供對來自 OpenAI、Anthropic、Google、Amazon、Meta、Mistral 和 AI21 的 40+ 模型的存取。 -1. 轉到您的 [SAP BTP Cockpit](https://account.hana.ondemand.com/),導覽到您的 SAP AI Core 服務實例,然後建立一個服務金鑰。 +1. 前往 [SAP BTP Cockpit](https://account.hana.ondemand.com/),導覽到您的 SAP AI Core 服務實例,並建立服務金鑰。 :::tip 服務金鑰是一個包含 `clientid`、`clientsecret`、`url` 和 `serviceurls.AI_API_URL` 的 JSON 物件。您可以在 BTP Cockpit 的 **Services** > **Instances and Subscriptions** 下找到您的 AI Core 實例。 @@ -1453,13 +1440,13 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"...","serviceurls":{"AI_API_URL":"..."}}' opencode ``` - 或者將其添加到您的 bash 設定檔中: + 或者新增到您的 bash 設定檔中: ```bash title="~/.bash_profile" export AICORE_SERVICE_KEY='{"clientid":"...","clientsecret":"...","url":"...","serviceurls":{"AI_API_URL":"..."}}' ``` -4. (可選)設定部署 ID 和資源組: +4. 可選:設定部署 ID 和資源群組: ```bash AICORE_DEPLOYMENT_ID=your-deployment-id AICORE_RESOURCE_GROUP=your-resource-group opencode @@ -1469,7 +1456,40 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M 這些設定是可選的,應根據您的 SAP AI Core 設定進行配置。 ::: -5. 執行 `/models` 指令從 40 多個可用模型中進行選擇。 +5. 執行 `/models` 指令從 40+ 個可用模型中進行選擇。 + + ```txt + /models + ``` + +--- + +### STACKIT + +STACKIT AI Model Serving 提供完全託管的主權託管環境,專注於 Llama、Mistral 和 Qwen 等大語言模型,在歐洲基礎設施上實現最大程度的資料主權。 + +1. 前往 [STACKIT Portal](https://portal.stackit.cloud),導覽到 **AI Model Serving**,為您的專案建立認證權杖。 + + :::tip + 您需要先擁有 STACKIT 客戶帳號、使用者帳號和專案,才能建立認證權杖。 + ::: + +2. 執行 `/connect` 指令並搜尋 **STACKIT**。 + + ```txt + /connect + ``` + +3. 輸入您的 STACKIT AI Model Serving 認證權杖。 + + ```txt + ┌ API key + │ + │ + └ enter + ``` + +4. 執行 `/models` 指令選擇模型,例如 _Qwen3-VL 235B_ 或 _Llama 3.3 70B_。 ```txt /models @@ -1479,7 +1499,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M ### OVHcloud AI Endpoints -1. 前往 [OVHcloud panel](https://ovh.com/manager)。導覽到 `Public Cloud` 部分,`AI & Machine Learning` > `AI Endpoints`,然後在 `API Keys` 選項卡中單擊 **Create a new API key**。 +1. 前往 [OVHcloud 管理面板](https://ovh.com/manager)。導覽到 `Public Cloud` 部分,`AI & Machine Learning` > `AI Endpoints`,在 `API Keys` 分頁中點擊 **Create a new API key**。 2. 執行 `/connect` 指令並搜尋 **OVHcloud AI Endpoints**。 @@ -1496,7 +1516,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _gpt-oss-120b_。 +4. 執行 `/models` 指令選擇模型,例如 _gpt-oss-120b_。 ```txt /models @@ -1506,9 +1526,9 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M ### Scaleway -要將 [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-apis/) 與 OpenCode 一起使用: +要在 OpenCode 中使用 [Scaleway Generative APIs](https://www.scaleway.com/en/docs/generative-apis/): -1. 前往 [Scaleway Console IAM settings](https://console.scaleway.com/iam/api-keys) 生成新的 API 金鑰。 +1. 前往 [Scaleway Console IAM 設定](https://console.scaleway.com/iam/api-keys)產生新的 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Scaleway**。 @@ -1525,7 +1545,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _devstral-2-123b-instruct-2512_ 或 _gpt-oss-120b_。 +4. 執行 `/models` 指令選擇模型,例如 _devstral-2-123b-instruct-2512_ 或 _gpt-oss-120b_。 ```txt /models @@ -1535,7 +1555,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M ### Together AI -1. 前往 [Together AI console](https://api.together.ai),建立一個帳戶,然後單擊 **Add Key**。 +1. 前往 [Together AI 控制台](https://api.together.ai),建立帳號並點擊 **Add Key**。 2. 執行 `/connect` 指令並搜尋 **Together AI**。 @@ -1552,7 +1572,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M └ enter ``` -4. 執行 `/models` 指令選擇一個模型,如 _Kimi K2 Instruct_。 +4. 執行 `/models` 指令選擇模型,例如 _Kimi K2 Instruct_。 ```txt /models @@ -1562,7 +1582,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M ### Venice AI -1. 前往 [Venice AI console](https://venice.ai),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [Venice AI 控制台](https://venice.ai),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Venice AI**。 @@ -1579,7 +1599,7 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M └ enter ``` -4. 執行 `/models` 指令選擇模型,如 _Llama 3.3 70B_。 +4. 執行 `/models` 指令選擇模型,例如 _Llama 3.3 70B_。 ```txt /models @@ -1589,9 +1609,9 @@ SAP AI Core 透過統一平台提供對 OpenAI、Anthropic、Google、Amazon、M ### Vercel AI Gateway -Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、Google、xAI 等的模型。模型按定價提供,不加價。 +Vercel AI Gateway 允許您透過統一端點存取來自 OpenAI、Anthropic、Google、xAI 等提供商的模型。模型按原價提供,不額外加價。 -1. 前往 [Vercel dashboard](https://vercel.com/),導覽至 **AI Gateway** 選項卡,然後單擊 **API keys** 以建立新的 API 金鑰。 +1. 前往 [Vercel 儀表板](https://vercel.com/),導覽到 **AI Gateway** 分頁,點擊 **API keys** 建立新的 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **Vercel AI Gateway**。 @@ -1614,7 +1634,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G /models ``` -您還可以透過 OpenCode 配置自定義模型。以下是指定供應商路由順序的範例。 +您也可以透過 OpenCode 設定自訂模型。以下是指定提供商路由順序的範例。 ```json title="opencode.json" { @@ -1633,19 +1653,19 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G } ``` -一些有用的路由選項: +一些常用的路由選項: -| Option | Description | -| ------------------- | ---------------------------------------------------- | -| `order` | Provider sequence to try | -| `only` | Restrict to specific providers | -| `zeroDataRetention` | Only use providers with zero data retention policies | +| 選項 | 描述 | +| ------------------- | -------------------------------- | +| `order` | 提供商嘗試順序 | +| `only` | 限制為特定提供商 | +| `zeroDataRetention` | 僅使用具有零資料留存策略的提供商 | --- ### xAI -1. 前往 [xAI console](https://console.x.ai/),建立一個帳戶並生成一個 API 金鑰。 +1. 前往 [xAI 控制台](https://console.x.ai/),建立帳號並產生 API 金鑰。 2. 執行 `/connect` 指令並搜尋 **xAI**。 @@ -1662,7 +1682,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G └ enter ``` -4. 執行 `/models` 指令選擇一個模型,如 _Grok Beta_。 +4. 執行 `/models` 指令選擇模型,例如 _Grok Beta_。 ```txt /models @@ -1672,7 +1692,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G ### Z.AI -1. 前往 [Z.AI API console](https://z.ai/manage-apikey/apikey-list),建立一個帳戶,然後單擊 **Create a new API key**。 +1. 前往 [Z.AI API 控制台](https://z.ai/manage-apikey/apikey-list),建立帳號並點擊 **Create a new API key**。 2. 執行 `/connect` 指令並搜尋 **Z.AI**。 @@ -1691,7 +1711,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G └ enter ``` -4. 執行 `/models` 指令選擇 _GLM-4.7_ 等模型。 +4. 執行 `/models` 指令選擇模型,例如 _GLM-4.7_。 ```txt /models @@ -1701,7 +1721,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G ### ZenMux -1. 前往 [ZenMux dashboard](https://zenmux.ai/settings/keys),單擊 **Create API Key**,然後複製金鑰。 +1. 前往 [ZenMux 儀表板](https://zenmux.ai/settings/keys),點擊 **Create API Key** 並複製金鑰。 2. 執行 `/connect` 指令並搜尋 ZenMux。 @@ -1709,7 +1729,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G /connect ``` -3. 輸入供應商的 API 金鑰。 +3. 輸入該提供商的 API 金鑰。 ```txt ┌ API key @@ -1718,13 +1738,13 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G └ enter ``` -4. 預設情況下預先載入了許多 ZenMux 模型,執行 `/models` 指令選擇您想要的模型。 +4. 預設已預先載入了許多 ZenMux 模型,執行 `/models` 指令選擇您想要的模型。 ```txt /models ``` - 您還可以透過 OpenCode 配置添加其他模型。 + 您也可以透過 OpenCode 設定新增更多模型。 ```json title="opencode.json" {6} { @@ -1741,15 +1761,15 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G --- -## 自定義供應商 +## 自訂提供商 -要添加 `/connect` 指令中未列出的任何 **OpenAI-compatible** 供應商: +要新增 `/connect` 指令中未列出的任何 **OpenAI 相容**提供商: :::tip -您可以將任何與 OpenAI 相容的供應商與 OpenCode 結合使用。大多數現代 AI 供應商都提供與 OpenAI 相容的 API。 +您可以在 OpenCode 中使用任何 OpenAI 相容的提供商。大多數現代 AI 提供商都提供 OpenAI 相容的 API。 ::: -1. 執行 `/connect` 指令並向下捲動到 **Other**。 +1. 執行 `/connect` 指令,向下捲動到 **Other**。 ```bash $ /connect @@ -1762,7 +1782,7 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G └ ``` -2. 輸入供應商的唯一 ID。 +2. 輸入該提供商的唯一 ID。 ```bash $ /connect @@ -1775,10 +1795,10 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G ``` :::note - 選擇一個容易記住的 ID,您將在設定檔中使用它。 + 請選擇一個容易記住的 ID,您將在設定檔中使用它。 ::: -3. 輸入供應商的 API 金鑰。 +3. 輸入該提供商的 API 金鑰。 ```bash $ /connect @@ -1814,17 +1834,17 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G } ``` - 以下是配置選項: - - **npm**:要使用的 AI SDK 套件,`@ai-sdk/openai-compatible` 用於 OpenAI 相容供應商 - - **name**:UI 中的顯示名稱。 + 以下是設定選項說明: + - **npm**:要使用的 AI SDK 套件,對於 OpenAI 相容的提供商使用 `@ai-sdk/openai-compatible` + - **name**:在 UI 中顯示的名稱。 - **models**:可用模型。 - **options.baseURL**:API 端點 URL。 - - **options.apiKey**:如果不使用身分驗證,可以選擇設定 API 金鑰。 - - **options.headers**:可選擇設定自定義標頭。 + - **options.apiKey**:可選,如果不使用 auth 認證,可直接設定 API 金鑰。 + - **options.headers**:可選,設定自訂請求標頭。 - 有關高級選項的更多資訊,請參見下面的範例。 + 更多進階選項請參見下面的範例。 -5. 執行 `/models` 指令,您的自定義供應商和模型將出現在選擇列表中。 +5. 執行 `/models` 指令,您自訂的提供商和模型將出現在選擇列表中。 --- @@ -1860,27 +1880,26 @@ Vercel AI Gateway 可讓您透過統一端點存取來自 OpenAI、Anthropic、G } ``` -配置詳情: +設定詳情: -- **apiKey**:使用 `env` 變數語法 [了解更多](/docs/config#env-vars) 設定。 -- **headers**:隨每個請求發送的自定義標頭。 -- **limit.context**:模型接受的最大輸入 tokens。 -- **limit.output**:模型可以生成的最大 tokens。 +- **apiKey**:使用 `env` 變數語法設定,[了解更多](/docs/config#env-vars)。 +- **headers**:隨每個請求傳送的自訂請求標頭。 +- **limit.context**:模型接受的最大輸入 Token 數。 +- **limit.output**:模型可產生的最大 Token 數。 -`limit` 欄位允許 OpenCode 了解您還剩下多少上下文。標準供應商會自動從 models.dev 中提取這些內容。 +`limit` 欄位讓 OpenCode 了解您還剩餘多少上下文空間。標準提供商會自動從 models.dev 拉取這些資訊。 --- ## 疑難排解 -如果您在配置供應商時遇到問題,請檢查以下內容: +如果您在設定提供商時遇到問題,請檢查以下幾點: -1. **檢查 auth 設定**:執行 `opencode auth list` 以查看憑證是否 - 供應商的配置已添加到您的配置中。 +1. **檢查認證設定**:執行 `opencode auth list` 查看該提供商的憑證是否已新增到設定中。 - 這不適用於 Amazon Bedrock 等依賴環境變數進行身分驗證的供應商。 + 這不適用於 Amazon Bedrock 等依賴環境變數進行認證的提供商。 -2. 對於自定義供應商,請檢查 OpenCode 配置並: - - 確保 `/connect` 指令中使用的供應商 ID 與 OpenCode 配置中的 ID 匹配。 - - 正確的 npm 套件用於供應商。例如,對 Cerebras 使用 `@ai-sdk/cerebras`。對於所有其他 OpenAI 相容供應商,請使用 `@ai-sdk/openai-compatible`。 - - 檢查 `options.baseURL` 欄位中使用了正確的 API 端點。 +2. 對於自訂提供商,請檢查 OpenCode 設定並確認: + - `/connect` 指令中使用的提供商 ID 與 OpenCode 設定中的 ID 一致。 + - 使用了正確的 npm 套件。例如,Cerebras 應使用 `@ai-sdk/cerebras`。對於其他所有 OpenAI 相容的提供商,使用 `@ai-sdk/openai-compatible`。 + - `options.baseURL` 欄位中的 API 端點位址正確。 diff --git a/packages/web/src/content/docs/zh-tw/rules.mdx b/packages/web/src/content/docs/zh-tw/rules.mdx index 2ce0c327b696..705474c8a205 100644 --- a/packages/web/src/content/docs/zh-tw/rules.mdx +++ b/packages/web/src/content/docs/zh-tw/rules.mdx @@ -1,9 +1,9 @@ --- title: 規則 -description: 設定 opencode 的自定義指令。 +description: 為 opencode 設定自訂指令。 --- -您可以透過建立 `AGENTS.md` 檔案來提供 opencode 的自定義指令。這和 Cursor 的規則類似。它包含將包含在 LLM 上下文中的說明,以便為您的特定專案自定義其行為。 +您可以透過建立 `AGENTS.md` 檔案來為 opencode 提供自訂指令。這類似於 Cursor 的規則功能。該檔案包含的指令會被納入 LLM 的上下文中,以便針對您的特定專案自訂其行為。 --- @@ -15,15 +15,15 @@ description: 設定 opencode 的自定義指令。 您應該將專案的 `AGENTS.md` 檔案提交到 Git。 ::: -這將掃描您的專案及其所有內容,以了解該專案的內容並生成一個 `AGENTS.md` 檔案。這有助於 opencode 更好地導覽專案。 +該指令會掃描您的專案及其所有內容,了解專案的用途,並據此產生一個 `AGENTS.md` 檔案。這有助於 opencode 更好地導覽您的專案。 -如果您有現有的 `AGENTS.md` 檔案,這將嘗試添加到其中。 +如果您已有 `AGENTS.md` 檔案,該指令會嘗試在其基礎上進行補充。 --- ## 範例 -您也可以手動建立此檔案。以下是您可以放入 `AGENTS.md` 檔案中的一些內容的範例。 +您也可以手動建立此檔案。以下是一些可以放入 `AGENTS.md` 檔案中的內容範例。 ```markdown title="AGENTS.md" # SST v3 Monorepo Project @@ -48,33 +48,33 @@ This is an SST v3 monorepo with TypeScript. The project uses bun workspaces for - Import shared modules using workspace names: `@my-app/core/example` ``` -我們在此處添加特定於專案的說明,這將在您的團隊中共享。 +我們在這裡新增了專案特定的指令,這些指令會在您的團隊中共享。 --- ## 類型 -opencode 還支援從多個位置讀取 `AGENTS.md` 檔案。這有不同的目的。 +opencode 還支援從多個位置讀取 `AGENTS.md` 檔案,不同的位置有不同的用途。 -### 專案 +### 專案級 -將 `AGENTS.md` 放置在專案根目錄中以獲取特定於專案的規則。這些僅適用於您在此目錄或其子目錄中工作時。 +在專案根目錄放置一個 `AGENTS.md` 檔案,用於定義專案特定的規則。這些規則僅在您於該目錄或其子目錄中工作時生效。 -### 全域 +### 全域級 -您還可以在 `~/.config/opencode/AGENTS.md` 檔案中包含全域規則。這適用於所有 opencode 工作階段。 +您還可以在 `~/.config/opencode/AGENTS.md` 檔案中設定全域規則。這些規則會套用於所有 opencode 工作階段。 -由於這未提交給 Git 或與您的團隊共享,因此我們建議使用它來指定 LLM 應遵循的任何個人規則。 +由於該檔案不會被提交到 Git 或與團隊共享,我們建議用它來指定 LLM 應遵循的個人規則。 ### Claude Code 相容性 -對於從 Claude Code 遷移的使用者,opencode 支援 Claude Code 的檔案慣例作為備援: +對於從 Claude Code 遷移過來的使用者,OpenCode 支援 Claude Code 的檔案慣例作為備援方案: -- **專案規則**:專案目錄中的 `CLAUDE.md`(如果 `AGENTS.md` 不存在則使用) -- **全域規則**:`~/.claude/CLAUDE.md`(如果不存在 `~/.config/opencode/AGENTS.md` 則使用) -- **技能**:`~/.claude/skills/` — 詳情請參閱 [代理技能](/docs/skills/) +- **專案規則**:專案目錄中的 `CLAUDE.md`(在沒有 `AGENTS.md` 的情況下使用) +- **全域規則**:`~/.claude/CLAUDE.md`(在沒有 `~/.config/opencode/AGENTS.md` 的情況下使用) +- **技能**:`~/.claude/skills/` — 詳情請參閱[代理技能](/docs/skills/) -要禁用 Claude Code 相容性,請設定以下環境變數之一: +要停用 Claude Code 相容性,請設定以下環境變數之一: ```bash export OPENCODE_DISABLE_CLAUDE_CODE=1 # Disable all .claude support @@ -88,17 +88,17 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills 當 opencode 啟動時,它會按以下順序尋找規則檔案: -1. **本地檔案**,從當前目錄向上遍歷 (`AGENTS.md`,`CLAUDE.md`) -2. **全域檔案** `~/.config/opencode/AGENTS.md` -3. **Claude Code 檔案** 位於 `~/.claude/CLAUDE.md`(除非禁用) +1. **本機檔案**,從當前目錄向上遍歷(`AGENTS.md`、`CLAUDE.md`) +2. **全域檔案**,位於 `~/.config/opencode/AGENTS.md` +3. **Claude Code 檔案**,位於 `~/.claude/CLAUDE.md`(除非已停用) -第一個匹配的檔案在每個類別中獲勝。例如,如果您同時擁有 `AGENTS.md` 和 `CLAUDE.md`,則僅使用 `AGENTS.md`。同樣,`~/.config/opencode/AGENTS.md` 優先於 `~/.claude/CLAUDE.md`。 +在每個類別中,第一個符合的檔案優先。例如,如果您同時擁有 `AGENTS.md` 和 `CLAUDE.md`,則只會使用 `AGENTS.md`。同樣,`~/.config/opencode/AGENTS.md` 優先於 `~/.claude/CLAUDE.md`。 --- -## 自定義指令 +## 自訂指令 -您可以在 `opencode.json` 或全域 `~/.config/opencode/opencode.json` 中指定自定義指令檔案。這允許您和您的團隊重複使用現有規則,而不必將它們複製到 AGENTS.md。 +您可以在 `opencode.json` 或全域設定檔 `~/.config/opencode/opencode.json` 中指定自訂指令檔案。這允許您和團隊複用現有規則,而無需將它們複製到 AGENTS.md 中。 範例: @@ -109,7 +109,7 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -您還可以使用遠端 URL 從 Web 載入指令。 +您還可以使用遠端 URL 從網路載入指令。 ```json title="opencode.json" { @@ -118,19 +118,19 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -遠端指令的獲取有 5 秒的超時時間。 +遠端指令的擷取逾時時間為 5 秒。 -所有指令檔案均與您的 `AGENTS.md` 檔案合併。 +所有指令檔案都會與您的 `AGENTS.md` 檔案合併。 --- -## 引用外部檔案 +## 參照外部檔案 -雖然 opencode 不會自動解析 `AGENTS.md` 中的檔案引用,但您可以透過兩種方式實現類似的功能: +雖然 opencode 不會自動解析 `AGENTS.md` 中的檔案參照,但您可以透過以下兩種方式實現類似的功能: ### 使用 opencode.json -推薦的方法是在 `instructions` 中使用 `opencode.json` 欄位: +建議的方式是使用 `opencode.json` 中的 `instructions` 欄位: ```json title="opencode.json" { @@ -139,9 +139,9 @@ export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills } ``` -### AGENTS.md 中的手動說明 +### 在 AGENTS.md 中手動指定 -您可以透過在 `AGENTS.md` 中提供明確的指令來教 opencode 讀取外部檔案。這是一個實際的範例: +您可以在 `AGENTS.md` 中提供明確的指令,教 opencode 讀取外部檔案。以下是一個實際範例: ```markdown title="AGENTS.md" # TypeScript Project Rules @@ -168,13 +168,13 @@ For testing strategies and coverage requirements: @test/testing-guidelines.md Read the following file immediately as it's relevant to all workflows: @rules/general-guidelines.md. ``` -這種方法允許您: +這種方式允許您: -- 建立模組化、可重複使用的規則檔案 -- 透過符號連結或 git 子模組在專案之間共享規則 -- 保持 AGENTS.md 簡潔,同時參考詳細指南 -- 確保 opencode 僅在特定任務需要時載入檔案 +- 建立模組化、可複用的規則檔案 +- 透過符號連結或 Git 子模組在專案之間共享規則 +- 保持 AGENTS.md 簡潔,同時參照詳細的指南 +- 確保 opencode 僅在特定任務需要時才載入檔案 :::tip -對於 Monorepos 或具有共享標準的專案,使用 `opencode.json` 和 glob 模式(如 `packages/*/AGENTS.md`)比手動指令更易於維護。 +對於 monorepo 或具有共享標準的專案,使用 `opencode.json` 搭配 glob 模式(如 `packages/*/AGENTS.md`)比手動指定指令更易於維護。 ::: diff --git a/packages/web/src/content/docs/zh-tw/sdk.mdx b/packages/web/src/content/docs/zh-tw/sdk.mdx index 713718c9b3d9..2ed216d8b91a 100644 --- a/packages/web/src/content/docs/zh-tw/sdk.mdx +++ b/packages/web/src/content/docs/zh-tw/sdk.mdx @@ -1,15 +1,15 @@ --- title: SDK -description: opencode 伺服器的類型安全 JS 客戶端。 +description: opencode 伺服器的型別安全 JS 用戶端。 --- import config from "../../../../config.mjs" export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts` -opencode JS/TS SDK 提供類型安全的客戶端用於與伺服器互動。 -使用它以程式化方式構建整合和控制 opencode。 +opencode JS/TS SDK 提供了一個型別安全的用戶端,用於與伺服器進行互動。 +您可以用它來建構整合方案,並以程式化方式控制 opencode。 -[了解更多](/docs/server) 關於伺服器如何工作。例如,查看社群構建的[專案](/docs/ecosystem#projects)。 +[了解更多](/docs/server)關於伺服器的運作原理。如需範例,請查看社群建構的[專案](/docs/ecosystem#projects)。 --- @@ -23,9 +23,9 @@ npm install @opencode-ai/sdk --- -## 建立客戶端 +## 建立用戶端 -建立 opencode 的實例: +建立一個 opencode 實例: ```javascript import { createOpencode } from "@opencode-ai/sdk" @@ -33,23 +33,23 @@ import { createOpencode } from "@opencode-ai/sdk" const { client } = await createOpencode() ``` -這會同時啟動伺服器和客戶端 +這會同時啟動伺服器和用戶端。 #### 選項 -| 選項 | 類型 | 描述 | 預設 | -| ---------- | ------------- | ------------------------------ | ----------- | -| `hostname` | `string` | 伺服器主機名稱 | `127.0.0.1` | -| `port` | `number` | 伺服器連接埠 | `4096` | -| `signal` | `AbortSignal` | 取消的中止訊號 | `undefined` | -| `timeout` | `number` | 伺服器啟動超時(以毫秒為單位) | `5000` | -| `config` | `Config` | 設定物件 | `{}` | +| 選項 | 型別 | 描述 | 預設值 | +| ---------- | ------------- | -------------------------- | ----------- | +| `hostname` | `string` | 伺服器主機名稱 | `127.0.0.1` | +| `port` | `number` | 伺服器連接埠 | `4096` | +| `signal` | `AbortSignal` | 用於取消操作的中止訊號 | `undefined` | +| `timeout` | `number` | 伺服器啟動逾時時間(毫秒) | `5000` | +| `config` | `Config` | 設定物件 | `{}` | --- -## 配置 +## 設定 -您可以傳遞設定物件來自定義行為。該實例仍然會選擇您的 `opencode.json`,但您可以覆寫或添加內聯設定: +您可以傳入一個設定物件來自訂行為。實例仍然會讀取您的 `opencode.json`,但您可以透過內嵌方式覆寫或新增設定: ```javascript import { createOpencode } from "@opencode-ai/sdk" @@ -67,9 +67,9 @@ console.log(`Server running at ${opencode.server.url}`) opencode.server.close() ``` -## 僅限客戶端 +## 僅用戶端模式 -如果您已經有一個正在執行的 opencode 實例,您可以建立一個客戶端實例來連接到它: +如果您已經有一個正在執行的 opencode 實例,可以建立一個用戶端實例來連線: ```javascript import { createOpencodeClient } from "@opencode-ai/sdk" @@ -81,29 +81,29 @@ const client = createOpencodeClient({ #### 選項 -| 選項 | 類型 | 描述 | 預設 | +| 選項 | 型別 | 描述 | 預設值 | | --------------- | ---------- | ---------------------------- | ----------------------- | -| `baseUrl` | `string` | 伺服器的 URL | `http://localhost:4096` | -| `fetch` | `function` | 自定義 fetch 實作 | `globalThis.fetch` | -| `parseAs` | `string` | 回應解析方法 | `auto` | -| `responseStyle` | `string` | 返回樣式:`data` 或 `fields` | `fields` | -| `throwOnError` | `boolean` | 拋出錯誤而不是返回 | `false` | +| `baseUrl` | `string` | 伺服器 URL | `http://localhost:4096` | +| `fetch` | `function` | 自訂 fetch 實作 | `globalThis.fetch` | +| `parseAs` | `string` | 回應解析方式 | `auto` | +| `responseStyle` | `string` | 回傳風格:`data` 或 `fields` | `fields` | +| `throwOnError` | `boolean` | 拋出錯誤而非回傳錯誤 | `false` | --- -## 類型 +## 型別 -SDK 包含所有 API 類型的 TypeScript 定義。直接匯入它們: +SDK 包含所有 API 型別的 TypeScript 定義。您可以直接匯入它們: ```typescript import type { Session, Message, Part } from "@opencode-ai/sdk" ``` -所有類型均根據伺服器的 OpenAPI 規範生成,並可在 types 檔案中找到。 +所有型別均根據伺服器的 OpenAPI 規範產生,可在型別檔案中查看。 --- -## 錯誤 +## 錯誤處理 SDK 可能會拋出錯誤,您可以捕捉並處理這些錯誤: @@ -117,13 +117,85 @@ try { --- +## 結構化輸出 + +您可以透過指定帶有 JSON Schema 的 `format` 來請求模型回傳結構化的 JSON 輸出。模型會使用 `StructuredOutput` 工具回傳符合您 Schema 的經過驗證的 JSON。 + +### 基本用法 + +```typescript +const result = await client.session.prompt({ + path: { id: sessionId }, + body: { + parts: [{ type: "text", text: "Research Anthropic and provide company info" }], + format: { + type: "json_schema", + schema: { + type: "object", + properties: { + company: { type: "string", description: "Company name" }, + founded: { type: "number", description: "Year founded" }, + products: { + type: "array", + items: { type: "string" }, + description: "Main products", + }, + }, + required: ["company", "founded"], + }, + }, + }, +}) + +// Access the structured output +console.log(result.data.info.structured_output) +// { company: "Anthropic", founded: 2021, products: ["Claude", "Claude API"] } +``` + +### 輸出格式型別 + +| 型別 | 描述 | +| ------------- | --------------------------------------- | +| `text` | 預設值。標準文字回應(無結構化輸出) | +| `json_schema` | 回傳符合所提供 Schema 的經過驗證的 JSON | + +### JSON Schema 格式 + +使用 `type: 'json_schema'` 時,需提供以下欄位: + +| 欄位 | 型別 | 描述 | +| ------------ | --------------- | ------------------------------------- | +| `type` | `'json_schema'` | 必填。指定 JSON Schema 模式 | +| `schema` | `object` | 必填。定義輸出結構的 JSON Schema 物件 | +| `retryCount` | `number` | 選填。驗證重試次數(預設值:2) | + +### 錯誤處理 + +如果模型在所有重試後仍無法產生有效的結構化輸出,回應中會包含 `StructuredOutputError`: + +```typescript +if (result.data.info.error?.name === "StructuredOutputError") { + console.error("Failed to produce structured output:", result.data.info.error.message) + console.error("Attempts:", result.data.info.error.retries) +} +``` + +### 最佳實務 + +1. **在 Schema 屬性中提供清晰的描述**,幫助模型理解需要擷取的資料 +2. **使用 `required`** 指定哪些欄位必須存在 +3. **保持 Schema 簡潔** — 複雜的巢狀 Schema 可能會讓模型更難正確填充 +4. **設定合適的 `retryCount`** — 對於複雜 Schema 可增加重試次數,對於簡單 Schema 可減少 + +--- + ## API -SDK 透過類型安全的客戶端公開所有伺服器 API。 +SDK 透過型別安全的用戶端公開所有伺服器 API。 --- -### 全域 +### Global | 方法 | 描述 | 回應 | | ----------------- | ------------------------ | ------------------------------------ | @@ -140,11 +212,11 @@ console.log(health.data.version) --- -### 應用程式 +### App | 方法 | 描述 | 回應 | | -------------- | ------------------ | ------------------------------------------- | -| `app.log()` | 寫入日誌項目 | `boolean` | +| `app.log()` | 寫入一筆日誌 | `boolean` | | `app.agents()` | 列出所有可用的代理 | Agent[] | --- @@ -152,7 +224,7 @@ console.log(health.data.version) #### 範例 ```javascript -// Write a log entry +// 寫入一筆日誌 await client.app.log({ body: { service: "my-app", @@ -161,18 +233,18 @@ await client.app.log({ }, }) -// List available agents +// 列出可用的代理 const agents = await client.app.agents() ``` --- -### 專案 +### Project | 方法 | 描述 | 回應 | | ------------------- | ------------ | --------------------------------------------- | | `project.list()` | 列出所有專案 | Project[] | -| `project.current()` | 獲取當前專案 | Project | +| `project.current()` | 取得當前專案 | Project | --- @@ -188,28 +260,28 @@ const currentProject = await client.project.current() --- -### 路徑 +### Path | 方法 | 描述 | 回應 | | ------------ | ------------ | ---------------------------------------- | -| `path.get()` | 獲取當前路徑 | Path | +| `path.get()` | 取得當前路徑 | Path | --- #### 範例 ```javascript -// Get current path information +// 取得當前路徑資訊 const pathInfo = await client.path.get() ``` --- -### 配置 +### Config | 方法 | 描述 | 回應 | | -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | -| `config.get()` | 獲取設定資訊 | Config | +| `config.get()` | 取得設定資訊 | Config | | `config.providers()` | 列出供應商和預設模型 | `{ providers: `Provider[]`, default: { [key: string]: string } }` | --- @@ -224,29 +296,29 @@ const { providers, default: defaults } = await client.config.providers() --- -### 工作階段 - -| 方法 | 描述 | 備註 | -| ---------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | -| `session.list()` | 列出工作階段 | 返回 Session[] | -| `session.get({ path })` | 獲取工作階段 | 返回 Session | -| `session.children({ path })` | 列出子工作階段 | 返回 Session[] | -| `session.create({ body })` | 建立工作階段 | 返回 Session | -| `session.delete({ path })` | 刪除工作階段 | 返回 `boolean` | -| `session.update({ path, body })` | 更新工作階段屬性 | 返回 Session | -| `session.init({ path, body })` | 分析應用程式並建立 `AGENTS.md` | 返回 `boolean` | -| `session.abort({ path })` | 中止正在執行的工作階段 | 返回 `boolean` | -| `session.share({ path })` | 分享工作階段 | 返回 Session | -| `session.unshare({ path })` | 取消分享工作階段 | 返回 Session | -| `session.summarize({ path, body })` | 工作階段摘要 | 返回 `boolean` | -| `session.messages({ path })` | 列出工作階段中的訊息 | 返回 `{ info: `Message`, parts: `Part[]`}[]` | -| `session.message({ path })` | 獲取訊息詳情 | 返回 `{ info: `Message`, parts: `Part[]`}` | -| `session.prompt({ path, body })` | 發送提示訊息 | `body.noReply: true` 返回 UserMessage(僅上下文)。預設返回帶有 AI 回應的 AssistantMessage | -| `session.command({ path, body })` | 向工作階段發送指令 | 返回 `{ info: `AssistantMessage`, parts: `Part[]`}` | -| `session.shell({ path, body })` | 執行 shell 指令 | 返回 AssistantMessage | -| `session.revert({ path, body })` | 還原訊息 | 返回 Session | -| `session.unrevert({ path })` | 恢復已還原的訊息 | 返回 Session | -| `postSessionByIdPermissionsByPermissionId({ path, body })` | 回覆權限請求 | 返回 `boolean` | +### Sessions + +| 方法 | 描述 | 備註 | +| ---------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `session.list()` | 列出工作階段 | 回傳 Session[] | +| `session.get({ path })` | 取得工作階段 | 回傳 Session | +| `session.children({ path })` | 列出子工作階段 | 回傳 Session[] | +| `session.create({ body })` | 建立工作階段 | 回傳 Session | +| `session.delete({ path })` | 刪除工作階段 | 回傳 `boolean` | +| `session.update({ path, body })` | 更新工作階段屬性 | 回傳 Session | +| `session.init({ path, body })` | 分析應用程式並建立 `AGENTS.md` | 回傳 `boolean` | +| `session.abort({ path })` | 中止正在執行的工作階段 | 回傳 `boolean` | +| `session.share({ path })` | 分享工作階段 | 回傳 Session | +| `session.unshare({ path })` | 取消分享工作階段 | 回傳 Session | +| `session.summarize({ path, body })` | 摘要工作階段 | 回傳 `boolean` | +| `session.messages({ path })` | 列出工作階段中的訊息 | 回傳 `{ info: `Message`, parts: `Part[]`}[]` | +| `session.message({ path })` | 取得訊息詳情 | 回傳 `{ info: `Message`, parts: `Part[]`}` | +| `session.prompt({ path, body })` | 傳送提示訊息 | `body.noReply: true` 回傳 UserMessage(僅注入上下文)。預設回傳帶有 AI 回應的 AssistantMessage。支援透過 `body.outputFormat` 使用[結構化輸出](#結構化輸出) | +| `session.command({ path, body })` | 向工作階段傳送指令 | 回傳 `{ info: `AssistantMessage`, parts: `Part[]`}` | +| `session.shell({ path, body })` | 執行 shell 指令 | 回傳 AssistantMessage | +| `session.revert({ path, body })` | 還原訊息 | 回傳 Session | +| `session.unrevert({ path })` | 恢復已還原的訊息 | 回傳 Session | +| `postSessionByIdPermissionsByPermissionId({ path, body })` | 回覆權限請求 | 回傳 `boolean` | --- @@ -281,28 +353,28 @@ await client.session.prompt({ --- -### 檔案 +### Files | 方法 | 描述 | 回應 | | ------------------------- | -------------------- | ----------------------------------------------------------------------------------- | -| `find.text({ query })` | 搜尋檔案中的文字 | 具有 `path`, `lines`, `line_number`, `absolute_offset`, `submatches` 的匹配物件陣列 | +| `find.text({ query })` | 搜尋檔案中的文字 | 包含 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的比對物件陣列 | | `find.files({ query })` | 按名稱尋找檔案和目錄 | `string[]`(路徑) | | `find.symbols({ query })` | 尋找工作區符號 | Symbol[] | | `file.read({ query })` | 讀取檔案 | `{ type: "raw" \| "patch", content: string }` | -| `file.status({ query? })` | 獲取追蹤檔案的狀態 | File[] | +| `file.status({ query? })` | 取得已追蹤檔案的狀態 | File[] | -`find.files` 支援一些可選的查詢欄位: +`find.files` 支援以下選填的查詢欄位: - `type`:`"file"` 或 `"directory"` - `directory`:覆寫搜尋的專案根目錄 -- `limit`:最大結果 (1–200) +- `limit`:最大結果數(1–200) --- #### 範例 ```javascript -// Search and read files +// 搜尋和讀取檔案 const textResults = await client.find.text({ query: { pattern: "function.*opencode" }, }) @@ -326,13 +398,13 @@ const content = await client.file.read({ | 方法 | 描述 | 回應 | | ------------------------------ | ------------------ | --------- | -| `tui.appendPrompt({ body })` | 將文字附加到提示 | `boolean` | -| `tui.openHelp()` | 打開說明對話方塊 | `boolean` | -| `tui.openSessions()` | 打開工作階段選擇器 | `boolean` | -| `tui.openThemes()` | 打開主題選擇器 | `boolean` | -| `tui.openModels()` | 打開模型選擇器 | `boolean` | -| `tui.submitPrompt()` | 提交當前提示 | `boolean` | -| `tui.clearPrompt()` | 清除提示 | `boolean` | +| `tui.appendPrompt({ body })` | 向提示詞追加文字 | `boolean` | +| `tui.openHelp()` | 開啟說明對話框 | `boolean` | +| `tui.openSessions()` | 開啟工作階段選擇器 | `boolean` | +| `tui.openThemes()` | 開啟主題選擇器 | `boolean` | +| `tui.openModels()` | 開啟模型選擇器 | `boolean` | +| `tui.submitPrompt()` | 送出當前提示詞 | `boolean` | +| `tui.clearPrompt()` | 清除提示詞 | `boolean` | | `tui.executeCommand({ body })` | 執行指令 | `boolean` | | `tui.showToast({ body })` | 顯示 Toast 通知 | `boolean` | @@ -341,7 +413,7 @@ const content = await client.file.read({ #### 範例 ```javascript -// Control TUI interface +// 控制 TUI 介面 await client.tui.appendPrompt({ body: { text: "Add this to prompt" }, }) @@ -353,11 +425,11 @@ await client.tui.showToast({ --- -### 授權 +### Auth -| 方法 | 描述 | 回應 | -| ------------------- | ---------------- | --------- | -| `auth.set({ ... })` | 設定身分驗證憑證 | `boolean` | +| 方法 | 描述 | 回應 | +| ------------------- | ------------ | --------- | +| `auth.set({ ... })` | 設定驗證憑證 | `boolean` | --- @@ -372,11 +444,11 @@ await client.auth.set({ --- -### 事件 +### Events | 方法 | 描述 | 回應 | | ------------------- | -------------------- | -------------------- | -| `event.subscribe()` | 伺服器發送的事件串流 | 伺服器發送的事件串流 | +| `event.subscribe()` | 伺服器傳送的事件串流 | 伺服器傳送的事件串流 | --- diff --git a/packages/web/src/content/docs/zh-tw/server.mdx b/packages/web/src/content/docs/zh-tw/server.mdx index 1c038a59dd4b..411bc0eb7264 100644 --- a/packages/web/src/content/docs/zh-tw/server.mdx +++ b/packages/web/src/content/docs/zh-tw/server.mdx @@ -6,7 +6,7 @@ description: 透過 HTTP 與 opencode 伺服器互動。 import config from "../../../../config.mjs" export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts` -`opencode serve` 指令執行一個無介面 HTTP 伺服器,該伺服器公開 opencode 客戶端可以使用的 OpenAPI 端點。 +`opencode serve` 指令執行一個無介面的 HTTP 伺服器,暴露一個 OpenAPI 端點供 opencode 客戶端使用。 --- @@ -18,13 +18,13 @@ opencode serve [--port ] [--hostname ] [--cors ] #### 選項 -| 旗標 | 描述 | 預設 | -| --------------- | ------------------------- | ---------------- | -| `--port` | 監聽連接埠 | `4096` | -| `--hostname` | 監聽的主機名稱 | `127.0.0.1` | -| `--mdns` | 啟用 mDNS 探索 | `false` | -| `--mdns-domain` | mDNS 服務的自定義網域名稱 | `opencode.local` | -| `--cors` | 允許的其他瀏覽器來源 | `[]` | +| 旗標 | 描述 | 預設 | +| --------------- | ----------------------- | ---------------- | +| `--port` | 監聽連接埠 | `4096` | +| `--hostname` | 監聽的主機名稱 | `127.0.0.1` | +| `--mdns` | 啟用 mDNS 探索 | `false` | +| `--mdns-domain` | mDNS 服務的自訂網域名稱 | `opencode.local` | +| `--cors` | 額外允許的瀏覽器來源 | `[]` | `--cors` 可以多次傳遞: @@ -34,9 +34,9 @@ opencode serve --cors http://localhost:5173 --cors https://app.example.com --- -### 身分驗證 +### 認證 -設定 `OPENCODE_SERVER_PASSWORD` 以使用 HTTP 基本身分驗證保護伺服器。使用者名稱預設為 `opencode`,或設定 `OPENCODE_SERVER_USERNAME` 來覆蓋它。這適用於 `opencode serve` 和 `opencode web`。 +設定 `OPENCODE_SERVER_PASSWORD` 以使用 HTTP 基本認證保護伺服器。使用者名稱預設為 `opencode`,也可以設定 `OPENCODE_SERVER_USERNAME` 來覆蓋它。這適用於 `opencode serve` 和 `opencode web`。 ```bash OPENCODE_SERVER_PASSWORD=your-password opencode serve @@ -44,46 +44,43 @@ OPENCODE_SERVER_PASSWORD=your-password opencode serve --- -### 它是如何運作的 +### 工作原理 -當您執行 `opencode` 時,它會啟動 TUI 和伺服器。 TUI 是 -與伺服器對話的客戶端。伺服器公開 OpenAPI 3.1 規範 -端點。該端點還用於生成 [軟體開發套件](/docs/sdk)。 +當你執行 `opencode` 時,它會啟動一個 TUI 和一個伺服器。TUI 是與伺服器通訊的客戶端。伺服器暴露一個 OpenAPI 3.1 規範端點。該端點也用於產生 [SDK](/docs/sdk)。 :::tip -使用 opencode 伺服器以程式化方式與 opencode 進行互動。 +使用 opencode 伺服器以程式化方式與 opencode 互動。 ::: -此架構讓 opencode 支援多個客戶端,並允許您以程式化方式與 opencode 進行互動。 +這種架構讓 opencode 支援多個客戶端,並允許你以程式化方式與 opencode 互動。 -您可以執行 `opencode serve` 來啟動獨立伺服器。如果您有 -opencode TUI 執行,`opencode serve` 將啟動一個新伺服器。 +你可以執行 `opencode serve` 來啟動一個獨立的伺服器。如果你已經在執行 opencode TUI,`opencode serve` 會啟動一個新的伺服器。 --- #### 連接到現有伺服器 -當您啟動 TUI 時,它會隨機分配連接埠和主機名稱。您可以改為傳入 `--hostname` 和 `--port` [旗標](/docs/cli)。然後使用它連接到其伺服器。 +當你啟動 TUI 時,它會隨機分配連接埠和主機名稱。你也可以傳入 `--hostname` 和 `--port` [旗標](/docs/cli),然後用它來連接對應的伺服器。 -[`/tui`](#tui) 端點可用於透過伺服器驅動 TUI。例如,您可以預填充或執行提示。此設定由 opencode [IDE](/docs/ide) 外掛使用。 +[`/tui`](#tui) 端點可用於透過伺服器驅動 TUI。例如,你可以預填充或執行一個提示詞。此方式被 OpenCode [IDE](/docs/ide) 外掛所使用。 --- -## 規格 +## 規範 -伺服器發布了 OpenAPI 3.1 規範,可以在以下位置查看: +伺服器發布了一個 OpenAPI 3.1 規範,可在以下位址查看: ``` http://:/doc ``` -例如,`http://localhost:4096/doc`。使用規範生成客戶端或檢查請求和回應類型。或者在 Swagger 瀏覽器中查看它。 +例如,`http://localhost:4096/doc`。使用該規範可以產生客戶端或檢查請求和回應類型,也可以在 Swagger 瀏覽器中查看。 --- ## API -opencode 伺服器公開以下 API。 +opencode 伺服器暴露以下 API。 --- @@ -91,8 +88,8 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ----- | ---------------- | ------------------------ | ------------------------------------ | -| `GET` | `/global/health` | 獲取伺服器健康狀態和版本 | `{ healthy: true, version: string }` | -| `GET` | `/global/event` | 獲取全域事件(SSE 串流) | 事件串流 | +| `GET` | `/global/health` | 取得伺服器健康狀態和版本 | `{ healthy: true, version: string }` | +| `GET` | `/global/event` | 取得全域事件(SSE 串流) | 事件串流 | --- @@ -101,7 +98,7 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ----- | ------------------ | ------------ | --------------------------------------------- | | `GET` | `/project` | 列出所有專案 | Project[] | -| `GET` | `/project/current` | 獲取當前專案 | Project | +| `GET` | `/project/current` | 取得當前專案 | Project | --- @@ -109,8 +106,8 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ----- | ------- | ----------------------- | ------------------------------------------- | -| `GET` | `/path` | 獲取當前路徑 | Path | -| `GET` | `/vcs` | 獲取當前專案的 VCS 資訊 | VcsInfo | +| `GET` | `/path` | 取得當前路徑 | Path | +| `GET` | `/vcs` | 取得當前專案的 VCS 資訊 | VcsInfo | --- @@ -118,15 +115,15 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ------ | ------------------- | ------------ | --------- | -| `POST` | `/instance/dispose` | 處置當前實例 | `boolean` | +| `POST` | `/instance/dispose` | 銷毀當前實例 | `boolean` | --- -### 配置 +### 設定 | 方法 | 路徑 | 描述 | 回應 | | ------- | ------------------- | -------------------- | ---------------------------------------------------------------------------------------- | -| `GET` | `/config` | 獲取設定資訊 | Config | +| `GET` | `/config` | 取得設定資訊 | Config | | `PATCH` | `/config` | 更新設定 | Config | | `GET` | `/config/providers` | 列出供應商和預設模型 | `{ providers: `Provider[]`, default: { [key: string]: string } }` | @@ -137,47 +134,47 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ------ | -------------------------------- | ----------------------- | ----------------------------------------------------------------------------------- | | `GET` | `/provider` | 列出所有供應商 | `{ all: `Provider[]`, default: {...}, connected: string[] }` | -| `GET` | `/provider/auth` | 獲取供應商身分驗證方法 | `{ [providerID: string]: `ProviderAuthMethod[]` }` | +| `GET` | `/provider/auth` | 取得供應商認證方式 | `{ [providerID: string]: `ProviderAuthMethod[]` }` | | `POST` | `/provider/{id}/oauth/authorize` | 使用 OAuth 授權供應商 | ProviderAuthAuthorization | -| `POST` | `/provider/{id}/oauth/callback` | 處理供應商的 OAuth 回調 | `boolean` | +| `POST` | `/provider/{id}/oauth/callback` | 處理供應商的 OAuth 回呼 | `boolean` | --- ### 工作階段 -| 方法 | 路徑 | 描述 | 備註 | -| -------- | ---------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------- | -| `GET` | `/session` | 列出所有工作階段 | 返回 Session[] | -| `POST` | `/session` | 建立新工作階段 | 正文:`{ parentID?, title? }`,返回 Session | -| `GET` | `/session/status` | 獲取所有工作階段的狀態 | 返回 `{ [sessionID: string]: `SessionStatus` }` | -| `GET` | `/session/:id` | 獲取工作階段詳細資訊 | 返回 Session | -| `DELETE` | `/session/:id` | 刪除工作階段及其所有數據 | 返回 `boolean` | -| `PATCH` | `/session/:id` | 更新工作階段屬性 | 正文:`{ title? }`,返回 Session | -| `GET` | `/session/:id/children` | 獲取工作階段的子工作階段 | 返回 Session[] | -| `GET` | `/session/:id/todo` | 獲取工作階段的待辦事項清單 | 返回 Todo[] | -| `POST` | `/session/:id/init` | 分析應用程式並建立 `AGENTS.md` | 主體:`{ messageID, providerID, modelID }`,返回`boolean` | -| `POST` | `/session/:id/fork` | 在訊息中分岔現有工作階段 | 正文:`{ messageID? }`,返回 Session | -| `POST` | `/session/:id/abort` | 中止正在執行的工作階段 | 返回 `boolean` | -| `POST` | `/session/:id/share` | 分享工作階段 | 返回 Session | -| `DELETE` | `/session/:id/share` | 取消分享工作階段 | 返回 Session | -| `GET` | `/session/:id/diff` | 獲取本次工作階段的差異 | 查詢:`messageID?`,返回 FileDiff[] | -| `POST` | `/session/:id/summarize` | 工作階段摘要 | 主體:`{ providerID, modelID }`,返回`boolean` | -| `POST` | `/session/:id/revert` | 還原訊息 | 主體:`{ messageID, partID? }`,返回`boolean` | -| `POST` | `/session/:id/unrevert` | 恢復所有已還原的訊息 | 返回 `boolean` | -| `POST` | `/session/:id/permissions/:permissionID` | 回覆權限請求 | 主體:`{ response, remember? }`,返回`boolean` | +| 方法 | 路徑 | 描述 | 說明 | +| -------- | ---------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------- | +| `GET` | `/session` | 列出所有工作階段 | 回傳 Session[] | +| `POST` | `/session` | 建立新工作階段 | 請求主體:`{ parentID?, title? }`,回傳 Session | +| `GET` | `/session/status` | 取得所有工作階段的狀態 | 回傳 `{ [sessionID: string]: `SessionStatus` }` | +| `GET` | `/session/:id` | 取得工作階段詳情 | 回傳 Session | +| `DELETE` | `/session/:id` | 刪除工作階段及其所有資料 | 回傳 `boolean` | +| `PATCH` | `/session/:id` | 更新工作階段屬性 | 請求主體:`{ title? }`,回傳 Session | +| `GET` | `/session/:id/children` | 取得工作階段的子工作階段 | 回傳 Session[] | +| `GET` | `/session/:id/todo` | 取得工作階段的待辦事項清單 | 回傳 Todo[] | +| `POST` | `/session/:id/init` | 分析應用程式並建立 `AGENTS.md` | 請求主體:`{ messageID, providerID, modelID }`,回傳 `boolean` | +| `POST` | `/session/:id/fork` | 在某條訊息處分岔現有工作階段 | 請求主體:`{ messageID? }`,回傳 Session | +| `POST` | `/session/:id/abort` | 中止正在執行的工作階段 | 回傳 `boolean` | +| `POST` | `/session/:id/share` | 分享工作階段 | 回傳 Session | +| `DELETE` | `/session/:id/share` | 取消分享工作階段 | 回傳 Session | +| `GET` | `/session/:id/diff` | 取得本次工作階段的差異 | 查詢參數:`messageID?`,回傳 FileDiff[] | +| `POST` | `/session/:id/summarize` | 摘要工作階段 | 請求主體:`{ providerID, modelID }`,回傳 `boolean` | +| `POST` | `/session/:id/revert` | 還原訊息 | 請求主體:`{ messageID, partID? }`,回傳 `boolean` | +| `POST` | `/session/:id/unrevert` | 恢復所有已還原的訊息 | 回傳 `boolean` | +| `POST` | `/session/:id/permissions/:permissionID` | 回應權限請求 | 請求主體:`{ response, remember? }`,回傳 `boolean` | --- ### 訊息 -| 方法 | 路徑 | 描述 | 備註 | -| ------ | --------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `GET` | `/session/:id/message` | 列出工作階段中的訊息 | 查詢:`limit?`,返回`{ info: `Message`, parts: `Part[]`}[]` | -| `POST` | `/session/:id/message` | 發送訊息並等待回覆 | 主體:`{ messageID?, model?, agent?, noReply?, system?, tools?, parts }`,返回`{ info: `Message`, parts: `Part[]`}` | -| `GET` | `/session/:id/message/:messageID` | 獲取訊息詳情 | 返回`{ info: `Message`, parts: `Part[]`}` | -| `POST` | `/session/:id/prompt_async` | 非同步發送訊息(無需等待) | body:與`/session/:id/message`相同,返回`204 No Content` | -| `POST` | `/session/:id/command` | 執行斜線指令 | 主體:`{ messageID?, agent?, model?, command, arguments }`,返回`{ info: `Message`, parts: `Part[]`}` | -| `POST` | `/session/:id/shell` | 執行 shell 指令 | 主體:`{ agent, model?, command }`,返回`{ info: `Message`, parts: `Part[]`}` | +| 方法 | 路徑 | 描述 | 說明 | +| ------ | --------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GET` | `/session/:id/message` | 列出工作階段中的訊息 | 查詢參數:`limit?`,回傳 `{ info: `Message`, parts: `Part[]`}[]` | +| `POST` | `/session/:id/message` | 傳送訊息並等待回應 | 請求主體:`{ messageID?, model?, agent?, noReply?, system?, tools?, parts }`,回傳 `{ info: `Message`, parts: `Part[]`}` | +| `GET` | `/session/:id/message/:messageID` | 取得訊息詳情 | 回傳 `{ info: `Message`, parts: `Part[]`}` | +| `POST` | `/session/:id/prompt_async` | 非同步傳送訊息(不等待回應) | 請求主體:與 `/session/:id/message` 相同,回傳 `204 No Content` | +| `POST` | `/session/:id/command` | 執行斜線指令 | 請求主體:`{ messageID?, agent?, model?, command, arguments }`,回傳 `{ info: `Message`, parts: `Part[]`}` | +| `POST` | `/session/:id/shell` | 執行 shell 指令 | 請求主體:`{ agent, model?, command }`,回傳 `{ info: `Message`, parts: `Part[]`}` | --- @@ -193,40 +190,40 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ----- | ------------------------ | -------------------- | ----------------------------------------------------------------------------------- | -| `GET` | `/find?pattern=` | 搜尋檔案中的文字 | 具有 `path`, `lines`, `line_number`, `absolute_offset`, `submatches` 的匹配物件陣列 | +| `GET` | `/find?pattern=` | 在檔案中搜尋文字 | 包含 `path`、`lines`、`line_number`、`absolute_offset`、`submatches` 的匹配物件陣列 | | `GET` | `/find/file?query=` | 按名稱尋找檔案和目錄 | `string[]`(路徑) | | `GET` | `/find/symbol?query=` | 尋找工作區符號 | Symbol[] | | `GET` | `/file?path=` | 列出檔案和目錄 | FileNode[] | | `GET` | `/file/content?path=

` | 讀取檔案 | FileContent | -| `GET` | `/file/status` | 獲取追蹤檔案的狀態 | File[] | +| `GET` | `/file/status` | 取得已追蹤檔案的狀態 | File[] | #### `/find/file` 查詢參數 -- `query`(必需)- 搜尋字串(模糊匹配) -- `type`(可選)- 將結果限制為 `"file"` 或 `"directory"` -- `directory` (可選) — 覆寫搜尋的專案根目錄 -- `limit`(可選)— 最大結果 (1–200) -- `dirs`(可選)- 舊旗標(`"false"` 僅返回檔案) +- `query`(必填)— 搜尋字串(模糊匹配) +- `type`(選填)— 將結果限制為 `"file"` 或 `"directory"` +- `directory`(選填)— 覆蓋搜尋的專案根目錄 +- `limit`(選填)— 最大結果數(1–200) +- `dirs`(選填)— 舊版旗標(`"false"` 僅回傳檔案) --- -### 工具(實驗) +### 工具(實驗性) -| 方法 | 路徑 | 描述 | 回應 | -| ----- | ------------------------------------------- | ---------------------------- | -------------------------------------------- | -| `GET` | `/experimental/tool/ids` | 列出所有工具 ID | ToolID | -| `GET` | `/experimental/tool?provider=

&model=` | 列出具有模型 JSON 架構的工具 | ToolList | +| 方法 | 路徑 | 描述 | 回應 | +| ----- | ------------------------------------------- | ---------------------------------- | -------------------------------------------- | +| `GET` | `/experimental/tool/ids` | 列出所有工具 ID | ToolIDs | +| `GET` | `/experimental/tool?provider=

&model=` | 列出指定模型的工具及其 JSON Schema | ToolList | --- -### LSP、格式化程式和 MCP +### LSP、格式化工具和 MCP | 方法 | 路徑 | 描述 | 回應 | | ------ | ------------ | ------------------- | -------------------------------------------------------- | -| `GET` | `/lsp` | 獲取 LSP 伺服器狀態 | LSPStatus[] | -| `GET` | `/formatter` | 獲取格式化程式狀態 | FormatterStatus[] | -| `GET` | `/mcp` | 獲取 MCP 伺服器狀態 | `{ [name: string]: `MCPStatus` }` | -| `POST` | `/mcp` | 動態添加 MCP 伺服器 | body: `{ name, config }`, 返回 MCP 狀態物件 | +| `GET` | `/lsp` | 取得 LSP 伺服器狀態 | LSPStatus[] | +| `GET` | `/formatter` | 取得格式化工具狀態 | FormatterStatus[] | +| `GET` | `/mcp` | 取得 MCP 伺服器狀態 | `{ [name: string]: `MCPStatus` }` | +| `POST` | `/mcp` | 動態新增 MCP 伺服器 | 請求主體:`{ name, config }`,回傳 MCP 狀態物件 | --- @@ -238,45 +235,45 @@ opencode 伺服器公開以下 API。 --- -### 記錄 +### 日誌 -| 方法 | 路徑 | 描述 | 回應 | -| ------ | ------ | --------------------------------------------------------- | --------- | -| `POST` | `/log` | 寫入日誌項目。正文:`{ service, level, message, extra? }` | `boolean` | +| 方法 | 路徑 | 描述 | 回應 | +| ------ | ------ | ------------------------------------------------------------- | --------- | +| `POST` | `/log` | 寫入日誌項目。請求主體:`{ service, level, message, extra? }` | `boolean` | --- ### TUI -| 方法 | 路徑 | 描述 | 回應 | -| ------ | ----------------------- | ------------------------------------------- | ------------ | -| `POST` | `/tui/append-prompt` | 將文字附加到提示 | `boolean` | -| `POST` | `/tui/open-help` | 打開說明對話方塊 | `boolean` | -| `POST` | `/tui/open-sessions` | 打開工作階段選擇器 | `boolean` | -| `POST` | `/tui/open-themes` | 打開主題選擇器 | `boolean` | -| `POST` | `/tui/open-models` | 打開模型選擇器 | `boolean` | -| `POST` | `/tui/submit-prompt` | 提交當前提示 | `boolean` | -| `POST` | `/tui/clear-prompt` | 清除提示 | `boolean` | -| `POST` | `/tui/execute-command` | 執行指令 (`{ command }`) | `boolean` | -| `POST` | `/tui/show-toast` | 顯示 Toast (`{ title?, message, variant }`) | `boolean` | -| `GET` | `/tui/control/next` | 等待下一個控制請求 | 控制請求物件 | -| `POST` | `/tui/control/response` | 回應控制請求 (`{ body }`) | `boolean` | +| 方法 | 路徑 | 描述 | 回應 | +| ------ | ----------------------- | ---------------------------------------------- | ------------ | +| `POST` | `/tui/append-prompt` | 向提示詞附加文字 | `boolean` | +| `POST` | `/tui/open-help` | 開啟說明對話框 | `boolean` | +| `POST` | `/tui/open-sessions` | 開啟工作階段選擇器 | `boolean` | +| `POST` | `/tui/open-themes` | 開啟主題選擇器 | `boolean` | +| `POST` | `/tui/open-models` | 開啟模型選擇器 | `boolean` | +| `POST` | `/tui/submit-prompt` | 提交當前提示詞 | `boolean` | +| `POST` | `/tui/clear-prompt` | 清除提示詞 | `boolean` | +| `POST` | `/tui/execute-command` | 執行指令(`{ command }`) | `boolean` | +| `POST` | `/tui/show-toast` | 顯示提示訊息(`{ title?, message, variant }`) | `boolean` | +| `GET` | `/tui/control/next` | 等待下一個控制請求 | 控制請求物件 | +| `POST` | `/tui/control/response` | 回應控制請求(`{ body }`) | `boolean` | --- -### 授權 +### 認證 -| 方法 | 路徑 | 描述 | 回應 | -| ----- | ----------- | ------------------------------------------ | --------- | -| `PUT` | `/auth/:id` | 設定身分驗證憑證。正文必須與供應商架構匹配 | `boolean` | +| 方法 | 路徑 | 描述 | 回應 | +| ----- | ----------- | ---------------------------------------------- | --------- | +| `PUT` | `/auth/:id` | 設定認證憑證。請求主體必須匹配供應商的資料結構 | `boolean` | --- ### 事件 -| 方法 | 路徑 | 描述 | 回應 | -| ----- | -------- | --------------------------------------------------------------------------- | -------------------- | -| `GET` | `/event` | 伺服器發送的事件串流。第一個活動是 `server.connected`,然後是事件匯流排事件 | 伺服器發送的事件串流 | +| 方法 | 路徑 | 描述 | 回應 | +| ----- | -------- | --------------------------------------------------------------------- | ------------------ | +| `GET` | `/event` | 伺服器傳送事件串流。第一個事件是 `server.connected`,之後是匯流排事件 | 伺服器傳送事件串流 | --- @@ -284,4 +281,4 @@ opencode 伺服器公開以下 API。 | 方法 | 路徑 | 描述 | 回應 | | ----- | ------ | ---------------- | ----------------------------- | -| `GET` | `/doc` | OpenAPI 3.1 規範 | 具有 OpenAPI 規範的 HTML 頁面 | +| `GET` | `/doc` | OpenAPI 3.1 規範 | 包含 OpenAPI 規範的 HTML 頁面 | diff --git a/packages/web/src/content/docs/zh-tw/share.mdx b/packages/web/src/content/docs/zh-tw/share.mdx index a7733e346a64..1512007bc356 100644 --- a/packages/web/src/content/docs/zh-tw/share.mdx +++ b/packages/web/src/content/docs/zh-tw/share.mdx @@ -1,43 +1,43 @@ --- title: 分享 -description: 分享您的 opencode 對話。 +description: 分享您的 OpenCode 對話。 --- -opencode 的共享功能允許您建立 opencode 對話的公共連結,以便您可以與團隊成員協作或從其他人那裡獲得幫助。 +OpenCode 的分享功能允許您建立指向 OpenCode 對話的公開連結,方便與團隊成員協作或向他人尋求幫助。 :::note -任何知道連結的人都可以公開存取共享對話。 +共享的對話對任何擁有連結的人都是公開可存取的。 ::: --- -## 它是如何運作的 +## 工作原理 -當您共享對話時,opencode: +當您分享一段對話時,OpenCode 會: -1. 為您的工作階段建立唯一的公共 URL -2. 將您的對話歷史記錄同步到我們的伺服器 -3. 透過可共享連結進行對話 — `opncd.ai/s/` +1. 為您的工作階段建立一個唯一的公開 URL +2. 將您的對話歷史同步到我們的伺服器 +3. 透過可分享的連結使對話可存取 — `opncd.ai/s/` --- -## 模式 +## 分享模式 -opencode 支援三種控制對話共享方式的共享模式: +OpenCode 支援三種分享模式,用於控制對話的共享方式: --- -### 手動(預設) +### 手動模式(預設) -opencode 預設使用手動共享模式。工作階段不會自動共享,但您可以使用 `/share` 指令手動共享它們: +預設情況下,OpenCode 使用手動分享模式。工作階段不會自動共享,但您可以使用 `/share` 指令手動分享: ``` /share ``` -這將生成一個唯一的 URL,並將其複製到您的剪貼簿。 +這將產生一個唯一的 URL 並複製到您的剪貼簿。 -要在 [設定檔](/docs/config) 中明確設定手動模式: +要在[設定檔](/docs/config)中明確設定手動模式: ```json title="opencode.json" { @@ -50,7 +50,7 @@ opencode 預設使用手動共享模式。工作階段不會自動共享,但 ### 自動分享 -您可以在 [設定檔](/docs/config) 中將 `share` 選項設定為 `"auto"` 來為所有新對話啟用自動共享: +您可以在[設定檔](/docs/config)中將 `share` 選項設定為 `"auto"`,為所有新對話啟用自動分享: ```json title="opencode.json" { @@ -59,13 +59,13 @@ opencode 預設使用手動共享模式。工作階段不會自動共享,但 } ``` -啟用自動共享後,每個新對話都會自動共享並生成連結。 +啟用自動分享後,每個新對話都會自動共享並產生連結。 --- -### 已禁用 +### 停用 -您可以在 [設定檔](/docs/config) 中將 `share` 選項設定為 `"disabled"` 來完全禁用共享: +您可以在[設定檔](/docs/config)中將 `share` 選項設定為 `"disabled"`,完全停用分享功能: ```json title="opencode.json" { @@ -74,55 +74,54 @@ opencode 預設使用手動共享模式。工作階段不會自動共享,但 } ``` -要在整個團隊中針對給定專案強制執行此操作,請將其添加到專案中的 `opencode.json` 並簽入 Git。 +要在團隊中對特定專案強制執行此設定,請將其新增到專案的 `opencode.json` 檔案中並提交到 Git。 --- -## 取消共享 +## 取消分享 -要停止共享對話並將其從公共存取中刪除: +要停止分享對話並將其從公開存取中移除: ``` /unshare ``` -這將刪除共享連結並刪除與對話相關的數據。 +這將移除分享連結並刪除與該對話相關的資料。 --- ## 隱私 -分享對話時需要記住一些事項。 +分享對話時需要注意以下幾點。 --- ### 資料保留 -共享對話仍然可以存取,直到您明確取消共享。這 -包括: +共享的對話在您明確取消分享之前將一直保持可存取狀態。這包括: -- 完整的對話歷史記錄 +- 完整的對話歷史 - 所有訊息和回覆 -- 工作階段元數據 +- 工作階段中繼資料 --- ### 建議 -- 僅共享不包含敏感資訊的對話。 -- 分享之前查看對話內容。 -- 協作完成後取消共享對話。 -- 避免與專有程式碼或機密數據共享對話。 -- 對於敏感專案,完全禁用共享。 +- 僅分享不包含敏感資訊的對話。 +- 分享前請檢查對話內容。 +- 協作完成後請取消分享。 +- 避免分享包含專有程式碼或機密資料的對話。 +- 對於敏感專案,請完全停用分享功能。 --- -## 對於企業 +## 企業版 -對於企業部署,共享功能可以是: +對於企業部署,分享功能可以: -- **出於安全合規性完全禁用** -- **僅限**僅透過 SSO 進行身分驗證的使用者 -- **在您自己的基礎架構上自行託管** +- 出於安全合規考量**完全停用** +- **限制**為僅通過 SSO 身分驗證的使用者可用 +- **自行託管**在您自己的基礎架構上 -[了解更多](/docs/enterprise) 關於在您的組織中使用 opencode。 +[了解更多](/docs/enterprise)關於在您的組織中使用 OpenCode 的資訊。 diff --git a/packages/web/src/content/docs/zh-tw/skills.mdx b/packages/web/src/content/docs/zh-tw/skills.mdx index f9bd62263b72..c37fca7bebad 100644 --- a/packages/web/src/content/docs/zh-tw/skills.mdx +++ b/packages/web/src/content/docs/zh-tw/skills.mdx @@ -1,60 +1,60 @@ --- -title: 代理技能 -description: 透過 SKILL.md 定義定義可重複使用行為 +title: "代理技能" +description: "透過 SKILL.md 定義可重複使用的行為" --- -代理技能讓 opencode 從您的儲存庫或主目錄中發現可重複使用的指令。 -技能透過原生 `skill` 工具按需載入 - 代理可以查看可用技能並可以在需要時載入完整內容。 +代理技能讓 OpenCode 能夠從你的儲存庫或主目錄中發現可重複使用的指令。 +技能透過原生的 `skill` 工具按需載入——代理可以查看可用技能,並在需要時載入完整內容。 --- ## 放置檔案 -每個技能名稱建立一個資料夾,並在其中放入 `SKILL.md`。 -opencode 搜尋這些位置: +為每個技能名稱建立一個資料夾,並在其中放入 `SKILL.md`。 +OpenCode 會搜尋以下位置: - 專案設定:`.opencode/skills//SKILL.md` - 全域設定:`~/.config/opencode/skills//SKILL.md` -- Claude 專案相容:`.claude/skills//SKILL.md` +- 專案 Claude 相容:`.claude/skills//SKILL.md` - 全域 Claude 相容:`~/.claude/skills//SKILL.md` - 專案代理相容:`.agents/skills//SKILL.md` - 全域代理相容:`~/.agents/skills//SKILL.md` --- -## 了解發現 +## 了解發現機制 -對於專案本地路徑,opencode 將從當前工作目錄開始,直到到達 git 工作樹。 -它載入 `skills/*/SKILL.md` 中任何匹配的 `.opencode/` 以及一路上任何匹配的 `.claude/skills/*/SKILL.md` 或 `.agents/skills/*/SKILL.md`。 +對於專案本地路徑,OpenCode 會從當前工作目錄向上遍歷,直到到達 git 工作樹根目錄。 +在此過程中,它會載入 `.opencode/` 中所有匹配的 `skills/*/SKILL.md`,以及匹配的 `.claude/skills/*/SKILL.md` 或 `.agents/skills/*/SKILL.md`。 -全域定義也從 `~/.config/opencode/skills/*/SKILL.md`、`~/.claude/skills/*/SKILL.md` 和 `~/.agents/skills/*/SKILL.md` 載入。 +全域定義也會從 `~/.config/opencode/skills/*/SKILL.md`、`~/.claude/skills/*/SKILL.md` 和 `~/.agents/skills/*/SKILL.md` 中載入。 --- -## 撰寫 Frontmatter +## 撰寫 frontmatter 每個 `SKILL.md` 必須以 YAML frontmatter 開頭。 -僅識別這些欄位: +僅識別以下欄位: - `name`(必填) - `description`(必填) -- `license`(可選) -- `compatibility`(可選) -- `metadata`(可選,字串到字串對應) +- `license`(選填) +- `compatibility`(選填) +- `metadata`(選填,字串到字串的映射) -未知的 frontmatter 欄位將被忽略。 +未知的 frontmatter 欄位會被忽略。 --- ## 驗證名稱 -`name` 必須: +`name` 必須滿足: - 長度為 1–64 個字元 -- 為小寫字母數字並帶有單個連字號分隔符 +- 僅包含小寫字母和數字,可用單個連字號分隔 - 不以 `-` 開頭或結尾 -- 不包含連續 `--` -- 匹配包含 `SKILL.md` 的目錄名 +- 不包含連續的 `--` +- 與包含 `SKILL.md` 的目錄名稱一致 等效的正規表示式: @@ -66,14 +66,14 @@ opencode 搜尋這些位置: ## 遵循長度規則 -`description` 必須是 1-1024 個字元。 -保持足夠具體,以便代理能夠正確選擇。 +`description` 必須為 1-1024 個字元。 +請保持描述足夠具體,以便代理能夠正確選擇。 --- -## 使用一個範例 +## 使用範例 -像這樣建立 `.opencode/skills/git-release/SKILL.md`: +建立 `.opencode/skills/git-release/SKILL.md`,內容如下: ```markdown --- @@ -100,10 +100,10 @@ Ask clarifying questions if the target versioning scheme is unclear. --- -## 識別工具說明 +## 識別工具描述 -opencode 在 `skill` 工具描述中列出了可用的技能。 -每個項目都包含技能名稱和描述: +OpenCode 會在 `skill` 工具描述中列出可用技能。 +每個條目包含技能名稱和描述: ```xml @@ -122,9 +122,9 @@ skill({ name: "git-release" }) --- -## 配置權限 +## 設定權限 -使用 `opencode.json` 中基於模式的權限控制代理可以存取哪些技能: +在 `opencode.json` 中使用基於模式的權限來控制代理可以存取哪些技能: ```json { @@ -139,21 +139,21 @@ skill({ name: "git-release" }) } ``` -| 許可 | 行為 | -| ------- | -------------------------- | -| `allow` | 技能立即載入 | -| `deny` | 技能對代理隱藏,存取被拒絕 | -| `ask` | 載入前提示使用者批准 | +| 權限 | 行為 | +| ------- | ------------------------ | +| `allow` | 技能立即載入 | +| `deny` | 對代理隱藏技能,拒絕存取 | +| `ask` | 載入前提示使用者確認 | -模式支援通配符:`internal-*` 匹配 `internal-docs`、`internal-tools` 等。 +模式支援萬用字元:`internal-*` 可匹配 `internal-docs`、`internal-tools` 等。 --- -## 覆寫每個代理 +## 按代理覆蓋權限 -為特定代理授予與全域預設權限不同的權限。 +為特定代理授予與全域預設值不同的權限。 -**對於自定義代理**(在代理前言中): +**自訂代理**(在代理 frontmatter 中): ```yaml --- @@ -163,7 +163,7 @@ permission: --- ``` -**對於內建代理**(在 `opencode.json` 中): +**內建代理**(在 `opencode.json` 中): ```json { @@ -181,11 +181,11 @@ permission: --- -## 禁用技能工具 +## 停用技能工具 -完全禁用不應該使用技能的代理: +為不需要使用技能的代理完全停用技能功能: -**對於自定義代理**: +**自訂代理**: ```yaml --- @@ -194,7 +194,7 @@ tools: --- ``` -**對於內建代理**: +**內建代理**: ```json { @@ -208,15 +208,15 @@ tools: } ``` -禁用時,`` 部分將被完全省略。 +停用後,`` 部分將被完全省略。 --- -## 解決載入問題 +## 排查載入問題 -如果某項技能沒有顯示: +如果某個技能沒有顯示: -1. 驗證 `SKILL.md` 是否全部大寫拼寫 +1. 確認 `SKILL.md` 檔案名稱全部為大寫字母 2. 檢查 frontmatter 是否包含 `name` 和 `description` -3. 確保技能名稱在所有位置都是唯一的 -4. 檢查權限 - `deny` 的技能對代理隱藏 +3. 確保技能名稱在所有位置中唯一 +4. 檢查權限設定——設為 `deny` 的技能會對代理隱藏 diff --git a/packages/web/src/content/docs/zh-tw/themes.mdx b/packages/web/src/content/docs/zh-tw/themes.mdx index d47e1afe5eab..513f2c8a774b 100644 --- a/packages/web/src/content/docs/zh-tw/themes.mdx +++ b/packages/web/src/content/docs/zh-tw/themes.mdx @@ -3,65 +3,65 @@ title: 主題 description: 選擇內建主題或定義您自己的主題。 --- -使用 opencode,您可以從多個內建主題之一中進行選擇,使用適合您的終端機主題的主題,或者定義您自己的自定義主題。 +透過 OpenCode,您可以從多個內建主題中進行選擇,使用能自動適配終端機主題的主題,或者定義您自己的自訂主題。 -預設情況下,opencode 使用我們自己的 `opencode` 主題。 +預設情況下,OpenCode 使用我們自己的 `opencode` 主題。 --- ## 終端機要求 -為了使主題能夠正確顯示完整的調色盤,您的終端機必須支援**真彩色**(24 位元顏色)。大多數現代終端機預設支援此功能,但您可能需要啟用它: +為了使主題能夠正確顯示完整的調色盤,您的終端機必須支援**真彩色**(24 位元色)。大多數現代終端機預設支援此功能,但您可能需要手動啟用: -- **檢查支援**:執行 `echo $COLORTERM` - 它應該輸出 `truecolor` 或 `24bit` -- **啟用真彩色**:在 shell 設定檔中設定環境變數 `COLORTERM=truecolor` -- **終端機相容性**:確保您的終端機模擬器支援 24 位元顏色(大多數現代終端機,​​如 iTerm2、Alacritty、Kitty、Windows Terminal 和最新版本的 GNOME Terminal 都支援) +- **檢查支援情況**:執行 `echo $COLORTERM` — 輸出應為 `truecolor` 或 `24bit` +- **啟用真彩色**:在您的 shell 設定檔中設定環境變數 `COLORTERM=truecolor` +- **終端機相容性**:確保您的終端機模擬器支援 24 位元色(大多數現代終端機如 iTerm2、Alacritty、Kitty、Windows Terminal 以及較新版本的 GNOME Terminal 均已支援) -如果沒有真彩色支援,主題的顏色精度可能會降低或回退到最接近的 256 色近似值。 +如果沒有真彩色支援,主題可能會出現色彩精度下降的情況,或者回退到最接近的 256 色近似值。 --- ## 內建主題 -opencode 附帶了幾個內建主題。 +OpenCode 自帶多個內建主題。 -| 名稱 | 描述 | -| ---------------------- | ------------------------------------------------------------------ | -| `system` | 適應您終端機的背景顏色 | -| `tokyonight` | 基於 [Tokyo Night](https://github.com/folke/tokyonight.nvim) 主題 | -| `everforest` | 基於 [Everforest](https://github.com/sainnhe/everforest) 主題 | -| `ayu` | 基於 [Ayu](https://github.com/ayu-theme) 深色主題 | -| `catppuccin` | 基於 [Catppuccin](https://github.com/catppuccin) 主題 | -| `catppuccin-macchiato` | 基於 [Catppuccin](https://github.com/catppuccin) 主題 | -| `gruvbox` | 基於 [Gruvbox](https://github.com/morhetz/gruvbox) 主題 | -| `kanagawa` | 基於 [Kanagawa](https://github.com/rebelot/kanagawa.nvim) 主題 | -| `nord` | 基於 [Nord](https://github.com/nordtheme/nord) 主題 | -| `matrix` | 駭客風格黑底綠主題 | -| `one-dark` | 基於 [One Dark](https://github.com/Th3Whit3Wolf/one-nvim) 深色主題 | +| 名稱 | 描述 | +| ---------------------- | ------------------------------------------------------------------- | +| `system` | 自動適配終端機的背景顏色 | +| `tokyonight` | 基於 [Tokyonight](https://github.com/folke/tokyonight.nvim) 主題 | +| `everforest` | 基於 [Everforest](https://github.com/sainnhe/everforest) 主題 | +| `ayu` | 基於 [Ayu](https://github.com/ayu-theme) 暗色主題 | +| `catppuccin` | 基於 [Catppuccin](https://github.com/catppuccin) 主題 | +| `catppuccin-macchiato` | 基於 [Catppuccin](https://github.com/catppuccin) 主題 | +| `gruvbox` | 基於 [Gruvbox](https://github.com/morhetz/gruvbox) 主題 | +| `kanagawa` | 基於 [Kanagawa](https://github.com/rebelot/kanagawa.nvim) 主題 | +| `nord` | 基於 [Nord](https://github.com/nordtheme/nord) 主題 | +| `matrix` | 駭客風格的黑底綠字主題 | +| `one-dark` | 基於 [Atom One](https://github.com/Th3Whit3Wolf/one-nvim) Dark 主題 | -此外,我們還在不斷添加新主題。 +我們還在不斷新增更多主題。 --- ## 系統主題 -`system` 主題旨在自動適應您終端機的配色方案。與使用固定顏色的傳統主題不同,_system_ 主題: +`system` 主題旨在自動適配您終端機的配色方案。與使用固定顏色的傳統主題不同,_system_ 主題具有以下特點: -- **生成灰階**:根據終端機的背景顏色建立自定義灰階,確保最佳對比度。 -- **使用 ANSI 顏色**:利用標準 ANSI 顏色 (0-15) 進行語法高亮顯示和 UI 元素,尊重終端機的調色盤。 -- **保留終端機預設設定**:使用 `none` 作為文字和背景顏色,以保持終端機的原生外觀。 +- **產生灰階色階**:根據終端機的背景顏色建立自訂灰階色階,確保最佳對比度。 +- **使用 ANSI 顏色**:利用標準 ANSI 顏色(0-15)進行語法高亮和 UI 元素渲染,遵循終端機的調色盤設定。 +- **保留終端機預設值**:將文字和背景顏色設為 `none`,以保持終端機的原生外觀。 系統主題適合以下使用者: -- 希望 opencode 與其終端機的外觀相匹配 -- 使用自定義終端機配色方案 -- 希望所有終端機應用程式具有一致的外觀 +- 希望 OpenCode 與終端機的外觀保持一致 +- 使用了自訂終端機配色方案 +- 偏好所有終端機應用程式擁有統一的視覺風格 --- ## 使用主題 -您可以透過使用 `/theme` 指令調出主題選擇來選擇主題。或者您可以在 [設定](/docs/config) 中指定它。 +您可以透過 `/theme` 指令調出主題選擇介面來選擇主題,也可以在[設定](/docs/config)檔案中直接指定。 ```json title="opencode.json" {3} { @@ -72,37 +72,37 @@ opencode 附帶了幾個內建主題。 --- -## 自定義主題 +## 自訂主題 -opencode 支援靈活的基於 JSON 的主題系統,允許使用者輕鬆建立和自定義主題。 +OpenCode 支援靈活的基於 JSON 的主題系統,讓使用者可以輕鬆建立和自訂主題。 --- -### 階層 +### 層級優先順序 -主題按以下順序從多個目錄載入,其中後面的目錄覆寫前面的目錄: +主題按以下順序從多個目錄載入,後面的目錄會覆蓋前面的目錄: -1. **內建主題** - 這些主題嵌入在二進位檔案中 -2. **使用者設定目錄** - 在 `~/.config/opencode/themes/*.json` 或 `$XDG_CONFIG_HOME/opencode/themes/*.json` 中定義 -3. **專案根目錄** - 定義在 `/.opencode/themes/*.json` -4. **當前工作目錄** - 在 `./.opencode/themes/*.json` 中定義 +1. **內建主題** — 嵌入在二進位檔案中 +2. **使用者設定目錄** — 定義在 `~/.config/opencode/themes/*.json` 或 `$XDG_CONFIG_HOME/opencode/themes/*.json` +3. **專案根目錄** — 定義在 `/.opencode/themes/*.json` +4. **當前工作目錄** — 定義在 `./.opencode/themes/*.json` -如果多個目錄包含同名主題,則將使用優先順序較高的目錄中的主題。 +如果多個目錄包含同名主題,將使用優先順序較高的目錄中的主題。 --- ### 建立主題 -要建立自定義主題,請在主題目錄之一中建立一個 JSON 檔案。 +要建立自訂主題,請在上述任一主題目錄中建立一個 JSON 檔案。 -對於使用者範圍的主題: +建立使用者級主題: ```bash no-frame mkdir -p ~/.config/opencode/themes vim ~/.config/opencode/themes/my-theme.json ``` -以及針對特定專案的主題。 +建立專案級主題: ```bash no-frame mkdir -p .opencode/themes @@ -113,34 +113,34 @@ vim .opencode/themes/my-theme.json ### JSON 格式 -主題使用靈活的 JSON 格式,支援: +主題使用靈活的 JSON 格式,支援以下特性: - **十六進位顏色**:`"#ffffff"` -- **ANSI 顏色**:`3` (0-255) -- **顏色參考**:`"primary"` 或自定義定義 -- **深色/淺色版本**:`{"dark": "#000", "light": "#fff"}` -- **無顏色**:`"none"` - 使用終端機的預設顏色或透明 +- **ANSI 顏色**:`3`(0-255) +- **顏色參考**:`"primary"` 或自訂定義的顏色名 +- **深色/淺色變體**:`{"dark": "#000", "light": "#fff"}` +- **無顏色**:`"none"` — 使用終端機的預設顏色或透明背景 --- ### 顏色定義 -`defs` 部分是可選的,它允許您定義可在主題中引用的可重複使用顏色。 +`defs` 部分是選填的,它允許您定義可在主題中重複引用的可重複使用顏色。 --- ### 終端機預設值 -特殊值 `"none"` 可用於任何顏色以繼承終端機的預設顏色。這對於建立與終端機配色方案無縫融合的主題特別有用: +特殊值 `"none"` 可用於任何顏色屬性,以繼承終端機的預設顏色。這在建立需要與終端機配色方案無縫融合的主題時特別有用: -- `"text": "none"` - 使用終端機的預設前景色 -- `"background": "none"` - 使用終端機的預設背景顏色 +- `"text": "none"` — 使用終端機的預設前景色 +- `"background": "none"` — 使用終端機的預設背景色 --- ### 範例 -以下是自定義主題的範例: +以下是一個自訂主題的完整範例: ```json title="my-theme.json" { diff --git a/packages/web/src/content/docs/zh-tw/tools.mdx b/packages/web/src/content/docs/zh-tw/tools.mdx index a030a5e84625..529b706194f3 100644 --- a/packages/web/src/content/docs/zh-tw/tools.mdx +++ b/packages/web/src/content/docs/zh-tw/tools.mdx @@ -3,15 +3,15 @@ title: 工具 description: 管理 LLM 可以使用的工具。 --- -工具允許 LLM 在您的程式碼庫中執行操作。 opencode 附帶了一組內建工具,但您可以使用 [自定義工具](/docs/custom-tools) 或 [MCP 伺服器](/docs/mcp-servers) 對其進行擴展。 +工具允許 LLM 在您的程式碼庫中執行操作。OpenCode 自帶一組內建工具,您也可以透過[自訂工具](/docs/custom-tools)或 [MCP 伺服器](/docs/mcp-servers)來擴充它。 -預設情況下,所有工具都是**啟用**並且不需要執行權限。您可以透過 [權限](/docs/permissions) 控制工具行為。 +預設情況下,所有工具都是**啟用**的,且無需權限即可執行。您可以透過[權限](/docs/permissions)來控制工具的行為。 --- -## 配置 +## 設定 -使用 `permission` 欄位控制工具行為。您可以允許、拒絕或要求批准每個工具。 +使用 `permission` 欄位來控制工具行為。您可以對每個工具設定允許、拒絕或需要審批。 ```json title="opencode.json" { @@ -24,7 +24,7 @@ description: 管理 LLM 可以使用的工具。 } ``` -您還可以使用通配符同時控制多個工具。例如,要求 MCP 伺服器批准所有工具: +您還可以使用萬用字元同時控制多個工具。例如,要求某個 MCP 伺服器的所有工具都需要審批: ```json title="opencode.json" { @@ -35,13 +35,13 @@ description: 管理 LLM 可以使用的工具。 } ``` -[了解更多](/docs/permissions) 關於配置權限。 +[了解更多](/docs/permissions)關於設定權限的內容。 --- -## 內建 +## 內建工具 -以下是 opencode 中可用的所有內建工具。 +以下是 OpenCode 中所有可用的內建工具。 --- @@ -58,13 +58,13 @@ description: 管理 LLM 可以使用的工具。 } ``` -該工具允許 LLM 執行 `npm install`、`git status` 等終端機指令或任何其他 shell 指令。 +該工具允許 LLM 執行終端機指令,例如 `npm install`、`git status` 或其他任何 shell 指令。 --- ### edit -使用精確的字串替換修改現有檔案。 +透過精確的字串替換來修改現有檔案。 ```json title="opencode.json" {4} { @@ -75,7 +75,7 @@ description: 管理 LLM 可以使用的工具。 } ``` -該工具透過替換精確的文字匹配來對檔案執行精確編輯。這是 LLM 修改程式碼的主要方式。 +該工具透過替換精確匹配的文字來對檔案進行編輯。這是 LLM 修改程式碼的主要方式。 --- @@ -92,17 +92,17 @@ description: 管理 LLM 可以使用的工具。 } ``` -使用它允許 LLM 建立新檔案。如果現有檔案已經存在,它將覆蓋它們。 +使用此工具允許 LLM 建立新檔案。如果檔案已存在,則會覆蓋現有檔案。 :::note -`write` 工具由 `edit` 權限控制,該權限涵蓋所有檔案修改(`edit`、`write`、`patch`、`multiedit`)。 +`write` 工具由 `edit` 權限控制,該權限涵蓋所有檔案修改操作(`edit`、`write`、`patch`、`multiedit`)。 ::: --- ### read -從程式碼庫中讀取檔案內容。 +讀取程式碼庫中的檔案內容。 ```json title="opencode.json" {4} { @@ -113,7 +113,7 @@ description: 管理 LLM 可以使用的工具。 } ``` -該工具讀取檔案並返回其內容。它支援讀取大檔案的特定行範圍。 +該工具讀取檔案並回傳其內容。它支援對大檔案讀取指定行範圍。 --- @@ -130,13 +130,13 @@ description: 管理 LLM 可以使用的工具。 } ``` -在您的程式碼庫中快速進行內容搜尋。支援完整的正規表示式語法和檔案模式過濾。 +在程式碼庫中快速搜尋內容。支援完整的正規表示式語法和檔案模式過濾。 --- ### glob -透過模式匹配尋找檔案。 +透過模式匹配查找檔案。 ```json title="opencode.json" {4} { @@ -147,13 +147,13 @@ description: 管理 LLM 可以使用的工具。 } ``` -使用 `**/*.js` 或 `src/**/*.ts` 等全域模式搜尋檔案。返回按修改時間排序的匹配檔案路徑。 +使用 `**/*.js` 或 `src/**/*.ts` 等 glob 模式搜尋檔案。回傳按修改時間排序的匹配檔案路徑。 --- ### list -列出給定路徑中的檔案和目錄。 +列出指定路徑下的檔案和目錄。 ```json title="opencode.json" {4} { @@ -164,16 +164,16 @@ description: 管理 LLM 可以使用的工具。 } ``` -該工具列出目錄內容。它接受全域模式來過濾結果。 +該工具用於列出目錄內容。它接受 glob 模式來過濾結果。 --- ### lsp(實驗性) -與您配置的 LSP 伺服器互動,以獲得程式碼智慧功能,例如定義、引用、游標懸停資訊和呼叫階層。 +與已設定的 LSP 伺服器互動,取得程式碼智慧功能,如定義跳轉、參考查找、懸停資訊和呼叫階層結構。 :::note -該工具僅在 `OPENCODE_EXPERIMENTAL_LSP_TOOL=true`(或 `OPENCODE_EXPERIMENTAL=true`)時可用。 +該工具僅在設定 `OPENCODE_EXPERIMENTAL_LSP_TOOL=true`(或 `OPENCODE_EXPERIMENTAL=true`)時可用。 ::: ```json title="opencode.json" {4} @@ -187,13 +187,13 @@ description: 管理 LLM 可以使用的工具。 支援的操作包括 `goToDefinition`、`findReferences`、`hover`、`documentSymbol`、`workspaceSymbol`、`goToImplementation`、`prepareCallHierarchy`、`incomingCalls` 和 `outgoingCalls`。 -要配置哪些 LSP 伺服器可用於您的專案,請參閱 [LSP 伺服器](/docs/lsp)。 +要設定專案可用的 LSP 伺服器,請參閱 [LSP 伺服器](/docs/lsp)。 --- ### patch -對檔案套用 Patch。 +對檔案套用補丁。 ```json title="opencode.json" {4} { @@ -204,17 +204,17 @@ description: 管理 LLM 可以使用的工具。 } ``` -該工具將 Patch 檔案套用到您的程式碼庫。對於套用來自各種來源的差異和 Patch 很有用。 +該工具將補丁檔案套用到您的程式碼庫中。適用於套用來自各種來源的 diff 和補丁。 :::note -`patch` 工具由 `edit` 權限控制,該權限涵蓋所有檔案修改(`edit`、`write`、`patch`、`multiedit`)。 +`patch` 工具由 `edit` 權限控制,該權限涵蓋所有檔案修改操作(`edit`、`write`、`patch`、`multiedit`)。 ::: --- ### skill -載入 [技能](/docs/skills)(`SKILL.md` 檔案)並在對話中返回其內容。 +載入一個[技能](/docs/skills)(即 `SKILL.md` 檔案)並在對話中回傳其內容。 ```json title="opencode.json" {4} { @@ -229,7 +229,7 @@ description: 管理 LLM 可以使用的工具。 ### todowrite -在編碼工作階段期間管理待辦事項清單。 +在編碼工作階段中管理待辦事項清單。 ```json title="opencode.json" {4} { @@ -240,17 +240,17 @@ description: 管理 LLM 可以使用的工具。 } ``` -建立和更新任務列表以追蹤複雜操作期間的進度。LLM 使用它來組織多步驟任務。 +建立和更新任務清單以追蹤複雜操作的進度。LLM 使用此工具來組織多步驟任務。 :::note -預設情況下,子代理禁用此工具,但您可以手動啟用它。 [了解更多](/docs/agents/#permissions) +該工具預設對子代理停用,但您可以手動啟用。[了解更多](/docs/agents/#permissions) ::: --- ### todoread -閱讀現有的待辦事項清單。 +讀取現有的待辦事項清單。 ```json title="opencode.json" {4} { @@ -261,17 +261,17 @@ description: 管理 LLM 可以使用的工具。 } ``` -讀取當前待辦事項清單狀態。LLM 用來追蹤哪些任務待處理或已完成。 +讀取當前待辦事項清單的狀態。LLM 使用此工具來追蹤哪些任務待處理、哪些已完成。 :::note -預設情況下,子代理禁用此工具,但您可以手動啟用它。 [了解更多](/docs/agents/#permissions) +該工具預設對子代理停用,但您可以手動啟用。[了解更多](/docs/agents/#permissions) ::: --- ### webfetch -獲取網頁內容。 +擷取網頁內容。 ```json title="opencode.json" {4} { @@ -282,7 +282,7 @@ description: 管理 LLM 可以使用的工具。 } ``` -允許 LLM 獲取和閱讀網頁。對於尋找文件或研究線上資源很有用。 +允許 LLM 擷取並讀取網頁內容。適用於查閱文件或研究線上資源。 --- @@ -291,9 +291,9 @@ description: 管理 LLM 可以使用的工具。 在網路上搜尋資訊。 :::note -僅當使用 opencode 供應商或 `OPENCODE_ENABLE_EXA` 環境變數設定為任何真值(例如 `true` 或 `1`)時,此工具才可用。 +該工具僅在使用 OpenCode 供應商時,或當 `OPENCODE_ENABLE_EXA` 環境變數設定為任意真值(例如 `true` 或 `1`)時可用。 -要在啟動 opencode 時啟用: +在啟動 OpenCode 時啟用: ```bash OPENCODE_ENABLE_EXA=1 opencode @@ -310,19 +310,19 @@ OPENCODE_ENABLE_EXA=1 opencode } ``` -使用 Exa AI 執行網路搜尋以線上尋找相關資訊。對於研究主題、尋找時事或收集超出訓練數據截止範圍的資訊很有用。 +使用 Exa AI 進行網路搜尋以查找相關資訊。適用於研究主題、了解時事動態或取得超出訓練資料截止日期的資訊。 -不需要 API 金鑰 - 該工具無需身分驗證即可直接連接到 Exa AI 的託管 MCP 服務。 +無需 API 金鑰——該工具無需身分驗證即可直接連接到 Exa AI 的託管 MCP 服務。 :::tip -當您需要尋找資訊(發現)時,請使用 `websearch`;當您需要從特定 URL 檢索內容(檢索)時,請使用 `webfetch`。 +當您需要查找資訊(發現)時使用 `websearch`,當您需要從特定 URL 擷取內容(檢索)時使用 `webfetch`。 ::: --- ### question -在執行過程中詢問使用者問題。 +在執行過程中向使用者提問。 ```json title="opencode.json" {4} { @@ -333,42 +333,42 @@ OPENCODE_ENABLE_EXA=1 opencode } ``` -該工具允許 LLM 在任務期間詢問使用者問題。它適用於: +該工具允許 LLM 在執行任務期間向使用者提問。適用於以下場景: -- 收集使用者偏好或要求 -- 澄清不明確的指令 -- 就實作選擇做出決策 -- 提供選擇方向 +- 收集使用者偏好或需求 +- 釐清模糊的指令 +- 取得實作方案的決策 +- 提供方向選擇的選項 -每個問題都包含標題、問題文字和選項列表。使用者可以從提供的選項中進行選擇或輸入自定義答案。當存在多個問題時,使用者可以在提交所有答案之前在這些問題之間導航。 +每個問題包含標題、問題正文和選項清單。使用者可以從提供的選項中選擇,也可以輸入自訂答案。當有多個問題時,使用者可以在提交所有答案之前在各問題之間切換瀏覽。 --- -## 自定義工具 +## 自訂工具 -自定義工具可讓您定義 LLM 可以呼叫的自己的函式。這些是在您的設定檔中定義的並且可以執行任意程式碼。 +自訂工具允許您定義 LLM 可以呼叫的自訂函式。這些函式在您的設定檔中定義,可以執行任意程式碼。 -[了解更多](/docs/custom-tools) 關於建立自定義工具。 +[了解更多](/docs/custom-tools)關於建立自訂工具的內容。 --- ## MCP 伺服器 -MCP(模型上下文協定)伺服器允許您整合外部工具和服務。這包括資料庫存取、API 整合和第三方服務。 +MCP(Model Context Protocol)伺服器允許您整合外部工具和服務,包括資料庫存取、API 整合和第三方服務。 -[了解更多](/docs/mcp-servers) 關於配置 MCP 伺服器。 +[了解更多](/docs/mcp-servers)關於設定 MCP 伺服器的內容。 --- -## 內部結構 +## 內部機制 -在內部,`grep`、`glob` 和 `list` 等工具在底層使用 [ripgrep](https://github.com/BurntSushi/ripgrep)。預設情況下,ripgrep 遵循 `.gitignore` 模式,這意味著 `.gitignore` 中列出的檔案和目錄將從搜尋和列表中排除。 +在內部,`grep`、`glob` 和 `list` 等工具底層使用 [ripgrep](https://github.com/BurntSushi/ripgrep)。預設情況下,ripgrep 遵循 `.gitignore` 中的模式,這意味著 `.gitignore` 中列出的檔案和目錄將被排除在搜尋和列表結果之外。 --- ### 忽略模式 -要包含通常會被忽略的檔案,請在專案根目錄中建立一個 `.ignore` 檔案。該檔案可以明確允許某些路徑。 +要包含通常會被忽略的檔案,請在專案根目錄下建立一個 `.ignore` 檔案。該檔案可以明確允許某些路徑。 ```text title=".ignore" !node_modules/ @@ -376,4 +376,4 @@ MCP(模型上下文協定)伺服器允許您整合外部工具和服務。 !build/ ``` -例如,此 `.ignore` 檔案允許 ripgrep 在 `node_modules/`、`dist/` 和 `build/` 目錄中搜尋,即使它們列在 `.gitignore` 中。 +例如,這個 `.ignore` 檔案允許 ripgrep 在 `node_modules/`、`dist/` 和 `build/` 目錄中進行搜尋,即使它們已在 `.gitignore` 中列出。 diff --git a/packages/web/src/content/docs/zh-tw/troubleshooting.mdx b/packages/web/src/content/docs/zh-tw/troubleshooting.mdx index 17c5aa3a6a7d..71981a032ecd 100644 --- a/packages/web/src/content/docs/zh-tw/troubleshooting.mdx +++ b/packages/web/src/content/docs/zh-tw/troubleshooting.mdx @@ -1,67 +1,67 @@ --- title: 疑難排解 -description: 常見問題以及如何解決它們。 +description: 常見問題及其解決方法。 --- -要除錯 opencode 問題,請首先檢查其儲存在磁碟上的日誌和本地數據。 +要偵錯 OpenCode 的問題,請先檢查其儲存在磁碟上的日誌和本地資料。 --- ## 日誌 -日誌檔案寫入: +日誌檔案寫入位置: -- **macOS/Linux**:`~/.local/share/opencode/log/` -- **Windows**:按 `WIN+R` 並貼上 `%USERPROFILE%\.local\share\opencode\log` +- **macOS/Linux**: `~/.local/share/opencode/log/` +- **Windows**: 按 `WIN+R` 並貼上 `%USERPROFILE%\.local\share\opencode\log` -日誌檔案以時間戳命名(例如 `2025-01-09T123456.log`),並保留最近的 10 個日誌檔案。 +日誌檔案以時間戳記命名(例如 `2025-01-09T123456.log`),並保留最近的 10 個日誌檔案。 -您可以使用 `--log-level` 命令列選項設定日誌等級以獲取更詳細的除錯資訊。例如,`opencode --log-level DEBUG`。 +你可以透過 `--log-level` 命令列選項設定日誌等級以取得更詳細的偵錯資訊。例如:`opencode --log-level DEBUG`。 --- ## 儲存 -opencode 將工作階段數據和其他應用程式數據儲存在磁碟上: +OpenCode 將工作階段資料和其他應用程式資料儲存在磁碟上: -- **macOS/Linux**:`~/.local/share/opencode/` -- **Windows**:按 `WIN+R` 並貼上 `%USERPROFILE%\.local\share\opencode` +- **macOS/Linux**: `~/.local/share/opencode/` +- **Windows**: 按 `WIN+R` 並貼上 `%USERPROFILE%\.local\share\opencode` 該目錄包含: -- `auth.json` - 身分驗證數據,例如 API 金鑰、OAuth 令牌 +- `auth.json` - 身分驗證資料,如 API 金鑰、OAuth Token - `log/` - 應用程式日誌 -- `project/` - 專案特定數據,例如工作階段和訊息數據 - - 如果專案位於 Git 儲存庫中,則它儲存在 `.//storage/` 中 - - 如果不是 Git 儲存庫,則儲存在 `./global/storage/` 中 +- `project/` - 專案特定資料,如工作階段和訊息資料 + - 如果專案位於 Git 儲存庫中,則儲存在 `.//storage/` + - 如果不是 Git 儲存庫,則儲存在 `./global/storage/` --- ## 桌面應用程式 -opencode Desktop 在背景執行本地 opencode 伺服器(`opencode-cli` sidecar)。大多數問題是由行為不當的外掛、損壞的快取或錯誤的伺服器設定引起的。 +OpenCode Desktop 會在背景執行一個本地 OpenCode 伺服器(即 `opencode-cli` 附屬程序)。大多數問題是由外掛異常、快取損壞或錯誤的伺服器設定引起的。 ### 快速檢查 - 完全退出並重新啟動應用程式。 -- 如果應用程式顯示錯誤螢幕,請單擊「**重新啟動**」並複製錯誤詳細資訊。 -- 僅限 macOS:`OpenCode` 選單 -> **重新載入 Webview**(如果 UI 為空白/凍結,則有幫助)。 +- 如果應用程式顯示錯誤頁面,請點擊**重新啟動**並複製錯誤詳情。 +- 僅限 macOS:`OpenCode` 選單 -> **Reload Webview**(當 UI 空白或凍結時有效)。 --- -### 禁用外掛 +### 停用外掛 -如果桌面應用程式在啟動時崩潰、卡住或行為異常,請首先禁用外掛。 +如果桌面應用程式在啟動時當機、卡住或行為異常,請先停用外掛。 -#### 檢查全域配置 +#### 檢查全域設定 -打開全域設定檔並查找 `plugin` 鍵。 +開啟你的全域設定檔,查找 `plugin` 鍵。 -- **macOS/Linux**:`~/.config/opencode/opencode.jsonc`(或 `~/.config/opencode/opencode.json`) -- **macOS/Linux**(較舊的安裝):`~/.local/share/opencode/opencode.jsonc` -- **Windows**:按 `WIN+R` 並貼上 `%USERPROFILE%\.config\opencode\opencode.jsonc` +- **macOS/Linux**: `~/.config/opencode/opencode.jsonc`(或 `~/.config/opencode/opencode.json`) +- **macOS/Linux**(舊版安裝): `~/.local/share/opencode/opencode.jsonc` +- **Windows**: 按 `WIN+R` 並貼上 `%USERPROFILE%\.config\opencode\opencode.jsonc` -如果您配置了外掛,請透過刪除鍵或將其設定為空陣列來暫時禁用它們: +如果你設定了外掛,請透過移除該鍵或將其設定為空陣列來暫時停用它們: ```jsonc { @@ -72,114 +72,114 @@ opencode Desktop 在背景執行本地 opencode 伺服器(`opencode-cli` sidec #### 檢查外掛目錄 -opencode 還可以從磁碟載入本地外掛。暫時將它們移開(或重命名資料夾)並重新啟動桌面應用程式: +OpenCode 還可以從磁碟載入本地外掛。暫時將這些外掛移走(或重新命名資料夾),然後重新啟動桌面應用程式: - **全域外掛** - - **macOS/Linux**:`~/.config/opencode/plugins/` - - **Windows**:按 `WIN+R` 並貼上 `%USERPROFILE%\.config\opencode\plugins` -- **專案外掛**(僅當您使用每個專案配置時) + - **macOS/Linux**: `~/.config/opencode/plugins/` + - **Windows**: 按 `WIN+R` 並貼上 `%USERPROFILE%\.config\opencode\plugins` +- **專案外掛**(僅當你使用了專案級設定時) - `/.opencode/plugins/` -如果應用程式再次開始工作,請一次重新啟用一個外掛,以找出導致問題的外掛。 +如果應用程式恢復正常,請逐個重新啟用外掛,找出導致問題的那個。 --- ### 清除快取 -如果禁用外掛沒有幫助(或者外掛安裝被卡住),請清除快取,以便 opencode 可以重建它。 +如果停用外掛沒有幫助(或外掛安裝卡住了),請清除快取以便 OpenCode 重新建置。 -1. 完全退出 opencode Desktop。 +1. 完全退出 OpenCode Desktop。 2. 刪除快取目錄: -- **macOS**:Finder -> `Cmd+Shift+G` -> 貼上 `~/.cache/opencode` -- **Linux**:刪除 `~/.cache/opencode`(或執行 `rm -rf ~/.cache/opencode`) -- **Windows**:按 `WIN+R` 並貼上 `%USERPROFILE%\.cache\opencode` +- **macOS**: Finder -> `Cmd+Shift+G` -> 貼上 `~/.cache/opencode` +- **Linux**: 刪除 `~/.cache/opencode`(或執行 `rm -rf ~/.cache/opencode`) +- **Windows**: 按 `WIN+R` 並貼上 `%USERPROFILE%\.cache\opencode` -3. 重新啟動 opencode 桌面。 +3. 重新啟動 OpenCode Desktop。 --- -### 修復伺服器連接問題 +### 修復伺服器連線問題 -opencode Desktop 可以啟動自己的本地伺服器(預設)或連接到您配置的伺服器 URL。 +OpenCode Desktop 可以啟動自己的本地伺服器(預設行為),也可以連線到你設定的伺服器 URL。 -如果您看到 **「連接失敗」** 對話方塊(或者應用程式永遠無法通過啟動螢幕),請檢查自定義伺服器 URL。 +如果你看到**「Connection Failed」**對話框(或應用程式始終停留在啟動畫面),請檢查自訂伺服器 URL。 #### 清除桌面預設伺服器 URL -在主螢幕中,單擊伺服器名稱(帶有狀態點)以打開伺服器選取器。在「**預設伺服器**」部分中,單擊「**清除**」。 +在主頁面上,點擊伺服器名稱(帶有狀態指示點)以開啟伺服器選擇器。在**預設伺服器**部分,點擊**清除**。 -#### 從您的配置中刪除 `server.port` / `server.hostname` +#### 從設定中移除 `server.port` / `server.hostname` -如果您的 `opencode.json(c)` 包含 `server` 部分,請將其暫時刪除並重新啟動桌面應用程式。 +如果你的 `opencode.json(c)` 包含 `server` 部分,請暫時移除該部分並重新啟動桌面應用程式。 #### 檢查環境變數 -如果您在環境中設定了 `OPENCODE_PORT`,桌面應用程式將嘗試將該連接埠用於本地伺服器。 +如果你在環境中設定了 `OPENCODE_PORT`,桌面應用程式將嘗試使用該連接埠作為本地伺服器連接埠。 - 取消設定 `OPENCODE_PORT`(或選擇一個空閒連接埠)並重新啟動。 --- -### Linux:Wayland / X11 問題 +### Linux: Wayland / X11 問題 -在 Linux 上,某些 Wayland 設定可能會導致空白視窗或合成器錯誤。 +在 Linux 上,某些 Wayland 設定可能會導致視窗空白或合成器錯誤。 -- 如果您在 Wayland 上且應用程式空白/崩潰,請嘗試使用 `OC_ALLOW_WAYLAND=1` 啟動。 -- 如果這讓事情變得更糟,請將其刪除並嘗試在 X11 工作階段下啟動。 +- 如果你使用 Wayland 且應用程式出現空白或當機,請嘗試使用 `OC_ALLOW_WAYLAND=1` 啟動。 +- 如果情況變得更糟,請移除該設定並嘗試在 X11 工作階段下啟動。 --- -### Windows:WebView2 執行時 +### Windows: WebView2 執行階段 -在 Windows 上,opencode Desktop 需要 Microsoft Edge **WebView2 執行時**。如果應用程式打開為空白視窗或無法啟動,請安裝/更新 WebView2,然後重試。 +在 Windows 上,OpenCode Desktop 需要 Microsoft Edge **WebView2 Runtime**。如果應用程式開啟後是空白視窗或無法啟動,請安裝或更新 WebView2 後重試。 --- -### Windows:一般性能問題 +### Windows: 常見效能問題 -如果您在 Windows 上遇到性能緩慢、檔案存取問題或終端機問題,請嘗試使用 [WSL(適用於 Linux 的 Windows 子系統)](/docs/windows-wsl)。 WSL 提供了一個可以與 opencode 功能更加無縫協作的 Linux 環境。 +如果你在 Windows 上遇到效能緩慢、檔案存取問題或終端機問題,請嘗試使用 [WSL (Windows Subsystem for Linux)](/docs/windows-wsl)。WSL 提供了一個 Linux 環境,能更好地與 OpenCode 的功能相容。 --- ### 通知不顯示 -opencode Desktop 僅在以下情況下顯示系統通知: +OpenCode Desktop 僅在以下情況下顯示系統通知: -- 在您的作業系統設定中啟用 opencode 通知,並且 -- 應用程式視窗未聚焦。 +- 在作業系統設定中已為 OpenCode 啟用通知,且 +- 應用程式視窗未處於焦點狀態。 --- -### 重置桌面應用程式儲存(最後手段) +### 重設桌面應用程式儲存(最後手段) -如果應用程式無法啟動並且您無法從 UI 內部清除設定,請重置桌面應用程式的保存狀態。 +如果應用程式無法啟動且你無法從 UI 內部清除設定,請重設桌面應用程式的儲存狀態。 -1. 退出 opencode Desktop。 -2. 查找並刪除這些檔案(它們位於 opencode Desktop 應用程式資料目錄中): +1. 退出 OpenCode Desktop。 +2. 找到並刪除以下檔案(它們位於 OpenCode Desktop 應用程式資料目錄中): - `opencode.settings.dat`(桌面預設伺服器 URL) - `opencode.global.dat` 和 `opencode.workspace.*.dat`(UI 狀態,如最近的伺服器/專案) -快速找到目錄: +快速找到該目錄: -- **macOS**:Finder -> `Cmd+Shift+G` -> `~/Library/Application Support`(然後搜尋上面的檔名) -- **Linux**:在 `~/.local/share` 下搜尋上述檔名 -- **Windows**:按 `WIN+R` -> `%APPDATA%`(然後搜尋上面的檔名) +- **macOS**: Finder -> `Cmd+Shift+G` -> `~/Library/Application Support`(然後搜尋上述檔案名稱) +- **Linux**: 在 `~/.local/share` 下搜尋上述檔案名稱 +- **Windows**: 按 `WIN+R` -> `%APPDATA%`(然後搜尋上述檔案名稱) --- -## 尋求幫助 +## 取得幫助 -如果您遇到 opencode 問題: +如果你遇到 OpenCode 的問題: -1. **在 GitHub 上報告問題** +1. **在 GitHub 上回報問題** - 報告錯誤或請求功能的最佳方式是透過我們的 GitHub 儲存庫: + 回報 Bug 或請求功能的最佳方式是透過我們的 GitHub 儲存庫: [**github.com/anomalyco/opencode/issues**](https://github.com/anomalyco/opencode/issues) - 在建立新問題之前,請搜尋現有問題以查看您的問題是否已被報告。 + 在建立新 Issue 之前,請先搜尋已有的 Issue,看看你的問題是否已被回報。 2. **加入我們的 Discord** @@ -191,35 +191,34 @@ opencode Desktop 僅在以下情況下顯示系統通知: ## 常見問題 -以下是一些常見問題以及解決方法。 +以下是一些常見問題及其解決方法。 --- -### opencode 無法啟動 +### OpenCode 無法啟動 -1. 檢查日誌中是否有錯誤訊息 -2. 嘗試使用 `--print-logs` 執行以查看終端機中的輸出 -3. 確保您擁有最新版本 `opencode upgrade` +1. 檢查日誌中的錯誤訊息 +2. 嘗試使用 `--print-logs` 執行以在終端機中查看輸出 +3. 使用 `opencode upgrade` 確保你使用的是最新版本 --- ### 身分驗證問題 -1. 嘗試使用 TUI 中的 `/connect` 指令重新進行身分驗證 -2. 檢查您的 API 金鑰是否有效 -3. 確保您的網路允許連接到供應商的 API +1. 嘗試在 TUI 中使用 `/connect` 指令重新進行身分驗證 +2. 檢查你的 API 金鑰是否有效 +3. 確保你的網路允許連線到供應商的 API --- ### 模型不可用 -1. 檢查您是否已通過供應商的身分驗證 -2. 驗證配置中的模型名稱是否正確 +1. 檢查你是否已通過供應商的身分驗證 +2. 驗證設定中的模型名稱是否正確 3. 某些模型可能需要特定的存取權限或訂閱 -如果您遇到 `ProviderModelNotFoundError`,您很可能是錯誤的 -在某處引用模型。 -模型應該像這樣引用:`/` +如果你遇到 `ProviderModelNotFoundError`,很可能是在某處錯誤地參考了模型。 +模型應按如下方式參考:`/` 範例: @@ -227,18 +226,18 @@ opencode Desktop 僅在以下情況下顯示系統通知: - `openrouter/google/gemini-2.5-flash` - `opencode/kimi-k2` -要了解您可以存取哪些模型,請執行 `opencode models` +要查看你有權存取哪些模型,請執行 `opencode models` --- -### 供應商初始化錯誤 +### ProviderInitError -如果遇到 ProviderInitError,您的配置可能無效或損壞。 +如果你遇到 ProviderInitError,很可能是設定無效或已損壞。 -要解決這個問題: +要解決此問題: -1. 首先,按照 [供應商指南](/docs/providers) 驗證您的供應商是否已正確設定 -2. 如果問題仍然存在,請嘗試清除儲存的配置: +1. 首先,按照[供應商指南](/docs/providers)驗證你的供應商是否已正確設定 +2. 如果問題仍然存在,請嘗試清除已儲存的設定: ```bash rm -rf ~/.local/share/opencode @@ -246,13 +245,13 @@ opencode Desktop 僅在以下情況下顯示系統通知: 在 Windows 上,按 `WIN+R` 並刪除:`%USERPROFILE%\.local\share\opencode` -3. 使用 TUI 中的 `/connect` 指令向您的供應商重新進行身分驗證。 +3. 在 TUI 中使用 `/connect` 指令重新與供應商進行身分驗證。 --- ### AI_APICallError 和供應商套件問題 -如果您遇到 API 呼叫錯誤,這可能是由於過時的供應商套件造成的。 opencode 根據需要動態安裝供應商套件(OpenAI、Anthropic、Google 等)並將其快取在本地。 +如果你遇到 API 呼叫錯誤,可能是由於供應商套件過期導致的。OpenCode 會根據需要動態安裝供應商套件(OpenAI、Anthropic、Google 等)並將它們快取到本地。 要解決供應商套件問題: @@ -264,15 +263,15 @@ opencode Desktop 僅在以下情況下顯示系統通知: 在 Windows 上,按 `WIN+R` 並刪除:`%USERPROFILE%\.cache\opencode` -2. 重新啟動 opencode 以重新安裝最新的供應商套件 +2. 重新啟動 OpenCode 以重新安裝最新的供應商套件 -這將強制 opencode 下載最新版本的供應商套件,這通常可以解決模型參數和 API 更改的相容性問題。 +這將強制 OpenCode 下載最新版本的供應商套件,通常可以解決模型參數和 API 變更帶來的相容性問題。 --- -### 複製/貼上在 Linux 上不起作用 +### 在 Linux 上複製/貼上不可用 -Linux 使用者需要安裝以下剪貼簿公用程式之一才能使用複製/貼上功能: +Linux 使用者需要安裝以下剪貼簿工具之一,複製/貼上功能才能正常運作: **對於 X11 系統:** @@ -288,7 +287,7 @@ apt install -y xsel apt install -y wl-clipboard ``` -**對於無介面環境:** +**對於無頭環境:** ```bash apt install -y xvfb @@ -297,4 +296,4 @@ Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & export DISPLAY=:99.0 ``` -opencode 將檢測您是否使用 Wayland 並更喜歡 `wl-clipboard`,否則它將嘗試按 `xclip` 和 `xsel` 的順序尋找剪貼簿工具。 +OpenCode 會偵測你是否正在使用 Wayland 並優先使用 `wl-clipboard`,否則將按以下順序嘗試查找剪貼簿工具:`xclip` 和 `xsel`。 diff --git a/packages/web/src/content/docs/zh-tw/tui.mdx b/packages/web/src/content/docs/zh-tw/tui.mdx index f234bad8cc19..017507f20e27 100644 --- a/packages/web/src/content/docs/zh-tw/tui.mdx +++ b/packages/web/src/content/docs/zh-tw/tui.mdx @@ -5,21 +5,21 @@ description: 使用 OpenCode 終端機使用者介面。 import { Tabs, TabItem } from "@astrojs/starlight/components" -OpenCode 提供了一個互動式終端機介面或 TUI,供您與 LLM 一起處理專案。 +OpenCode 提供了一個互動式終端機介面(TUI),用於配合 LLM 處理您的專案。 -執行 OpenCode 會啟動當前目錄的 TUI。 +執行 OpenCode 即可啟動當前目錄的 TUI。 ```bash opencode ``` -或者您可以為特定的工作目錄啟動它。 +或者您可以為指定的工作目錄啟動它。 ```bash opencode /path/to/project ``` -進入 TUI 後,您可以透過訊息進行提示。 +進入 TUI 後,您可以輸入訊息進行提示。 ```text Give me a quick summary of the codebase. @@ -29,29 +29,29 @@ Give me a quick summary of the codebase. ## 檔案參考 -您可以使用 `@` 在訊息中引用檔案。這會在當前工作目錄中進行模糊檔案搜尋。 +您可以使用 `@` 在訊息中參考檔案。這會在當前工作目錄中進行模糊檔案搜尋。 :::tip -您還可以使用 `@` 來引用訊息中的檔案。 +您還可以使用 `@` 來參考訊息中的檔案。 ::: ```text "@packages/functions/src/api/index.ts" How is auth handled in @packages/functions/src/api/index.ts? ``` -檔案的內容會自動添加到對話中。 +檔案的內容會自動新增到對話中。 --- ## Bash 指令 -以 `!` 開始一條訊息以執行 shell 指令。 +以 `!` 開頭的訊息會作為 shell 指令執行。 ```bash frame="none" !ls -la ``` -指令的輸出將作為工具結果添加到對話中。 +指令的輸出會作為工具結果新增到對話中。 --- @@ -63,7 +63,7 @@ How is auth handled in @packages/functions/src/api/index.ts? /help ``` -大多數指令也有使用 `ctrl+x` 作為 Leader 鍵的按鍵綁定,其中 `ctrl+x` 是預設的 Leader 鍵。 [了解更多](/docs/keybinds)。 +大多數指令還支援以 `ctrl+x` 作為前導鍵的快速鍵,其中 `ctrl+x` 是預設前導鍵。[了解更多](/docs/keybinds)。 以下是所有可用的斜線指令: @@ -71,7 +71,7 @@ How is auth handled in @packages/functions/src/api/index.ts? ### connect -將供應商添加到 OpenCode。允許您從可用的供應商中進行選擇並添加其 API 金鑰。 +將供應商新增到 OpenCode。允許您從可用的供應商中選擇並新增其 API 金鑰。 ```bash frame="none" /connect @@ -81,85 +81,85 @@ How is auth handled in @packages/functions/src/api/index.ts? ### compact -壓縮當前工作階段。 _別名_:`/summarize` +壓縮當前工作階段。_別名_:`/summarize` ```bash frame="none" /compact ``` -**按鍵綁定:** `ctrl+x c` +**快速鍵:** `ctrl+x c` --- ### details -切換工具執行詳細資訊。 +切換工具執行詳情的顯示。 ```bash frame="none" /details ``` -**按鍵綁定:** `ctrl+x d` +**快速鍵:** `ctrl+x d` --- ### editor -打開外部編輯器來撰寫訊息。使用 `EDITOR` 環境變數中設定的編輯器。 [了解更多](#editor-setup)。 +開啟外部編輯器來撰寫訊息。使用 `EDITOR` 環境變數中設定的編輯器。[了解更多](#editor-setup)。 ```bash frame="none" /editor ``` -**按鍵綁定:** `ctrl+x e` +**快速鍵:** `ctrl+x e` --- ### exit -退出 OpenCode。 _別名_:`/quit`、`/q` +退出 OpenCode。_別名_:`/quit`、`/q` ```bash frame="none" /exit ``` -**按鍵綁定:** `ctrl+x q` +**快速鍵:** `ctrl+x q` --- ### export -將當前對話導出到 Markdown 並在預設編輯器中打開。使用 `EDITOR` 環境變數中設定的編輯器。 [了解更多](#editor-setup)。 +將當前對話匯出為 Markdown 並在預設編輯器中開啟。使用 `EDITOR` 環境變數中設定的編輯器。[了解更多](#editor-setup)。 ```bash frame="none" /export ``` -**按鍵綁定:** `ctrl+x x` +**快速鍵:** `ctrl+x x` --- ### help -顯示說明對話方塊。 +顯示說明對話框。 ```bash frame="none" /help ``` -**按鍵綁定:** `ctrl+x h` +**快速鍵:** `ctrl+x h` --- ### init -建立或更新 `AGENTS.md` 檔案。 [了解更多](/docs/rules)。 +建立或更新 `AGENTS.md` 檔案。[了解更多](/docs/rules)。 ```bash frame="none" /init ``` -**按鍵綁定:** `ctrl+x i` +**快速鍵:** `ctrl+x i` --- @@ -171,83 +171,82 @@ How is auth handled in @packages/functions/src/api/index.ts? /models ``` -**按鍵綁定:** `ctrl+x m` +**快速鍵:** `ctrl+x m` --- ### new -開始新的工作階段。 _別名_:`/clear` +開始新的工作階段。_別名_:`/clear` ```bash frame="none" /new ``` -**按鍵綁定:** `ctrl+x n` +**快速鍵:** `ctrl+x n` --- ### redo -重做之前撤銷的訊息。僅在使用 `/undo` 後可用。 +重做之前復原的訊息。僅在使用 `/undo` 後可用。 :::tip -任何檔案變更也將被恢復。 +所有檔案變更也會被恢復。 ::: -在內部,這使用 Git 來管理檔案變更。所以你的專案**需要 -是一個 Git 儲存庫**。 +在內部,這使用 Git 來管理檔案變更。因此您的專案**需要是一個 Git 儲存庫**。 ```bash frame="none" /redo ``` -**按鍵綁定:** `ctrl+x r` +**快速鍵:** `ctrl+x r` --- ### sessions -列出工作階段並在工作階段之間切換。 _別名_:`/resume`、`/continue` +列出工作階段並在工作階段之間切換。_別名_:`/resume`、`/continue` ```bash frame="none" /sessions ``` -**按鍵綁定:** `ctrl+x l` +**快速鍵:** `ctrl+x l` --- ### share -分享當前工作階段。 [了解更多](/docs/share)。 +分享當前工作階段。[了解更多](/docs/share)。 ```bash frame="none" /share ``` -**按鍵綁定:** `ctrl+x s` +**快速鍵:** `ctrl+x s` --- ### themes -列出可用的主題。 +列出可用主題。 ```bash frame="none" /theme ``` -**按鍵綁定:** `ctrl+x t` +**快速鍵:** `ctrl+x t` --- ### thinking -切換對話中思考/推理區塊的可見性。啟用後,您可以看到支援擴展思考的模型的推理過程。 +切換對話中思考/推理區塊的可見性。啟用後,您可以看到支援擴充思考的模型的推理過程。 :::note -該指令僅控制是否**顯示** - 它不啟用或禁用模型的推理功能。要切換實際推理功能,請使用 `ctrl+t` 循環切換模型變體。 +此指令僅控制思考區塊是否**顯示** — 它不會啟用或停用模型的推理能力。要切換實際的推理能力,請使用 `ctrl+t` 循環切換模型變體。 ::: ```bash frame="none" @@ -258,26 +257,25 @@ How is auth handled in @packages/functions/src/api/index.ts? ### undo -撤銷對話中的最後一條訊息。刪除最近的使用者訊息、所有後續回應以及任何檔案變更。 +復原對話中的最後一條訊息。移除最近的使用者訊息、所有後續回應以及所有檔案變更。 :::tip -所做的任何檔案變更也將被恢復。 +所做的任何檔案變更也會被還原。 ::: -在內部,這使用 Git 來管理檔案變更。所以你的專案**需要 -是一個 Git 儲存庫**。 +在內部,這使用 Git 來管理檔案變更。因此您的專案**需要是一個 Git 儲存庫**。 ```bash frame="none" /undo ``` -**按鍵綁定:** `ctrl+x u` +**快速鍵:** `ctrl+x u` --- ### unshare -取消分享當前工作階段。 [了解更多](/docs/share#un-sharing)。 +取消分享當前工作階段。[了解更多](/docs/share#un-sharing)。 ```bash frame="none" /unshare @@ -301,8 +299,8 @@ How is auth handled in @packages/functions/src/api/index.ts? export EDITOR="code --wait" ``` - To make it permanent, add this to your shell profile; - `~/.bashrc`, `~/.zshrc`, etc. + 要使其永久生效,請將其新增到您的 shell 設定檔中; + `~/.bashrc`、`~/.zshrc` 等。 @@ -315,8 +313,7 @@ How is auth handled in @packages/functions/src/api/index.ts? set EDITOR=code --wait ``` - To make it permanent, use **System Properties** > **Environment - Variables**. + 要使其永久生效,請使用**系統內容** > **環境變數**。 @@ -329,12 +326,12 @@ How is auth handled in @packages/functions/src/api/index.ts? $env:EDITOR = "code --wait" ``` - To make it permanent, add this to your PowerShell profile. + 要使其永久生效,請將其新增到您的 PowerShell 設定檔中。 -流行的編輯器選項包括: +常用的編輯器選項包括: - `code` - Visual Studio Code - `cursor` - Cursor @@ -342,20 +339,20 @@ How is auth handled in @packages/functions/src/api/index.ts? - `nvim` - Neovim 編輯器 - `vim` - Vim 編輯器 - `nano` - Nano 編輯器 -- `notepad` - Windows 記事本 +- `notepad` - Notepad(Windows 記事本) - `subl` - Sublime Text :::note -某些編輯器(例如 VS Code)需要以 `--wait` 旗標啟動。 +某些編輯器(如 VS Code)需要以 `--wait` 旗標啟動。 ::: -某些編輯器需要命令列參數才能在阻止模式下執行。 `--wait` 旗標使編輯器處理程序阻塞直到關閉。 +某些編輯器需要命令列參數才能以阻塞模式執行。`--wait` 旗標使編輯器程序阻塞直到關閉。 --- -## 配置 +## 設定 -您可以透過 OpenCode 設定檔自定義 TUI 行為。 +您可以透過 OpenCode 設定檔自訂 TUI 行為。 ```json title="opencode.json" { @@ -371,20 +368,20 @@ How is auth handled in @packages/functions/src/api/index.ts? ### 選項 -- `scroll_acceleration` - 啟用 macOS 風格的捲動加速,實現平滑、自然的捲動。啟用後,捲動速度會隨著快速捲動手勢而增加,並在較慢的移動時保持精確。 **此設定優先於 `scroll_speed`,並在啟用時覆寫它。** -- `scroll_speed` - 控制使用捲動指令時 TUI 捲動的速度(最小值:`1`)。預設為 `3`。 **注意:如果 `scroll_acceleration.enabled` 設定為 `true`,則忽略此設定。** +- `scroll_acceleration` - 啟用 macOS 風格的捲動加速,實現平滑、自然的捲動體驗。啟用後,快速捲動時速度會增加,慢速移動時保持精確。**此設定優先於 `scroll_speed`,啟用時會覆蓋它。** +- `scroll_speed` - 控制使用捲動指令時 TUI 的捲動速度(最小值:`1`)。預設為 `3`。**注意:如果 `scroll_acceleration.enabled` 設定為 `true`,則此設定會被忽略。** --- -## 自定義 +## 自訂 -您可以使用指令面板(`ctrl+x h` 或 `/help`)自定義 TUI 視圖的各個方面。這些設定在重新啟動後仍然存在。 +您可以使用指令面板(`ctrl+x h` 或 `/help`)自訂 TUI 檢視的各個方面。這些設定在重新啟動後仍會保留。 --- #### 使用者名稱顯示 -切換您的使用者名稱是否出現在聊天訊息中。透過以下方式存取: +切換您的使用者名稱是否顯示在聊天訊息中。透過以下方式存取: -- 指令面板:搜尋「使用者名稱」或「隱藏使用者名稱」 -- 該設定會自動保留並在 TUI 工作階段中被記住 +- 指令面板:搜尋「username」或「hide username」 +- 該設定會自動儲存,並在各個 TUI 工作階段中保持記憶 diff --git a/packages/web/src/content/docs/zh-tw/web.mdx b/packages/web/src/content/docs/zh-tw/web.mdx index 548307ffb21b..f55f9956065a 100644 --- a/packages/web/src/content/docs/zh-tw/web.mdx +++ b/packages/web/src/content/docs/zh-tw/web.mdx @@ -1,39 +1,39 @@ --- title: Web -description: 在瀏覽器中使用 opencode。 +description: 在瀏覽器中使用 OpenCode。 --- -opencode 可以在瀏覽器中作為 Web 應用程式執行,無需終端機即可提供同樣強大的 AI 程式碼體驗。 +OpenCode 可以作為 Web 應用程式在瀏覽器中執行,無需終端機即可獲得同樣強大的 AI 編碼體驗。 -![opencode Web - 新工作階段](../../../assets/web/web-homepage-new-session.png) +![OpenCode Web - New Session](../../../assets/web/web-homepage-new-session.png) -## 入門 +## 快速開始 -透過執行以下指令啟動 Web 介面: +執行以下指令啟動 Web 介面: ```bash opencode web ``` -這將在 `127.0.0.1` 上啟動一個具有隨機可用連接埠的本地伺服器,並自動在預設瀏覽器中打開 opencode。 +這會在 `127.0.0.1` 上啟動一個本地伺服器,使用隨機可用連接埠,並自動在預設瀏覽器中開啟 OpenCode。 :::caution -如果未設定 `OPENCODE_SERVER_PASSWORD`,伺服器將不安全。這對於本地使用來說很好,但應該針對網路存取進行設定。 +如果未設定 `OPENCODE_SERVER_PASSWORD`,伺服器將沒有安全保護。本地使用沒有問題,但在網路存取時應當設定密碼。 ::: :::tip[Windows 使用者] -為了獲得最佳體驗,請從 [WSL](/docs/windows-wsl) 而不是 PowerShell 執行 `opencode web`。這確保了正確的檔案系統存取和終端機整合。 +為獲得最佳體驗,建議從 [WSL](/docs/windows-wsl) 而非 PowerShell 執行 `opencode web`。這可以確保正確的檔案系統存取和終端機整合。 ::: --- -## 配置 +## 設定 -您可以使用命令列旗標或在 [設定檔](/docs/config) 中配置 Web 伺服器。 +你可以透過命令列旗標或[設定檔](/docs/config)來設定 Web 伺服器。 ### 連接埠 -預設情況下,opencode 選擇一個可用連接埠。您可以指定一個連接埠: +預設情況下,OpenCode 會選擇一個可用連接埠。你也可以指定連接埠: ```bash opencode web --port 4096 @@ -41,13 +41,13 @@ opencode web --port 4096 ### 主機名稱 -預設情況下,伺服器綁定到 `127.0.0.1`(僅限 localhost)。要使 opencode 在您的網路上可存取: +預設情況下,伺服器繫結到 `127.0.0.1`(僅限本地存取)。要使 OpenCode 在網路中可存取: ```bash opencode web --hostname 0.0.0.0 ``` -當使用 `0.0.0.0` 時,opencode 將顯示本地位址和網路位址: +使用 `0.0.0.0` 時,OpenCode 會同時顯示本地位址和網路位址: ``` Local access: http://localhost:4096 @@ -56,15 +56,15 @@ opencode web --hostname 0.0.0.0 ### mDNS 探索 -啟用 mDNS 以使您的伺服器在本地網路上可探索: +啟用 mDNS 可以讓你的伺服器在本地網路中被自動探索: ```bash opencode web --mdns ``` -這會自動將主機名稱設定為 `0.0.0.0` 並將伺服器廣播為 `opencode.local`。 +這會自動將主機名稱設定為 `0.0.0.0`,並將伺服器廣播為 `opencode.local`。 -您可以自定義 mDNS 網域名稱以在同一網路上執行多個實例: +你可以自訂 mDNS 網域名稱,以便在同一網路中執行多個實例: ```bash opencode web --mdns --mdns-domain myproject.local @@ -72,7 +72,7 @@ opencode web --mdns --mdns-domain myproject.local ### CORS -允許 CORS 的其他域(對於自定義前端有用): +要為 CORS 新增額外的允許網域(適用於自訂前端): ```bash opencode web --cors https://example.com @@ -80,53 +80,53 @@ opencode web --cors https://example.com ### 身分驗證 -要保護存取,請使用 `OPENCODE_SERVER_PASSWORD` 環境變數設定密碼: +要保護伺服器存取,可以透過 `OPENCODE_SERVER_PASSWORD` 環境變數設定密碼: ```bash OPENCODE_SERVER_PASSWORD=secret opencode web ``` -使用者名稱預設為 `opencode`,但可以使用 `OPENCODE_SERVER_USERNAME` 進行更改。 +使用者名稱預設為 `opencode`,可以透過 `OPENCODE_SERVER_USERNAME` 進行更改。 --- ## 使用 Web 介面 -啟動後,Web 介面將提供對 opencode 工作階段的存取。 +啟動後,Web 介面提供對 OpenCode 工作階段的存取。 ### 工作階段 -從主頁查看和管理您的工作階段。您可以查看活動工作階段並開始新工作階段。 +在主頁上查看和管理你的工作階段。你可以查看進行中的工作階段,也可以建立新的工作階段。 -![opencode Web - 活動工作階段](../../../assets/web/web-homepage-active-session.png) +![OpenCode Web - Active Session](../../../assets/web/web-homepage-active-session.png) ### 伺服器狀態 -單擊「查看伺服器」可查看連接的伺服器及其狀態。 +點擊「See Servers」可以查看已連線的伺服器及其狀態。 -![opencode Web - 查看伺服器](../../../assets/web/web-homepage-see-servers.png) +![OpenCode Web - See Servers](../../../assets/web/web-homepage-see-servers.png) --- ## 連接終端機 -您可以將終端機 TUI 連接到正在執行的 Web 伺服器: +你可以將終端機 TUI 連接到正在執行的 Web 伺服器: ```bash -# Start the web server +# 啟動 Web 伺服器 opencode web --port 4096 -# In another terminal, attach the TUI +# 在另一個終端機中連接 TUI opencode attach http://localhost:4096 ``` -這允許您同時使用 Web 介面和終端機,共享相同的工作階段和狀態。 +這樣你就可以同時使用 Web 介面和終端機,共享相同的工作階段和狀態。 --- ## 設定檔 -您還可以在 `opencode.json` 設定檔中配置伺服器設定: +你也可以在 `opencode.json` 設定檔中設定伺服器選項: ```json { @@ -139,4 +139,4 @@ opencode attach http://localhost:4096 } ``` -命令列旗標優先於設定檔設定。 +命令列旗標的優先順序高於設定檔中的設定。 diff --git a/packages/web/src/content/docs/zh-tw/windows-wsl.mdx b/packages/web/src/content/docs/zh-tw/windows-wsl.mdx index db858db02e2a..66b203acc782 100644 --- a/packages/web/src/content/docs/zh-tw/windows-wsl.mdx +++ b/packages/web/src/content/docs/zh-tw/windows-wsl.mdx @@ -1,37 +1,37 @@ --- title: Windows (WSL) -description: 在 Windows 透過 WSL 使用 opencode。 +description: 透過 WSL 在 Windows 上執行 OpenCode 以獲得最佳體驗。 --- import { Steps } from "@astrojs/starlight/components" -雖然 opencode 可以直接在 Windows 上執行,但為了獲得最佳體驗,我們建議使用 [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)。WSL 提供了可與 opencode 功能順暢配合的 Linux 環境。 +雖然 OpenCode 可以直接在 Windows 上執行,但我們推薦使用 [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) 以獲得最佳體驗。WSL 提供了一個 Linux 環境,能夠與 OpenCode 的各項功能無縫配合。 -:::tip[為什麼要用 WSL?] -WSL 提供更好的檔案系統效能、完整的terminal支援,以及與 opencode 依賴之開發工具的相容性。 +:::tip[為什麼選擇 WSL?] +WSL 提供更出色的檔案系統效能、完整的終端機支援,以及與 OpenCode 所依賴的開發工具的良好相容性。 ::: --- -## 設定 +## 安裝設定 1. **安裝 WSL** - 如果你尚未安裝,請依照 Microsoft 官方指南[安裝 WSL](https://learn.microsoft.com/en-us/windows/wsl/install)。 + 如果尚未安裝,請參照 Microsoft 官方指南[安裝 WSL](https://learn.microsoft.com/en-us/windows/wsl/install)。 -2. **在 WSL 中安裝 opencode** +2. **在 WSL 中安裝 OpenCode** - 完成 WSL 設定後,打開 WSL terminal,並使用其中一種[安裝方式](/docs/)安裝 opencode。 + WSL 設定完成後,開啟 WSL 終端機,使用任一[安裝方式](/docs/)安裝 OpenCode。 ```bash curl -fsSL https://opencode.ai/install | bash ``` -3. **從 WSL 使用 opencode** +3. **從 WSL 中使用 OpenCode** - 移動到你的專案目錄(可透過 `/mnt/c/`、`/mnt/d/` 等路徑存取 Windows 檔案),然後執行 opencode。 + 導航到你的專案目錄(透過 `/mnt/c/`、`/mnt/d/` 等路徑存取 Windows 檔案),然後執行 OpenCode。 ```bash cd /mnt/c/Users/YourName/project @@ -44,54 +44,53 @@ WSL 提供更好的檔案系統效能、完整的terminal支援,以及與 open ## 桌面應用程式 + WSL 伺服器 -如果你偏好使用 opencode 桌面應用程式,但希望在 WSL 內執行伺服器: +如果你希望使用 OpenCode 桌面應用程式,同時在 WSL 中執行伺服器: -1. **在 WSL 中啟動伺服器**,並使用 `--hostname 0.0.0.0` 允許外部連線: +1. **在 WSL 中啟動伺服器**,新增 `--hostname 0.0.0.0` 以允許外部連線: ```bash opencode serve --hostname 0.0.0.0 --port 4096 ``` -2. **將桌面應用程式連線到** `http://localhost:4096` +2. **在桌面應用程式中連線到** `http://localhost:4096` :::note -若你的環境中 `localhost` 無法使用,請改用 WSL 的 IP 位址連線(在 WSL 執行:`hostname -I`),並使用 `http://:4096`。 +如果 `localhost` 在你的環境中無法使用,請改用 WSL 的 IP 位址進行連線(在 WSL 中執行:`hostname -I`),使用 `http://:4096`。 ::: :::caution -使用 `--hostname 0.0.0.0` 時,請設定 `OPENCODE_SERVER_PASSWORD` 來保護伺服器。 +使用 `--hostname 0.0.0.0` 時,請設定 `OPENCODE_SERVER_PASSWORD` 以保護伺服器安全。 +::: ```bash OPENCODE_SERVER_PASSWORD=your-password opencode serve --hostname 0.0.0.0 ``` -::: - --- -## Web 用戶端 + WSL +## Web 客戶端 + WSL -在 Windows 上取得最佳 Web 體驗: +要在 Windows 上獲得最佳的 Web 體驗: -1. **請在 WSL terminal執行 `opencode web`**,而不是在 PowerShell 執行: +1. **在 WSL 終端機中執行 `opencode web`**,而非在 PowerShell 中執行: ```bash opencode web --hostname 0.0.0.0 ``` -2. **在 Windows 瀏覽器中開啟** `http://localhost:`(opencode 會輸出該 URL) +2. **在 Windows 瀏覽器中存取** `http://localhost:`(OpenCode 會輸出該 URL) -從 WSL 執行 `opencode web` 可確保正確的檔案系統存取與terminal整合,同時仍可由 Windows 瀏覽器使用。 +從 WSL 中執行 `opencode web` 可確保正確的檔案系統存取和終端機整合,同時仍可透過 Windows 瀏覽器進行存取。 --- ## 存取 Windows 檔案 -WSL 可透過 `/mnt/` 目錄存取你所有的 Windows 檔案: +WSL 可以透過 `/mnt/` 目錄存取你的所有 Windows 檔案: -- `C:` drive → `/mnt/c/` -- `D:` drive → `/mnt/d/` -- 其他磁碟機也相同 +- `C:` 磁碟 → `/mnt/c/` +- `D:` 磁碟 → `/mnt/d/` +- 其他磁碟以此類推... 範例: @@ -101,13 +100,13 @@ opencode ``` :::tip -為了更流暢的使用體驗,建議將你的儲存庫 clone 或複製到 WSL 檔案系統(例如 `~/code/`)中,再從那裡執行 opencode。 +為了獲得更流暢的體驗,建議將儲存庫克隆或複製到 WSL 檔案系統中(例如 `~/code/` 目錄下),然後在該位置執行 OpenCode。 ::: --- -## 提示 +## 使用技巧 -- 即使專案存放在 Windows 磁碟機上,也建議在 WSL 中執行 opencode,檔案存取會更順暢 -- 可將 opencode 與 VS Code 的 [WSL 擴充套件](https://code.visualstudio.com/docs/remote/wsl)搭配使用,建立整合式開發流程 -- opencode 的設定與工作階段會儲存在 WSL 環境中的 `~/.local/share/opencode/` +- 對於儲存在 Windows 磁碟上的專案,在 WSL 中執行 OpenCode 即可無縫存取檔案 +- 搭配 VS Code 的 [WSL 擴充套件](https://code.visualstudio.com/docs/remote/wsl) 使用 OpenCode,打造一體化的開發工作流程 +- OpenCode 的設定和工作階段資料儲存在 WSL 環境中的 `~/.local/share/opencode/` diff --git a/packages/web/src/content/docs/zh-tw/zen.mdx b/packages/web/src/content/docs/zh-tw/zen.mdx index 142597fadccd..5216194a9382 100644 --- a/packages/web/src/content/docs/zh-tw/zen.mdx +++ b/packages/web/src/content/docs/zh-tw/zen.mdx @@ -1,66 +1,57 @@ --- title: Zen -description: opencode 提供的精選模型列表。 +description: 由 OpenCode 提供的精選模型列表。 --- import config from "../../../../config.mjs" export const console = config.console export const email = `mailto:${config.email}` -OpenCode Zen 是 opencode 團隊提供的經過測試和驗證的模型列表。 +OpenCode Zen 是由 OpenCode 團隊提供的一組經過測試和驗證的模型列表。 :::note OpenCode Zen 目前處於測試階段。 ::: -Zen 的工作方式與 opencode 中的任何其他供應商一樣。您登入 OpenCode Zen 並獲取 -您的 API 金鑰。它是**完全可選的**,您不需要使用它即可使用 -opencode。 +Zen 的工作方式與 OpenCode 中的任何其他供應商相同。你只需登入 OpenCode Zen 並取得你的 API 金鑰。它是**完全選用的**,你無需使用它也能正常使用 OpenCode。 --- ## 背景 -市面上有很多模型,但其中只有少數幾個 -這些模型可以很好地用作程式碼代理。此外,大多數供應商都 -配置非常不同;所以你會得到截然不同的性能和質量。 +市面上有大量的模型,但其中只有少數能夠很好地充當編碼代理。此外,大多數供應商的設定方式差異很大,因此你獲得的效能和品質也會截然不同。 :::tip -我們測試了一組精選的與 opencode 配合良好的模型和供應商。 +我們測試了一組與 OpenCode 配合良好的精選模型和供應商。 ::: -因此,如果您透過 OpenRouter 之類的東西使用模型,那麼您永遠無法 -確定您是否獲得了您想要的模型的最佳版本。 +所以如果你透過 OpenRouter 之類的服務使用模型,你永遠無法確定是否獲得了你想要的模型的最佳版本。 -為了解決這個問題,我們做了幾件事: +為了解決這個問題,我們做了以下幾件事: -1. 我們測試了一組選定的模型,並與他們的團隊討論瞭如何 - 最好執行它們。 -2. 然後我們與一些供應商合作以確保這些服務得到服務 - 正確。 -3. 最後,我們對模型/供應商的組合進行了基準測試並提出了 - 並附上一份我們覺得不錯的推薦清單。 +1. 我們測試了一組精選的模型,並與它們的團隊討論了最佳執行方式。 +2. 然後我們與幾家供應商合作,確保這些模型能被正確地提供服務。 +3. 最後,我們對模型與供應商的組合進行了基準測試,整理出了一份我們有信心推薦的列表。 -OpenCode Zen 是一個 AI 閘道,可讓您存取這些模型。 +OpenCode Zen 是一個 AI 閘道,讓你可以存取這些模型。 --- -## 它是如何運作的 +## 工作原理 -OpenCode Zen 的工作方式與 opencode 中的任何其他供應商一樣。 +OpenCode Zen 的工作方式與 OpenCode 中的任何其他供應商相同。 -1. 您登入 **OpenCode Zen**,添加您的帳單 - 詳細資訊,然後複製您的 API 金鑰。 -2. 您在 TUI 中執行 `/connect` 指令,選擇 OpenCode Zen,然後貼上您的 API 金鑰。 -3. 在 TUI 中執行 `/models` 以查看我們推薦的模型列表。 +1. 登入 **OpenCode Zen**,新增你的帳單資訊,然後複製你的 API 金鑰。 +2. 在 TUI 中執行 `/connect` 指令,選擇 OpenCode Zen,然後貼上你的 API 金鑰。 +3. 在 TUI 中執行 `/models` 查看我們推薦的模型列表。 -您需要按請求付費,並且可以將點數添加到您的帳戶中。 +你按請求付費,並且可以向你的帳戶中儲值。 --- ## 端點 -您還可以透過以下 API 端點存取我們的模型。 +你還可以透過以下 API 端點存取我們的模型。 | 模型 | 模型 ID | 端點 | AI SDK 套件 | | ------------------ | ------------------ | -------------------------------------------------- | --------------------------- | @@ -82,10 +73,11 @@ OpenCode Zen 的工作方式與 opencode 中的任何其他供應商一樣。 | Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | | Gemini 3 Pro | gemini-3-pro | `https://opencode.ai/zen/v1/models/gemini-3-pro` | `@ai-sdk/google` | | Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` | +| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| MiniMax M2.1 Free | minimax-m2.1-free | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | +| GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | -| GLM 4.7 Free | glm-4.7-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.6 | glm-4.6 | `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` | | Kimi K2.5 Free | kimi-k2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | @@ -94,15 +86,13 @@ OpenCode Zen 的工作方式與 opencode 中的任何其他供應商一樣。 | Qwen3 Coder 480B | qwen3-coder | `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` | -opencode 配置中的 [模型編號](/docs/config/#models) -使用格式 `opencode/`。例如,對於 GPT 5.2 Codex,您將 -在您的配置中使用 `opencode/gpt-5.2-codex`。 +在 OpenCode 設定中,[模型 ID](/docs/config/#models) 使用 `opencode/` 格式。例如,對於 GPT 5.2 Codex,你需要在設定中使用 `opencode/gpt-5.2-codex`。 --- ### 模型 -您可以從以下位置獲取可用模型及其元數據的完整列表: +你可以從以下位址取得可用模型及其中繼資料的完整列表: ``` https://opencode.ai/zen/v1/models @@ -112,135 +102,127 @@ https://opencode.ai/zen/v1/models ## 定價 -我們支援按用量付費模式。以下是**每 100 萬 Tokens 的價格**。 - -| 模型 | 輸入 | 輸出 | 快取讀取 | 快取寫入 | -| --------------------------------- | ------ | ------ | -------- | -------- | -| Big Pickle | Free | Free | Free | - | -| MiniMax M2.1 Free | Free | Free | Free | - | -| MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | -| GLM 4.7 Free | Free | Free | Free | - | -| GLM 4.7 | $0.60 | $2.20 | $0.10 | - | -| GLM 4.6 | $0.60 | $2.20 | $0.10 | - | -| Kimi K2.5 Free | Free | Free | Free | - | -| Kimi K2.5 | $0.60 | $3.00 | $0.08 | - | -| Kimi K2 Thinking | $0.40 | $2.50 | - | - | -| Kimi K2 | $0.40 | $2.50 | - | - | -| Qwen3 Coder 480B | $0.45 | $1.50 | - | - | -| Claude Sonnet 4.5 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | -| Claude Sonnet 4.5 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | -| Claude Sonnet 4 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | -| Claude Sonnet 4 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | -| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | -| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | -| Claude Opus 4.6 (≤ 200K tokens) | $5.00 | $25.00 | $0.50 | $6.25 | -| Claude Opus 4.6 (> 200K tokens) | $10.00 | $37.50 | $1.00 | $12.50 | -| Claude Opus 4.5 | $5.00 | $25.00 | $0.50 | $6.25 | -| Claude Opus 4.1 | $15.00 | $75.00 | $1.50 | $18.75 | -| Gemini 3 Pro (≤ 200K tokens) | $2.00 | $12.00 | $0.20 | - | -| Gemini 3 Pro (> 200K tokens) | $4.00 | $18.00 | $0.40 | - | -| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | -| GPT 5.2 | $1.75 | $14.00 | $0.175 | - | -| GPT 5.2 Codex | $1.75 | $14.00 | $0.175 | - | -| GPT 5.1 | $1.07 | $8.50 | $0.107 | - | -| GPT 5.1 Codex | $1.07 | $8.50 | $0.107 | - | -| GPT 5.1 Codex Max | $1.25 | $10.00 | $0.125 | - | -| GPT 5.1 Codex Mini | $0.25 | $2.00 | $0.025 | - | -| GPT 5 | $1.07 | $8.50 | $0.107 | - | -| GPT 5 Codex | $1.07 | $8.50 | $0.107 | - | -| GPT 5 Nano | Free | Free | Free | - | - -您可能會在使用歷史記錄中注意到 _Claude Haiku 3.5_。這是一個 [低成本模型](/docs/config/#models),用於生成工作階段標題。 +我們支援按量付費模式。以下是**每 100 萬 Token** 的價格。 + +| 模型 | 輸入 | 輸出 | 快取讀取 | 快取寫入 | +| -------------------------------- | ------ | ------ | -------- | -------- | +| Big Pickle | 免費 | 免費 | 免費 | - | +| MiniMax M2.5 Free | 免費 | 免費 | 免費 | - | +| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | - | +| MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | +| GLM 5 | $1.00 | $3.20 | $0.20 | - | +| GLM 4.7 | $0.60 | $2.20 | $0.10 | - | +| GLM 4.6 | $0.60 | $2.20 | $0.10 | - | +| Kimi K2.5 Free | 免費 | 免費 | 免費 | - | +| Kimi K2.5 | $0.60 | $3.00 | $0.08 | - | +| Kimi K2 Thinking | $0.40 | $2.50 | - | - | +| Kimi K2 | $0.40 | $2.50 | - | - | +| Qwen3 Coder 480B | $0.45 | $1.50 | - | - | +| Claude Sonnet 4.5 (≤ 200K Token) | $3.00 | $15.00 | $0.30 | $3.75 | +| Claude Sonnet 4.5 (> 200K Token) | $6.00 | $22.50 | $0.60 | $7.50 | +| Claude Sonnet 4 (≤ 200K Token) | $3.00 | $15.00 | $0.30 | $3.75 | +| Claude Sonnet 4 (> 200K Token) | $6.00 | $22.50 | $0.60 | $7.50 | +| Claude Haiku 4.5 | $1.00 | $5.00 | $0.10 | $1.25 | +| Claude Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 | +| Claude Opus 4.6 (≤ 200K Token) | $5.00 | $25.00 | $0.50 | $6.25 | +| Claude Opus 4.6 (> 200K Token) | $10.00 | $37.50 | $1.00 | $12.50 | +| Claude Opus 4.5 | $5.00 | $25.00 | $0.50 | $6.25 | +| Claude Opus 4.1 | $15.00 | $75.00 | $1.50 | $18.75 | +| Gemini 3 Pro (≤ 200K Token) | $2.00 | $12.00 | $0.20 | - | +| Gemini 3 Pro (> 200K Token) | $4.00 | $18.00 | $0.40 | - | +| Gemini 3 Flash | $0.50 | $3.00 | $0.05 | - | +| GPT 5.2 | $1.75 | $14.00 | $0.175 | - | +| GPT 5.2 Codex | $1.75 | $14.00 | $0.175 | - | +| GPT 5.1 | $1.07 | $8.50 | $0.107 | - | +| GPT 5.1 Codex | $1.07 | $8.50 | $0.107 | - | +| GPT 5.1 Codex Max | $1.25 | $10.00 | $0.125 | - | +| GPT 5.1 Codex Mini | $0.25 | $2.00 | $0.025 | - | +| GPT 5 | $1.07 | $8.50 | $0.107 | - | +| GPT 5 Codex | $1.07 | $8.50 | $0.107 | - | +| GPT 5 Nano | 免費 | 免費 | 免費 | - | + +你可能會在使用記錄中看到 _Claude Haiku 3.5_。這是一個[低成本模型](/docs/config/#models),用於產生工作階段標題。 :::note -信用卡費用按成本轉嫁(4.4% + 每筆交易 0.30 美元);除此之外我們不收取任何費用。 +信用卡手續費按成本轉嫁(每筆交易 4.4% + $0.30);除此之外我們不收取任何額外費用。 ::: -免費模型: +免費模型說明: -- GLM 4.7 免費版在 opencode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。 -- Kimi K2.5 Free 在 opencode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。 -- MiniMax M2.1 免費版在 opencode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。 -- Big Pickle 是一個秘密模型,在 opencode 上限時免費。團隊正在利用這段時間收集回饋並改進模型。 +- Kimi K2.5 Free 在 OpenCode 上限時免費提供。團隊正在利用這段時間收集回饋並改進模型。 +- MiniMax M2.5 Free 在 OpenCode 上限時免費提供。團隊正在利用這段時間收集回饋並改進模型。 +- Big Pickle 是一個隱身模型,在 OpenCode 上限時免費提供。團隊正在利用這段時間收集回饋並改進模型。 -如果您有任何疑問,請聯絡我們。 +如有任何疑問,請聯絡我們。 --- -### 自動重新載入 +### 自動儲值 -如果您的餘額低於 5 美元,Zen 將自動加值 20 美元。 +如果你的餘額低於 $5,Zen 將自動儲值 $20。 -您可以更改自動加值金額。您還可以完全禁用自動重新載入。 +你可以更改自動儲值的金額,也可以完全停用自動儲值功能。 --- -### 每月限額 +### 月度限額 -您還可以為整個工作區和每個工作區設定每月使用限制 -你的團隊的成員。 +你還可以為整個工作區以及團隊中的每個成員設定月度使用限額。 -例如,假設您將每月使用限額設定為 20 美元,Zen 將不會使用 -一個月超過 20 美元。但如果你啟用了自動重新載入,Zen 可能會結束 -如果您的餘額低於 5 美元,則向您收取超過 20 美元的費用。 +例如,假設你將月度使用限額設為 $20,Zen 在一個月內的使用量將不會超過 $20。但如果你啟用了自動儲值,當餘額低於 $5 時,Zen 可能會向你收取超過 $20 的費用。 --- ## 隱私 -我們所有的模型都在美國託管。我們的供應商遵循零保留政策,不會將您的數據用於模型訓練,但以下情況除外: +我們所有的模型都託管在美國。我們的供應商遵循零保留政策,不會將你的資料用於模型訓練,但以下情況除外: -- Big Pickle:在免費期間,收集的數據可用於改進模型。 -- GLM 4.7 免費:在免費期間,收集的數據可用於改進模型。 -- Kimi K2.5 免費:在免費期間,收集的數據可用於改進模型。 -- MiniMax M2.1 免費:在免費期間,收集的數據可用於改進模型。 -- OpenAI API:根據 [OpenAI 的數據政策](https://platform.openai.com/docs/guides/your-data),請求將保留 30 天。 -- Anthropic API:根據 [Anthropic 的數據政策](https://docs.anthropic.com/en/docs/claude-code/data-usage),請求將保留 30 天。 +- Big Pickle:在免費期間,收集的資料可能會被用於改進模型。 +- Kimi K2.5 Free:在免費期間,收集的資料可能會被用於改進模型。 +- MiniMax M2.5 Free:在免費期間,收集的資料可能會被用於改進模型。 +- OpenAI API:請求會根據 [OpenAI 資料政策](https://platform.openai.com/docs/guides/your-data)保留 30 天。 +- Anthropic API:請求會根據 [Anthropic 資料政策](https://docs.anthropic.com/en/docs/claude-code/data-usage)保留 30 天。 --- -## 對於團隊 +## 團隊版 -Zen 對團隊也很有效。您可以邀請隊友、分配角色、精選 -您的團隊使用的模型等等。 +Zen 也非常適合團隊使用。你可以邀請隊友、分配角色、管理團隊使用的模型等。 :::note -作為測試版的一部分,工作區目前對團隊免費。 +作為測試版的一部分,工作區功能目前對團隊免費開放。 ::: -作為測試版的一部分,管理工作區目前對團隊免費。我們將會 -很快就會分享更多有關定價的細節。 +作為測試版的一部分,管理工作區目前對團隊免費。我們將很快公布更多定價詳情。 --- ### 角色 -您可以邀請團隊成員到您的工作區並分配角色: +你可以邀請團隊成員加入你的工作區並分配角色: -- **管理員**:管理模型、成員、API 金鑰和計費 +- **管理員**:管理模型、成員、API 金鑰和帳單 - **成員**:僅管理自己的 API 金鑰 -管理員還可以為每個成員設定每月支出限額,以控制成本。 +管理員還可以為每個成員設定月度支出限額,以控制成本。 --- ### 模型存取 -管理員可以啟用或禁用工作區的特定模型。對禁用模型發出的請求將返回錯誤。 +管理員可以啟用或停用工作區中的特定模型。對已停用模型發出的請求將回傳錯誤。 -這對於您想要禁用以下模型的情況很有用: -收集數據。 +這在你想要停用某個會收集資料的模型時非常有用。 --- -### 帶上你自己的金鑰 +### 自帶金鑰 -您可以使用自己的 OpenAI 或 Anthropic API 金鑰,同時仍然存取 Zen 中的其他模型。 +你可以使用自己的 OpenAI 或 Anthropic API 金鑰,同時仍然可以存取 Zen 中的其他模型。 -當您使用自己的金鑰時,Tokens 將由供應商直接計費,而不是由 Zen。 +當你使用自己的金鑰時,Token 費用由供應商直接計費,而非透過 Zen 計費。 -例如,您的組織可能已經擁有 OpenAI 或 Anthropic 的金鑰 -你想使用它而不是 Zen 提供的。 +例如,你的組織可能已經擁有 OpenAI 或 Anthropic 的金鑰,你希望使用它們而不是 Zen 提供的金鑰。 --- @@ -248,7 +230,7 @@ Zen 對團隊也很有效。您可以邀請隊友、分配角色、精選 我們建立 OpenCode Zen 的目的是: -1. **設定基準**編碼代理的最佳模型/供應商。 -2. 可以使用**最高品質**選項,而不是降低性能或轉向更便宜的供應商。 -3. 透過按成本價銷售來傳遞任何**價格下跌**;所以唯一的加價就是支付我們的處理費。 -4. 透過允許您將其與任何其他編碼代理一起使用,**無鎖定**。並且始終允許您將任何其他供應商與 opencode 一起使用。 +1. 為編碼代理**基準測試**最佳的模型和供應商組合。 +2. 提供**最高品質**的選項,不降低效能或路由到更廉價的供應商。 +3. 以成本價銷售來傳遞任何**降價優惠**;唯一的加價僅用於覆蓋我們的處理費用。 +4. **無鎖定**,允許你將其與任何其他編碼代理配合使用,同時也始終允許你在 OpenCode 中使用任何其他供應商。 From 6e984378d7601f2a74640bb61e27648e2c470758 Mon Sep 17 00:00:00 2001 From: Minung Han Date: Tue, 17 Feb 2026 21:07:09 +0900 Subject: [PATCH 048/255] fix(docs): correct reversed meaning in Korean plugins logging section (#13945) --- packages/web/src/content/docs/ko/plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/ko/plugins.mdx b/packages/web/src/content/docs/ko/plugins.mdx index 9a2995df8394..7214f326587e 100644 --- a/packages/web/src/content/docs/ko/plugins.mdx +++ b/packages/web/src/content/docs/ko/plugins.mdx @@ -312,7 +312,7 @@ export const CustomToolsPlugin: Plugin = async (ctx) => { ### 로깅 -구조화된 로깅을 위한 `client.app.log()` 대신에 `console.log`를 사용하십시오: +구조화된 로깅을 위해 `console.log` 대신 `client.app.log()`를 사용하십시오: ```ts title=".opencode/plugins/my-plugin.ts" export const MyPlugin = async ({ client }) => { From 4eed55973f002b4fecfcdfe10a01a798e80e83a3 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Tue, 17 Feb 2026 12:08:03 +0000 Subject: [PATCH 049/255] chore: generate --- packages/web/src/content/docs/ko/zen.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/ko/zen.mdx b/packages/web/src/content/docs/ko/zen.mdx index 254341b98277..04d5c0df8e94 100644 --- a/packages/web/src/content/docs/ko/zen.mdx +++ b/packages/web/src/content/docs/ko/zen.mdx @@ -140,7 +140,7 @@ https://opencode.ai/zen/v1/models | GPT 5 Codex | $1.07 | $8.50 | $0.107 | - | | GPT 5 Nano | Free | Free | Free | - | -사용 내역에서 _Claude Haiku 3.5_를 확인하실 수 있습니다. 이는 세션 제목을 생성하는 데 사용되는 [저비용 모델](/docs/config/#models)입니다. +사용 내역에서 *Claude Haiku 3.5*를 확인하실 수 있습니다. 이는 세션 제목을 생성하는 데 사용되는 [저비용 모델](/docs/config/#models)입니다. :::note 신용카드 수수료는 원가 기준(거래당 4.4% + $0.30)으로 그대로 반영되며, 당사는 그 외의 추가 수수료를 부과하지 않습니다. From 07947bab7d7f164ae5b46038deadda2284e97025 Mon Sep 17 00:00:00 2001 From: David Hill <1879069+iamdavidhill@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:43:55 +0000 Subject: [PATCH 050/255] tweak(tui): new session banner with logo and details (#13970) --- .../src/cli/cmd/tui/routes/session/index.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index e83b9abe98ae..55ab4d54dd4c 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -227,12 +227,20 @@ export function Session() { createEffect(() => { const title = Locale.truncate(session()?.title ?? "", 50) + const pad = (text: string) => text.padEnd(10, " ") + const weak = (text: string) => UI.Style.TEXT_DIM + pad(text) + UI.Style.TEXT_NORMAL + const logo = UI.logo(" ").split(/\r?\n/) return exit.message.set( [ ``, - ` █▀▀█ ${UI.Style.TEXT_DIM}${title}${UI.Style.TEXT_NORMAL}`, - ` █ █ ${UI.Style.TEXT_DIM}opencode -s ${session()?.id}${UI.Style.TEXT_NORMAL}`, - ` ▀▀▀▀ `, + `${logo[0] ?? ""}`, + `${logo[1] ?? ""}`, + `${logo[2] ?? ""}`, + `${logo[3] ?? ""}`, + ``, + ` ${weak("Session")}${UI.Style.TEXT_NORMAL_BOLD}${title}${UI.Style.TEXT_NORMAL}`, + ` ${weak("Continue")}${UI.Style.TEXT_NORMAL_BOLD}opencode -s ${session()?.id}${UI.Style.TEXT_NORMAL}`, + ``, ].join("\n"), ) }) From 3dfbb7059345350fdcb3f45fe9a44697c08a040a Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Tue, 17 Feb 2026 18:40:39 +0530 Subject: [PATCH 051/255] fix(app): recover state after sse reconnect and harden sse streams (#13973) --- packages/app/src/context/global-sdk.tsx | 48 ++++++++++++++++++- packages/app/src/context/global-sync.tsx | 5 ++ .../context/global-sync/event-reducer.test.ts | 14 ++++++ .../src/context/global-sync/event-reducer.ts | 2 +- packages/opencode/src/server/routes/global.ts | 6 ++- packages/opencode/src/server/server.ts | 6 ++- 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/packages/app/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx index 3f93b76a723c..c7f7708e62f7 100644 --- a/packages/app/src/context/global-sdk.tsx +++ b/packages/app/src/context/global-sdk.tsx @@ -2,9 +2,14 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { batch, onCleanup } from "solid-js" +import z from "zod" import { usePlatform } from "./platform" import { useServer } from "./server" +const abortError = z.object({ + name: z.literal("AbortError"), +}) + export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({ name: "GlobalSDK", init: () => { @@ -93,12 +98,35 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo let streamErrorLogged = false const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + const aborted = (error: unknown) => abortError.safeParse(error).success + + let attempt: AbortController | undefined + const HEARTBEAT_TIMEOUT_MS = 15_000 + let heartbeat: ReturnType | undefined + const resetHeartbeat = () => { + if (heartbeat) clearTimeout(heartbeat) + heartbeat = setTimeout(() => { + attempt?.abort() + }, HEARTBEAT_TIMEOUT_MS) + } + const clearHeartbeat = () => { + if (!heartbeat) return + clearTimeout(heartbeat) + heartbeat = undefined + } void (async () => { while (!abort.signal.aborted) { + attempt = new AbortController() + const onAbort = () => { + attempt?.abort() + } + abort.signal.addEventListener("abort", onAbort) try { const events = await eventSdk.global.event({ + signal: attempt.signal, onSseError: (error) => { + if (aborted(error)) return if (streamErrorLogged) return streamErrorLogged = true console.error("[global-sdk] event stream error", { @@ -109,7 +137,9 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo }, }) let yielded = Date.now() + resetHeartbeat() for await (const event of events.stream) { + resetHeartbeat() streamErrorLogged = false const directory = event.directory ?? "global" const payload = event.payload @@ -130,7 +160,7 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo await wait(0) } } catch (error) { - if (!streamErrorLogged) { + if (!aborted(error) && !streamErrorLogged) { streamErrorLogged = true console.error("[global-sdk] event stream failed", { url: server.url, @@ -138,6 +168,10 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo error, }) } + } finally { + abort.signal.removeEventListener("abort", onAbort) + attempt = undefined + clearHeartbeat() } if (abort.signal.aborted) return @@ -145,7 +179,19 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo } })().finally(flush) + const onVisibility = () => { + if (typeof document === "undefined") return + if (document.visibilityState !== "visible") return + attempt?.abort() + } + if (typeof document !== "undefined") { + document.addEventListener("visibilitychange", onVisibility) + } + onCleanup(() => { + if (typeof document !== "undefined") { + document.removeEventListener("visibilitychange", onVisibility) + } abort.abort() flush() }) diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 62c7eb66ec9c..ec5efc6754ee 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -270,6 +270,11 @@ function createGlobalSync() { setGlobalStore("project", next) }, }) + if (event.type === "server.connected" || event.type === "global.disposed") { + for (const directory of Object.keys(children.children)) { + queue.push(directory) + } + } return } 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 ad63f3c202eb..ab7f99cef3fa 100644 --- a/packages/app/src/context/global-sync/event-reducer.test.ts +++ b/packages/app/src/context/global-sync/event-reducer.test.ts @@ -116,6 +116,20 @@ describe("applyGlobalEvent", () => { expect(refreshCount).toBe(1) }) + + test("handles server.connected by triggering refresh", () => { + let refreshCount = 0 + applyGlobalEvent({ + event: { type: "server.connected" }, + project: [], + refresh: () => { + refreshCount += 1 + }, + setGlobalProject() {}, + }) + + expect(refreshCount).toBe(1) + }) }) describe("applyDirectoryEvent", () => { diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 66fcac66d560..48ac0fea13d2 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -20,7 +20,7 @@ export function applyGlobalEvent(input: { setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void refresh: () => void }) { - if (input.event.type === "global.disposed") { + if (input.event.type === "global.disposed" || input.event.type === "server.connected") { input.refresh() return } diff --git a/packages/opencode/src/server/routes/global.ts b/packages/opencode/src/server/routes/global.ts index 5e2df052ec4e..4d019f6a7eeb 100644 --- a/packages/opencode/src/server/routes/global.ts +++ b/packages/opencode/src/server/routes/global.ts @@ -66,6 +66,8 @@ export const GlobalRoutes = lazy(() => }), async (c) => { log.info("global event connected") + c.header("X-Accel-Buffering", "no") + c.header("X-Content-Type-Options", "nosniff") return streamSSE(c, async (stream) => { stream.writeSSE({ data: JSON.stringify({ @@ -82,7 +84,7 @@ export const GlobalRoutes = lazy(() => } GlobalBus.on("event", handler) - // Send heartbeat every 30s to prevent WKWebView timeout (60s default) + // Send heartbeat every 10s to prevent stalled proxy streams. const heartbeat = setInterval(() => { stream.writeSSE({ data: JSON.stringify({ @@ -92,7 +94,7 @@ export const GlobalRoutes = lazy(() => }, }), }) - }, 30000) + }, 10_000) await new Promise((resolve) => { stream.onAbort(() => { diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index c1896a8d1393..9fba9c1fe1a0 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -501,6 +501,8 @@ export namespace Server { }), async (c) => { log.info("event connected") + c.header("X-Accel-Buffering", "no") + c.header("X-Content-Type-Options", "nosniff") return streamSSE(c, async (stream) => { stream.writeSSE({ data: JSON.stringify({ @@ -517,7 +519,7 @@ export namespace Server { } }) - // Send heartbeat every 30s to prevent WKWebView timeout (60s default) + // Send heartbeat every 10s to prevent stalled proxy streams. const heartbeat = setInterval(() => { stream.writeSSE({ data: JSON.stringify({ @@ -525,7 +527,7 @@ export namespace Server { properties: {}, }), }) - }, 30000) + }, 10_000) await new Promise((resolve) => { stream.onAbort(() => { From 10985671ad9553e7ac594ede30981166f69ba3c5 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 17 Feb 2026 07:16:23 -0600 Subject: [PATCH 052/255] feat(app): session timeline/turn rework (#13196) Co-authored-by: David Hill --- packages/app/e2e/models/model-picker.spec.ts | 12 +- .../src/components/dialog-select-model.tsx | 2 +- packages/app/src/components/prompt-input.tsx | 438 ++++++---- .../components/prompt-input/context-items.tsx | 7 +- .../prompt-input/placeholder.test.ts | 19 +- .../components/prompt-input/placeholder.ts | 2 + .../components/prompt-input/slash-popover.tsx | 6 +- .../app/src/components/prompt-input/submit.ts | 4 + packages/app/src/components/question-dock.tsx | 440 ++++++---- .../src/components/session-context-usage.tsx | 10 +- .../app/src/components/session-todo-dock.tsx | 208 +++++ .../src/components/session/session-header.tsx | 83 +- .../components/session/session-new-view.tsx | 2 +- .../app/src/components/status-popover.tsx | 22 +- packages/app/src/components/titlebar.tsx | 80 +- packages/app/src/context/command.tsx | 2 +- packages/app/src/context/global-sync.tsx | 23 + .../app/src/context/global-sync/bootstrap.ts | 4 + .../src/context/global-sync/event-reducer.ts | 14 +- packages/app/src/context/sync.tsx | 17 +- packages/app/src/i18n/ar.ts | 4 + packages/app/src/i18n/br.ts | 4 + packages/app/src/i18n/bs.ts | 4 + packages/app/src/i18n/da.ts | 4 + packages/app/src/i18n/de.ts | 4 + packages/app/src/i18n/en.ts | 6 +- packages/app/src/i18n/es.ts | 4 + packages/app/src/i18n/fr.ts | 4 + packages/app/src/i18n/ja.ts | 4 + packages/app/src/i18n/ko.ts | 4 + packages/app/src/i18n/no.ts | 4 + packages/app/src/i18n/pl.ts | 4 + packages/app/src/i18n/ru.ts | 4 + packages/app/src/i18n/th.ts | 4 + packages/app/src/i18n/zh.ts | 4 + packages/app/src/i18n/zht.ts | 4 + packages/app/src/pages/directory-layout.tsx | 1 - packages/app/src/pages/layout.tsx | 8 +- .../app/src/pages/layout/sidebar-shell.tsx | 4 +- packages/app/src/pages/session.tsx | 81 +- .../src/pages/session/message-timeline.tsx | 54 +- .../src/pages/session/session-prompt-dock.tsx | 138 ++- .../pages/session/use-session-commands.tsx | 368 ++++---- .../enterprise/src/routes/share/[shareID].tsx | 11 - packages/ui/src/components/basic-tool.css | 83 +- packages/ui/src/components/basic-tool.tsx | 83 +- packages/ui/src/components/button.css | 12 + packages/ui/src/components/checkbox.css | 3 +- packages/ui/src/components/collapsible.css | 41 +- packages/ui/src/components/collapsible.tsx | 7 +- packages/ui/src/components/diff-changes.css | 7 +- packages/ui/src/components/diff-changes.tsx | 4 +- packages/ui/src/components/icon-button.css | 36 + packages/ui/src/components/icon-button.tsx | 1 + packages/ui/src/components/icon.tsx | 13 +- packages/ui/src/components/inline-input.css | 2 +- packages/ui/src/components/inline-input.tsx | 15 +- packages/ui/src/components/markdown.css | 52 +- packages/ui/src/components/markdown.tsx | 6 +- packages/ui/src/components/message-part.css | 661 +++++++++++---- packages/ui/src/components/message-part.tsx | 797 +++++++++++------- packages/ui/src/components/session-turn.css | 591 ++----------- packages/ui/src/components/session-turn.tsx | 769 ++++------------- packages/ui/src/components/text-shimmer.css | 43 + packages/ui/src/components/text-shimmer.tsx | 36 + packages/ui/src/context/data.tsx | 4 - packages/ui/src/i18n/ar.ts | 5 +- packages/ui/src/i18n/br.ts | 5 +- packages/ui/src/i18n/bs.ts | 5 +- packages/ui/src/i18n/da.ts | 5 +- packages/ui/src/i18n/de.ts | 5 +- packages/ui/src/i18n/en.ts | 7 +- packages/ui/src/i18n/es.ts | 5 +- packages/ui/src/i18n/fr.ts | 5 +- packages/ui/src/i18n/ja.ts | 5 +- packages/ui/src/i18n/ko.ts | 5 +- packages/ui/src/i18n/no.ts | 5 +- packages/ui/src/i18n/pl.ts | 5 +- packages/ui/src/i18n/ru.ts | 5 +- packages/ui/src/i18n/th.ts | 5 +- packages/ui/src/i18n/zh.ts | 5 +- packages/ui/src/i18n/zht.ts | 5 +- packages/ui/src/styles/animations.css | 11 + packages/ui/src/styles/index.css | 1 + packages/ui/src/styles/theme.css | 1 + 85 files changed, 3084 insertions(+), 2403 deletions(-) create mode 100644 packages/app/src/components/session-todo-dock.tsx create mode 100644 packages/ui/src/components/text-shimmer.css create mode 100644 packages/ui/src/components/text-shimmer.tsx diff --git a/packages/app/e2e/models/model-picker.spec.ts b/packages/app/e2e/models/model-picker.spec.ts index 01e72464cc56..220a0baa1a89 100644 --- a/packages/app/e2e/models/model-picker.spec.ts +++ b/packages/app/e2e/models/model-picker.spec.ts @@ -28,7 +28,6 @@ test("smoke model selection updates prompt footer", async ({ page, gotoSession } const key = await target.getAttribute("data-key") if (!key) throw new Error("Failed to resolve model key from list item") - const name = (await target.locator("span").first().innerText()).trim() const model = key.split(":").slice(1).join(":") await input.fill(model) @@ -37,6 +36,13 @@ test("smoke model selection updates prompt footer", async ({ page, gotoSession } await expect(dialog).toHaveCount(0) - const form = page.locator(promptSelector).locator("xpath=ancestor::form[1]") - await expect(form.locator('[data-component="button"]').filter({ hasText: name }).first()).toBeVisible() + await page.locator(promptSelector).click() + await page.keyboard.type("/model") + await expect(command).toBeVisible() + await command.hover() + await page.keyboard.press("Enter") + + const dialogAgain = page.getByRole("dialog") + await expect(dialogAgain).toBeVisible() + await expect(dialogAgain.locator(`[data-slot="list-item"][data-key="${key}"][data-selected="true"]`)).toBeVisible() }) diff --git a/packages/app/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx index a196db231a67..9f7afb8cd27d 100644 --- a/packages/app/src/components/dialog-select-model.tsx +++ b/packages/app/src/components/dialog-select-model.tsx @@ -121,7 +121,7 @@ export function ModelSelectorPopover(props: { }} modal={false} placement="top-start" - gutter={8} + gutter={4} > {props.children} diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index e21798738175..7813e01cd6ae 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -32,7 +32,6 @@ import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid import { useProviders } from "@/hooks/use-providers" import { useCommand } from "@/context/command" import { Persist, persisted } from "@/utils/persist" -import { SessionContextUsage } from "@/components/session-context-usage" import { usePermission } from "@/context/permission" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" @@ -94,7 +93,6 @@ export const PromptInput: Component = (props) => { const local = useLocal() const files = useFile() const prompt = usePrompt() - const commentCount = createMemo(() => prompt.context.items().filter((item) => !!item.comment?.trim()).length) const layout = useLayout() const comments = useComments() const params = useParams() @@ -105,7 +103,7 @@ export const PromptInput: Component = (props) => { const language = useLanguage() const platform = usePlatform() let editorRef!: HTMLDivElement - let fileInputRef!: HTMLInputElement + let fileInputRef: HTMLInputElement | undefined let scrollRef!: HTMLDivElement let slashPopoverRef!: HTMLDivElement @@ -223,14 +221,25 @@ export const PromptInput: Component = (props) => { mode: "normal", applyingHistory: false, }) - const placeholder = createMemo(() => - promptPlaceholder({ - mode: store.mode, - commentCount: commentCount(), - example: language.t(EXAMPLES[store.placeholder]), - t: (key, params) => language.t(key as Parameters[0], params as never), - }), - ) + + const commentCount = createMemo(() => { + if (store.mode === "shell") return 0 + return prompt.context.items().filter((item) => !!item.comment?.trim()).length + }) + + const contextItems = createMemo(() => { + const items = prompt.context.items() + if (store.mode !== "shell") return items + return items.filter((item) => !item.comment?.trim()) + }) + + const hasUserPrompt = createMemo(() => { + const sessionID = params.id + if (!sessionID) return false + const messages = sync.data.message[sessionID] + if (!messages) return false + return messages.some((m) => m.role === "user") + }) const MAX_HISTORY = 100 const [history, setHistory] = persisted( @@ -250,6 +259,18 @@ export const PromptInput: Component = (props) => { }), ) + const suggest = createMemo(() => !hasUserPrompt()) + + const placeholder = createMemo(() => + promptPlaceholder({ + mode: store.mode, + commentCount: commentCount(), + example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "", + suggest: suggest(), + t: (key, params) => language.t(key as Parameters[0], params as never), + }), + ) + const applyHistoryPrompt = (p: Prompt, position: "start" | "end") => { const length = position === "start" ? 0 : promptLength(p) setStore("applyingHistory", true) @@ -282,6 +303,25 @@ export const PromptInput: Component = (props) => { const isFocused = createFocusSignal(() => editorRef) const escBlur = () => platform.platform === "desktop" && platform.os === "macos" + const pick = () => fileInputRef?.click() + + const setMode = (mode: "normal" | "shell") => { + setStore("mode", mode) + setStore("popover", null) + requestAnimationFrame(() => editorRef?.focus()) + } + + command.register("prompt-input", () => [ + { + id: "file.attach", + title: language.t("prompt.action.attachFile"), + category: language.t("command.category.file"), + keybind: "mod+u", + disabled: store.mode !== "normal", + onSelect: pick, + }, + ]) + const closePopover = () => setStore("popover", null) const resetHistoryNavigation = (force = false) => { @@ -326,6 +366,7 @@ export const PromptInput: Component = (props) => { createEffect(() => { params.id if (params.id) return + if (!suggest()) return const interval = setInterval(() => { setStore("placeholder", (prev) => (prev + 1) % EXAMPLES.length) }, 6500) @@ -816,6 +857,13 @@ export const PromptInput: Component = (props) => { }) const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === "u") { + event.preventDefault() + if (store.mode !== "normal") return + pick() + return + } + if (event.key === "Backspace") { const selection = window.getSelection() if (selection && selection.isCollapsed) { @@ -956,8 +1004,10 @@ export const PromptInput: Component = (props) => { } } + const variants = createMemo(() => ["default", ...local.model.variant.list()]) + return ( -

+
(slashPopoverRef = el)} @@ -977,8 +1027,8 @@ export const PromptInput: Component = (props) => { onSubmit={handleSubmit} classList={{ "group/prompt-input": true, - "bg-surface-raised-stronger-non-alpha shadow-xs-border relative": true, - "rounded-[14px] overflow-clip focus-within:shadow-xs-border": true, + "bg-surface-raised-stronger-non-alpha shadow-xs-border relative z-10": true, + "rounded-[12px] overflow-clip focus-within:shadow-xs-border": true, "border-icon-info-active border-dashed": store.draggingType !== null, [props.class ?? ""]: !!props.class, }} @@ -988,7 +1038,7 @@ export const PromptInput: Component = (props) => { label={language.t(store.draggingType === "@mention" ? "prompt.dropzone.file.label" : "prompt.dropzone.label")} /> { const active = comments.active() return !!item.commentID && item.commentID === active?.id && item.path === active?.file @@ -1008,7 +1058,22 @@ export const PromptInput: Component = (props) => { onRemove={removeImageAttachment} removeLabel={language.t("prompt.attachment.remove")} /> -
(scrollRef = el)}> +
(scrollRef = el)} + onMouseDown={(e) => { + const target = e.target + if (!(target instanceof HTMLElement)) return + if ( + target.closest( + '[data-action="prompt-attach"], [data-action="prompt-submit"], [data-action="prompt-permissions"]', + ) + ) { + return + } + editorRef?.focus() + }} + >
{ @@ -1029,41 +1094,158 @@ export const PromptInput: Component = (props) => { onKeyDown={handleKeyDown} classList={{ "select-text": true, - "w-full p-3 pr-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true, + "w-full pl-3 pr-2 pt-2 pb-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true, "[&_[data-type=file]]:text-syntax-property": true, "[&_[data-type=agent]]:text-syntax-type": true, "font-mono!": store.mode === "shell", }} /> -
+
{placeholder()}
+ +
+ { + const file = e.currentTarget.files?.[0] + if (file) addImageAttachment(file) + e.currentTarget.value = "" + }} + /> + +
+ + + + + + +
+ {language.t("prompt.action.stop")} + {language.t("common.key.esc")} +
+
+ +
+ {language.t("prompt.action.send")} + +
+
+ + } + > + +
+
+
+ + +
+
+ + + +
+
+
-
-
- - -
- - {language.t("prompt.mode.shell")} - {language.t("prompt.mode.shell.exit")} + + +
+
+
+ +
+ {language.t("prompt.mode.shell")} +
- - + + + { - const file = e.currentTarget.files?.[0] - if (file) addImageAttachment(file) - e.currentTarget.value = "" - }} - /> -
- - - - + } + > +
{ + if (store.sending) { + e.preventDefault() + return + } + if (e.target instanceof HTMLTextAreaElement) return + const input = e.currentTarget.querySelector('[data-slot="question-custom-input"]') + if (input instanceof HTMLTextAreaElement) input.focus() + }} + onSubmit={(e) => { + e.preventDefault() + commitCustom() + }} + > + + + {language.t("ui.messagePart.option.typeOwnAnswer")} +
` diff --git a/packages/app/src/pages/session/helpers.ts b/packages/app/src/pages/session/helpers.ts index 5ca355d1d291..995f6eb191db 100644 --- a/packages/app/src/pages/session/helpers.ts +++ b/packages/app/src/pages/session/helpers.ts @@ -35,6 +35,27 @@ export const createOpenReviewFile = (input: { } } +export const createOpenSessionFileTab = (input: { + normalizeTab: (tab: string) => string + openTab: (tab: string) => void + pathFromTab: (tab: string) => string | undefined + loadFile: (path: string) => void + openReviewPanel: () => void + setActive: (tab: string) => void +}) => { + return (value: string) => { + const next = input.normalizeTab(value) + input.openTab(next) + + const path = input.pathFromTab(next) + if (!path) return + + input.loadFile(path) + input.openReviewPanel() + input.setActive(next) + } +} + export const getTabReorderIndex = (tabs: readonly string[], from: string, to: string) => { const fromIndex = tabs.indexOf(from) const toIndex = tabs.indexOf(to) diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 68dfc346f918..7f2d4fc76736 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -23,7 +23,7 @@ import { useLayout } from "@/context/layout" import { useSync } from "@/context/sync" import { createFileTabListSync } from "@/pages/session/file-tab-scroll" import { FileTabContent } from "@/pages/session/file-tabs" -import { getTabReorderIndex } from "@/pages/session/helpers" +import { createOpenSessionFileTab, getTabReorderIndex } from "@/pages/session/helpers" import { StickyAddButton } from "@/pages/session/review-tab" import { setSessionHandoff } from "@/pages/session/handoff" @@ -96,15 +96,14 @@ export function SessionSidePanel(props: { if (!view().reviewPanel.opened()) view().reviewPanel.open() } - const openTab = (value: string) => { - const next = normalizeTab(value) - tabs().open(next) - - const path = file.pathFromTab(next) - if (!path) return - file.load(path) - openReviewPanel() - } + const openTab = createOpenSessionFileTab({ + normalizeTab, + openTab: tabs().open, + pathFromTab: file.pathFromTab, + loadFile: file.load, + openReviewPanel, + setActive: tabs().setActive, + }) const contextOpen = createMemo(() => tabs().active() === "context" || tabs().all().includes("context")) const openedTabs = createMemo(() => From de25703e9dd33df4dff6b5b8ae9a722f6ca2aa81 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 18 Feb 2026 13:56:05 -0600 Subject: [PATCH 160/255] fix(app): terminal cross-talk (#14184) --- packages/opencode/src/pty/index.ts | 43 ++++++++++------- packages/opencode/src/server/routes/pty.ts | 16 +++++-- .../test/pty/pty-output-isolation.test.ts | 46 +++++++++++++++++++ 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index a9052a79eb8c..c98e99daf586 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -18,18 +18,24 @@ export namespace Pty { type Socket = { readyState: number + data: object send: (data: string | Uint8Array | ArrayBuffer) => void close: (code?: number, reason?: string) => void } - const sockets = new WeakMap() - let socketCounter = 0 + // Bun's ServerWebSocket has a per-connection `.data` object (set during + // `server.upgrade`) that changes when the underlying connection is recycled. + // We keep a reference to a stable part of it so output can't leak even when + // websocket objects are reused. + const token = (ws: Socket) => { + const data = ws.data + const events = (data as { events?: unknown }).events + if (events && typeof events === "object") return events - const tagSocket = (ws: Socket) => { - if (!ws || typeof ws !== "object") return - const next = (socketCounter = (socketCounter + 1) % Number.MAX_SAFE_INTEGER) - sockets.set(ws, next) - return next + const url = (data as { url?: unknown }).url + if (url && typeof url === "object") return url + + return data } // WebSocket control frame: 0x00 + UTF-8 JSON (currently { cursor }). @@ -96,7 +102,7 @@ export namespace Pty { buffer: string bufferCursor: number cursor: number - subscribers: Map + subscribers: Map } const state = Instance.state( @@ -176,26 +182,27 @@ export namespace Pty { subscribers: new Map(), } state().set(id, session) - ptyProcess.onData((data) => { - session.cursor += data.length + ptyProcess.onData((chunk) => { + session.cursor += chunk.length - for (const [ws, id] of session.subscribers) { + for (const [ws, data] of session.subscribers) { if (ws.readyState !== 1) { session.subscribers.delete(ws) continue } - if (typeof ws === "object" && sockets.get(ws) !== id) { + + if (token(ws) !== data) { session.subscribers.delete(ws) continue } try { - ws.send(data) + ws.send(chunk) } catch { session.subscribers.delete(ws) } } - session.buffer += data + session.buffer += chunk if (session.buffer.length <= BUFFER_LIMIT) return const excess = session.buffer.length - BUFFER_LIMIT session.buffer = session.buffer.slice(excess) @@ -305,8 +312,12 @@ export namespace Pty { return } - const socketId = tagSocket(ws) - if (typeof socketId === "number") session.subscribers.set(ws, socketId) + if (!ws.data || typeof ws.data !== "object") { + ws.close() + return + } + + session.subscribers.set(ws, token(ws)) return { onMessage: (message: string | ArrayBuffer) => { session.process.write(String(message)) diff --git a/packages/opencode/src/server/routes/pty.ts b/packages/opencode/src/server/routes/pty.ts index 21156190dce4..d516859f7f76 100644 --- a/packages/opencode/src/server/routes/pty.ts +++ b/packages/opencode/src/server/routes/pty.ts @@ -163,6 +163,7 @@ export const PtyRoutes = lazy(() => type Socket = { readyState: number + data: object send: (data: string | Uint8Array | ArrayBuffer) => void close: (code?: number, reason?: string) => void } @@ -170,6 +171,10 @@ export const PtyRoutes = lazy(() => const isSocket = (value: unknown): value is Socket => { if (!value || typeof value !== "object") return false if (!("readyState" in value)) return false + if (!("data" in value)) return false + if (!((value as { data?: unknown }).data && typeof (value as { data?: unknown }).data === "object")) { + return false + } if (!("send" in value) || typeof (value as { send?: unknown }).send !== "function") return false if (!("close" in value) || typeof (value as { close?: unknown }).close !== "function") return false return typeof (value as { readyState?: unknown }).readyState === "number" @@ -177,11 +182,16 @@ export const PtyRoutes = lazy(() => return { onOpen(_event, ws) { - const socket = isSocket(ws.raw) ? ws.raw : ws - handler = Pty.connect(id, socket, cursor) + const raw = ws.raw + if (!isSocket(raw)) { + ws.close() + return + } + handler = Pty.connect(id, raw, cursor) }, onMessage(event) { - handler?.onMessage(String(event.data)) + if (typeof event.data !== "string") return + handler?.onMessage(event.data) }, onClose() { handler?.onClose() diff --git a/packages/opencode/test/pty/pty-output-isolation.test.ts b/packages/opencode/test/pty/pty-output-isolation.test.ts index b80d373458f4..337280d18de8 100644 --- a/packages/opencode/test/pty/pty-output-isolation.test.ts +++ b/packages/opencode/test/pty/pty-output-isolation.test.ts @@ -18,6 +18,7 @@ describe("pty", () => { const ws = { readyState: 1, + data: { events: { connection: "a" } }, send: (data: unknown) => { outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) }, @@ -30,6 +31,7 @@ describe("pty", () => { Pty.connect(a.id, ws as any) // Now "reuse" the same ws object for another connection. + ws.data = { events: { connection: "b" } } ws.send = (data: unknown) => { outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) } @@ -51,4 +53,48 @@ describe("pty", () => { }, }) }) + + test("does not leak output when Bun recycles websocket objects before re-connect", async () => { + await using dir = await tmpdir({ git: true }) + + await Instance.provide({ + directory: dir.path, + fn: async () => { + const a = await Pty.create({ command: "cat", title: "a" }) + try { + const outA: string[] = [] + const outB: string[] = [] + + const ws = { + readyState: 1, + data: { events: { connection: "a" } }, + send: (data: unknown) => { + outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) + }, + close: () => { + // no-op (simulate abrupt drop) + }, + } + + // Connect "a" first. + Pty.connect(a.id, ws as any) + outA.length = 0 + + // Simulate Bun reusing the same websocket object for another connection + // before the new onOpen handler has a chance to tag it. + ws.data = { events: { connection: "b" } } + ws.send = (data: unknown) => { + outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) + } + + Pty.write(a.id, "AAA\n") + await Bun.sleep(100) + + expect(outB.join("")).not.toContain("AAA") + } finally { + await Pty.remove(a.id) + } + }, + }) + }) }) From 1aa18c6cd64412db89ccfb58c2641ab3e49233e4 Mon Sep 17 00:00:00 2001 From: Helge Tesdal Date: Wed, 18 Feb 2026 21:14:18 +0100 Subject: [PATCH 161/255] feat(plugin): pass sessionID and callID to shell.env hook input (#13662) --- packages/opencode/src/session/prompt.ts | 2 +- packages/opencode/src/tool/bash.ts | 2 +- packages/plugin/src/index.ts | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 43ad9a09d399..c99eb72a732e 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1618,7 +1618,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the const args = matchingInvocation?.args const cwd = Instance.directory - const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} }) + const shellEnv = await Plugin.trigger("shell.env", { cwd, sessionID: input.sessionID, callID: part.callID }, { env: {} }) const proc = spawn(shell, args, { cwd, detached: process.platform !== "win32", diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 67559b78c085..2b692db6ee91 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -163,7 +163,7 @@ export const BashTool = Tool.define("bash", async () => { }) } - const shellEnv = await Plugin.trigger("shell.env", { cwd }, { env: {} }) + const shellEnv = await Plugin.trigger("shell.env", { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, { env: {} }) const proc = spawn(params.command, { shell, cwd, diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index bd4ba530498d..76370d1d5a7f 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -185,7 +185,10 @@ export interface Hooks { input: { tool: string; sessionID: string; callID: string }, output: { args: any }, ) => Promise - "shell.env"?: (input: { cwd: string }, output: { env: Record }) => Promise + "shell.env"?: ( + input: { cwd: string; sessionID?: string; callID?: string }, + output: { env: Record }, + ) => Promise "tool.execute.after"?: ( input: { tool: string; sessionID: string; callID: string; args: any }, output: { From 2d7c9c9692f9232d2977487f13ecddc758a4a250 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Wed, 18 Feb 2026 20:15:14 +0000 Subject: [PATCH 162/255] chore: generate --- packages/opencode/src/session/prompt.ts | 6 +++++- packages/opencode/src/tool/bash.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index c99eb72a732e..d1f4072586e4 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1618,7 +1618,11 @@ NOTE: At any point in time through this workflow you should feel free to ask the const args = matchingInvocation?.args const cwd = Instance.directory - const shellEnv = await Plugin.trigger("shell.env", { cwd, sessionID: input.sessionID, callID: part.callID }, { env: {} }) + const shellEnv = await Plugin.trigger( + "shell.env", + { cwd, sessionID: input.sessionID, callID: part.callID }, + { env: {} }, + ) const proc = spawn(shell, args, { cwd, detached: process.platform !== "win32", diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 2b692db6ee91..ee2279bbfbeb 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -163,7 +163,11 @@ export const BashTool = Tool.define("bash", async () => { }) } - const shellEnv = await Plugin.trigger("shell.env", { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, { env: {} }) + const shellEnv = await Plugin.trigger( + "shell.env", + { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, + { env: {} }, + ) const proc = spawn(params.command, { shell, cwd, From d6331cf792e02c75a96f5f8a39adaedd9a2d1298 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 16:36:15 +0000 Subject: [PATCH 163/255] Update colors.css --- packages/ui/src/styles/colors.css | 144 ++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 48 deletions(-) diff --git a/packages/ui/src/styles/colors.css b/packages/ui/src/styles/colors.css index 61e3ac950ad9..5ef17d1cc518 100644 --- a/packages/ui/src/styles/colors.css +++ b/packages/ui/src/styles/colors.css @@ -1,52 +1,100 @@ :root { - --smoke-dark-1: #131010; - --smoke-dark-2: #1b1818; - --smoke-dark-3: #252121; - --smoke-dark-4: #2d2828; - --smoke-dark-5: #343030; - --smoke-dark-6: #3e3939; - --smoke-dark-7: #4b4646; - --smoke-dark-8: #645f5f; - --smoke-dark-9: #716c6b; - --smoke-dark-10: #7f7979; - --smoke-dark-11: #b7b1b1; - --smoke-dark-12: #f1ecec; - --smoke-light-1: #fdfcfc; - --smoke-light-2: #f9f8f8; - --smoke-light-3: #f1f0f0; - --smoke-light-4: #e9e8e8; - --smoke-light-5: #e2e0e0; - --smoke-light-6: #dad9d9; - --smoke-light-7: #cfcecd; - --smoke-light-8: #bcbbbb; - --smoke-light-9: #8e8b8b; - --smoke-light-10: #848181; - --smoke-light-11: #656363; - --smoke-light-12: #211e1e; - --smoke-dark-alpha-1: #82383803; - --smoke-dark-alpha-2: #e6c6c60b; - --smoke-dark-alpha-3: #edd5d516; - --smoke-dark-alpha-4: #f2e1e11e; - --smoke-dark-alpha-5: #f5e8e826; - --smoke-dark-alpha-6: #f5e8e831; - --smoke-dark-alpha-7: #f7ecec3f; - --smoke-dark-alpha-8: #faf5f559; - --smoke-dark-alpha-9: #faf5f467; - --smoke-dark-alpha-10: #fbf5f576; - --smoke-dark-alpha-11: #fcf9f9b2; - --smoke-dark-alpha-12: #fdfbfbf0; - --smoke-light-alpha-1: #55000003; - --smoke-light-alpha-2: #25000007; - --smoke-light-alpha-3: #1100000f; - --smoke-light-alpha-4: #0c000017; - --smoke-light-alpha-5: #1100001f; - --smoke-light-alpha-6: #07000026; - --smoke-light-alpha-7: #0b060032; - --smoke-light-alpha-8: #04000044; - --smoke-light-alpha-9: #07000074; - --smoke-light-alpha-10: #0400009c; - --smoke-light-alpha-11: #0700007e; - --smoke-light-alpha-12: #020000df; + --gray-dark-1: #161616; + --gray-dark-2: #1C1C1C; + --gray-dark-3: #232323; + --gray-dark-4: #282828; + --gray-dark-5: #2E2E2E; + --gray-dark-6: #343434; + --gray-dark-7: #3E3E3E; + --gray-dark-8: #505050; + --gray-dark-9: #707070; + --gray-dark-10: #7E7E7E; + --gray-dark-11: #A0A0A0; + --gray-dark-12: #EDEDED; + --gray-light-1: #FCFCFC; + --gray-light-2: #F8F8F8; + --gray-light-3: #F3F3F3; + --gray-light-4: #EDEDED; + --gray-light-5: #E8E8E8; + --gray-light-6: #E2E2E2; + --gray-light-7: #DBDBDB; + --gray-light-8: #C7C7C7; + --gray-light-9: #8F8F8F; + --gray-light-10: #858585; + --gray-light-11: #6F6F6F; + --gray-light-12: #171717; + --gray-dark-alpha-1: #00000000; + --gray-dark-alpha-2: #FFFFFF08; + --gray-dark-alpha-3: #FFFFFF0F; + --gray-dark-alpha-4: #FFFFFF14; + --gray-dark-alpha-5: #FFFFFF1A; + --gray-dark-alpha-6: #FFFFFF21; + --gray-dark-alpha-7: #FFFFFF2B; + --gray-dark-alpha-8: #FFFFFF40; + --gray-dark-alpha-9: #FFFFFF63; + --gray-dark-alpha-10: #FFFFFF73; + --gray-dark-alpha-11: #FFFFFF96; + --gray-dark-alpha-12: #FFFFFFEB; + --gray-light-alpha-1: #00000003; + --gray-light-alpha-2: #00000008; + --gray-light-alpha-3: #0000000D; + --gray-light-alpha-4: #00000012; + --gray-light-alpha-5: #00000017; + --gray-light-alpha-6: #0000001C; + --gray-light-alpha-7: #00000024; + --gray-light-alpha-8: #00000038; + --gray-light-alpha-9: #00000070; + --gray-light-alpha-10: #0000007A; + --gray-light-alpha-11: #0000008F; + --gray-light-alpha-12: #000000E8; + --gray-dark-1: #131010; + --gray-dark-2: #1b1818; + --gray-dark-3: #252121; + --gray-dark-4: #2d2828; + --gray-dark-5: #343030; + --gray-dark-6: #3e3939; + --gray-dark-7: #4b4646; + --gray-dark-8: #645f5f; + --gray-dark-9: #716c6b; + --gray-dark-10: #7f7979; + --gray-dark-11: #b7b1b1; + --gray-dark-12: #f1ecec; + --gray-light-1: #fdfcfc; + --gray-light-2: #f9f8f8; + --gray-light-3: #f1f0f0; + --gray-light-4: #e9e8e8; + --gray-light-5: #e2e0e0; + --gray-light-6: #dad9d9; + --gray-light-7: #cfcecd; + --gray-light-8: #bcbbbb; + --gray-light-9: #8e8b8b; + --gray-light-10: #848181; + --gray-light-11: #656363; + --gray-light-12: #211e1e; + --gray-dark-alpha-1: #82383803; + --gray-dark-alpha-2: #e6c6c60b; + --gray-dark-alpha-3: #edd5d516; + --gray-dark-alpha-4: #f2e1e11e; + --gray-dark-alpha-5: #f5e8e826; + --gray-dark-alpha-6: #f5e8e831; + --gray-dark-alpha-7: #f7ecec3f; + --gray-dark-alpha-8: #faf5f559; + --gray-dark-alpha-9: #faf5f467; + --gray-dark-alpha-10: #fbf5f576; + --gray-dark-alpha-11: #fcf9f9b2; + --gray-dark-alpha-12: #fdfbfbf0; + --gray-light-alpha-1: #55000003; + --gray-light-alpha-2: #25000007; + --gray-light-alpha-3: #1100000f; + --gray-light-alpha-4: #0c000017; + --gray-light-alpha-5: #1100001f; + --gray-light-alpha-6: #07000026; + --gray-light-alpha-7: #0b060032; + --gray-light-alpha-8: #04000044; + --gray-light-alpha-9: #07000074; + --gray-light-alpha-10: #0400009c; + --gray-light-alpha-11: #0700007e; + --gray-light-alpha-12: #020000df; --yuzu-dark-1: #11120c; --yuzu-light-1: #fdfdfb; --yuzu-light-2: #fbfceb; From 12016c8eb451a119c1017b2fc5554b49232399e3 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 16:36:30 +0000 Subject: [PATCH 164/255] oc-2 theme init --- packages/ui/src/theme/default-themes.ts | 3 + packages/ui/src/theme/index.ts | 1 + packages/ui/src/theme/themes/oc-2.json | 532 ++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 packages/ui/src/theme/themes/oc-2.json diff --git a/packages/ui/src/theme/default-themes.ts b/packages/ui/src/theme/default-themes.ts index 4d44c3b01824..52b2b42eba8e 100644 --- a/packages/ui/src/theme/default-themes.ts +++ b/packages/ui/src/theme/default-themes.ts @@ -1,5 +1,6 @@ import type { DesktopTheme } from "./types" import oc1ThemeJson from "./themes/oc-1.json" +import oc2ThemeJson from "./themes/oc-2.json" import tokyoThemeJson from "./themes/tokyonight.json" import draculaThemeJson from "./themes/dracula.json" import monokaiThemeJson from "./themes/monokai.json" @@ -16,6 +17,7 @@ import gruvboxThemeJson from "./themes/gruvbox.json" import auraThemeJson from "./themes/aura.json" export const oc1Theme = oc1ThemeJson as DesktopTheme +export const oc2Theme = oc2ThemeJson as DesktopTheme export const tokyonightTheme = tokyoThemeJson as DesktopTheme export const draculaTheme = draculaThemeJson as DesktopTheme export const monokaiTheme = monokaiThemeJson as DesktopTheme @@ -33,6 +35,7 @@ export const auraTheme = auraThemeJson as DesktopTheme export const DEFAULT_THEMES: Record = { "oc-1": oc1Theme, + "oc-2": oc2Theme, aura: auraTheme, ayu: ayuTheme, carbonfox: carbonfoxTheme, diff --git a/packages/ui/src/theme/index.ts b/packages/ui/src/theme/index.ts index e8d2fe79f83d..d2c60179ec0e 100644 --- a/packages/ui/src/theme/index.ts +++ b/packages/ui/src/theme/index.ts @@ -32,6 +32,7 @@ export { ThemeProvider, useTheme, type ColorScheme } from "./context" export { DEFAULT_THEMES, oc1Theme, + oc2Theme, tokyonightTheme, draculaTheme, monokaiTheme, diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json new file mode 100644 index 000000000000..d47322aef4d0 --- /dev/null +++ b/packages/ui/src/theme/themes/oc-2.json @@ -0,0 +1,532 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "OC-2", + "id": "oc-2", + "light": { + "seeds": { + "neutral": "#8e8b8b", + "primary": "#dcde8d", + "success": "#12c905", + "warning": "#ffdc17", + "error": "#fc533a", + "info": "#a753ae", + "interactive": "#034cff", + "diffAdd": "#9ff29a", + "diffDelete": "#fc533a" + }, + "overrides": { + "background-base": "#f8f7f7", + "background-weak": "var(--gray-light-3)", + "background-strong": "var(--gray-light-1)", + "background-stronger": "#fcfcfc", + "surface-base": "var(--gray-light-alpha-2)", + "base": "var(--gray-light-alpha-2)", + "surface-base-hover": "#0500000f", + "surface-base-active": "var(--gray-light-alpha-3)", + "surface-base-interactive-active": "var(--cobalt-light-alpha-3)", + "base2": "var(--gray-light-alpha-2)", + "base3": "var(--gray-light-alpha-2)", + "surface-inset-base": "var(--gray-light-alpha-2)", + "surface-inset-base-hover": "var(--gray-light-alpha-3)", + "surface-inset-strong": "#1f000017", + "surface-inset-strong-hover": "#1f000017", + "surface-raised-base": "var(--gray-light-alpha-2)", + "surface-float-base": "var(--gray-dark-1)", + "surface-float-base-hover": "var(--gray-dark-2)", + "surface-raised-base-hover": "var(--gray-light-alpha-3)", + "surface-raised-base-active": "var(--gray-light-alpha-4)", + "surface-raised-strong": "var(--gray-light-1)", + "surface-raised-strong-hover": "var(--white)", + "surface-raised-stronger": "var(--white)", + "surface-raised-stronger-hover": "var(--white)", + "surface-weak": "var(--gray-light-alpha-3)", + "surface-weaker": "var(--gray-light-alpha-4)", + "surface-strong": "#ffffff", + "surface-raised-stronger-non-alpha": "var(--white)", + "surface-brand-base": "var(--yuzu-light-9)", + "surface-brand-hover": "var(--yuzu-light-10)", + "surface-interactive-base": "var(--cobalt-light-3)", + "surface-interactive-hover": "#E5F0FF", + "surface-interactive-weak": "var(--cobalt-light-2)", + "surface-interactive-weak-hover": "var(--cobalt-light-3)", + "surface-success-base": "var(--apple-light-3)", + "surface-success-weak": "var(--apple-light-2)", + "surface-success-strong": "var(--apple-light-9)", + "surface-warning-base": "var(--solaris-light-3)", + "surface-warning-weak": "var(--solaris-light-2)", + "surface-warning-strong": "var(--solaris-light-9)", + "surface-critical-base": "var(--ember-light-3)", + "surface-critical-weak": "var(--ember-light-2)", + "surface-critical-strong": "var(--ember-light-9)", + "surface-info-base": "var(--lilac-light-3)", + "surface-info-weak": "var(--lilac-light-2)", + "surface-info-strong": "var(--lilac-light-9)", + "surface-diff-unchanged-base": "#ffffff00", + "surface-diff-skip-base": "var(--gray-light-2)", + "surface-diff-hidden-base": "var(--blue-light-3)", + "surface-diff-hidden-weak": "var(--blue-light-2)", + "surface-diff-hidden-weaker": "var(--blue-light-1)", + "surface-diff-hidden-strong": "var(--blue-light-5)", + "surface-diff-hidden-stronger": "var(--blue-light-9)", + "surface-diff-add-base": "#dafbe0", + "surface-diff-add-weak": "var(--mint-light-2)", + "surface-diff-add-weaker": "var(--mint-light-1)", + "surface-diff-add-strong": "var(--mint-light-5)", + "surface-diff-add-stronger": "var(--mint-light-9)", + "surface-diff-delete-base": "var(--ember-light-3)", + "surface-diff-delete-weak": "var(--ember-light-2)", + "surface-diff-delete-weaker": "var(--ember-light-1)", + "surface-diff-delete-strong": "var(--ember-light-6)", + "surface-diff-delete-stronger": "var(--ember-light-9)", + "input-base": "var(--gray-light-1)", + "input-hover": "var(--gray-light-2)", + "input-active": "var(--cobalt-light-1)", + "input-selected": "var(--cobalt-light-4)", + "input-focus": "var(--cobalt-light-1)", + "input-disabled": "var(--gray-light-4)", + "text-base": "var(--gray-light-11)", + "text-weak": "var(--gray-light-9)", + "text-weaker": "var(--gray-light-8)", + "text-strong": "var(--gray-light-12)", + "text-invert-base": "var(--gray-dark-alpha-11)", + "text-invert-weak": "var(--gray-dark-alpha-9)", + "text-invert-weaker": "var(--gray-dark-alpha-8)", + "text-invert-strong": "var(--gray-dark-alpha-12)", + "text-interactive-base": "var(--cobalt-light-9)", + "text-on-brand-base": "var(--gray-light-alpha-11)", + "text-on-interactive-base": "var(--gray-light-1)", + "text-on-interactive-weak": "var(--gray-dark-alpha-11)", + "text-on-success-base": "var(--apple-light-10)", + "text-on-critical-base": "var(--ember-light-10)", + "text-on-critical-weak": "var(--ember-light-8)", + "text-on-critical-strong": "var(--ember-light-12)", + "text-on-warning-base": "var(--gray-dark-alpha-11)", + "text-on-info-base": "var(--gray-dark-alpha-11)", + "text-diff-add-base": "var(--mint-light-11)", + "text-diff-delete-base": "var(--ember-light-10)", + "text-diff-delete-strong": "var(--ember-light-12)", + "text-diff-add-strong": "var(--mint-light-12)", + "text-on-info-weak": "var(--gray-dark-alpha-9)", + "text-on-info-strong": "var(--gray-dark-alpha-12)", + "text-on-warning-weak": "var(--gray-dark-alpha-9)", + "text-on-warning-strong": "var(--gray-dark-alpha-12)", + "text-on-success-weak": "var(--apple-light-6)", + "text-on-success-strong": "var(--apple-light-12)", + "text-on-brand-weak": "var(--gray-light-alpha-9)", + "text-on-brand-weaker": "var(--gray-light-alpha-8)", + "text-on-brand-strong": "var(--gray-light-alpha-12)", + "button-primary-base": "var(--gray-light-12)", + "button-secondary-base": "#fdfcfc", + "button-secondary-hover": "#faf9f9", + "border-base": "var(--gray-light-alpha-7)", + "border-hover": "var(--gray-light-alpha-8)", + "border-active": "var(--gray-light-alpha-9)", + "border-selected": "var(--cobalt-light-alpha-9)", + "border-disabled": "var(--gray-light-alpha-8)", + "border-focus": "var(--gray-light-alpha-9)", + "border-weak-base": "var(--gray-light-alpha-5)", + "border-strong-base": "var(--gray-light-alpha-7)", + "border-strong-hover": "var(--gray-light-alpha-8)", + "border-strong-active": "var(--gray-light-alpha-7)", + "border-strong-selected": "var(--cobalt-light-alpha-6)", + "border-strong-disabled": "var(--gray-light-alpha-6)", + "border-strong-focus": "var(--gray-light-alpha-7)", + "border-weak-hover": "var(--gray-light-alpha-6)", + "border-weak-active": "var(--gray-light-alpha-7)", + "border-weak-selected": "var(--cobalt-light-alpha-5)", + "border-weak-disabled": "var(--gray-light-alpha-6)", + "border-weak-focus": "var(--gray-light-alpha-7)", + "border-interactive-base": "var(--cobalt-light-7)", + "border-interactive-hover": "var(--cobalt-light-8)", + "border-interactive-active": "var(--cobalt-light-9)", + "border-interactive-selected": "var(--cobalt-light-9)", + "border-interactive-disabled": "var(--gray-light-8)", + "border-interactive-focus": "var(--cobalt-light-9)", + "border-success-base": "var(--apple-light-6)", + "border-success-hover": "var(--apple-light-7)", + "border-success-selected": "var(--apple-light-9)", + "border-warning-base": "var(--solaris-light-6)", + "border-warning-hover": "var(--solaris-light-7)", + "border-warning-selected": "var(--solaris-light-9)", + "border-critical-base": "var(--ember-light-6)", + "border-critical-hover": "var(--ember-light-7)", + "border-critical-selected": "var(--ember-light-9)", + "border-info-base": "var(--lilac-light-6)", + "border-info-hover": "var(--lilac-light-7)", + "border-info-selected": "var(--lilac-light-9)", + "icon-base": "var(--gray-light-9)", + "icon-hover": "var(--gray-light-11)", + "icon-active": "var(--gray-light-12)", + "icon-selected": "var(--gray-light-12)", + "icon-disabled": "var(--gray-light-8)", + "icon-focus": "var(--gray-light-12)", + "icon-invert-base": "#ffffff", + "icon-weak-base": "var(--gray-light-7)", + "icon-weak-hover": "var(--gray-light-8)", + "icon-weak-active": "var(--gray-light-9)", + "icon-weak-selected": "var(--gray-light-10)", + "icon-weak-disabled": "var(--gray-light-6)", + "icon-weak-focus": "var(--gray-light-9)", + "icon-strong-base": "var(--gray-light-12)", + "icon-strong-hover": "#151313", + "icon-strong-active": "#020202", + "icon-strong-selected": "#020202", + "icon-strong-disabled": "var(--gray-light-8)", + "icon-strong-focus": "#020202", + "icon-brand-base": "var(--gray-light-12)", + "icon-interactive-base": "var(--cobalt-light-9)", + "icon-success-base": "var(--apple-light-7)", + "icon-success-hover": "var(--apple-light-8)", + "icon-success-active": "var(--apple-light-11)", + "icon-warning-base": "var(--amber-light-7)", + "icon-warning-hover": "var(--amber-light-8)", + "icon-warning-active": "var(--amber-light-11)", + "icon-critical-base": "var(--ember-light-10)", + "icon-critical-hover": "var(--ember-light-11)", + "icon-critical-active": "var(--ember-light-12)", + "icon-info-base": "var(--lilac-light-7)", + "icon-info-hover": "var(--lilac-light-8)", + "icon-info-active": "var(--lilac-light-11)", + "icon-on-brand-base": "var(--gray-light-alpha-11)", + "icon-on-brand-hover": "var(--gray-light-alpha-12)", + "icon-on-brand-selected": "var(--gray-light-alpha-12)", + "icon-on-interactive-base": "var(--gray-light-1)", + "icon-agent-plan-base": "var(--purple-light-9)", + "icon-agent-docs-base": "var(--amber-light-9)", + "icon-agent-ask-base": "var(--cyan-light-9)", + "icon-agent-build-base": "var(--cobalt-light-9)", + "icon-on-success-base": "var(--apple-light-alpha-9)", + "icon-on-success-hover": "var(--apple-light-alpha-10)", + "icon-on-success-selected": "var(--apple-light-alpha-11)", + "icon-on-warning-base": "var(--amber-lightalpha-9)", + "icon-on-warning-hover": "var(--amber-lightalpha-10)", + "icon-on-warning-selected": "var(--amber-lightalpha-11)", + "icon-on-critical-base": "var(--ember-light-alpha-9)", + "icon-on-critical-hover": "var(--ember-light-alpha-10)", + "icon-on-critical-selected": "var(--ember-light-alpha-11)", + "icon-on-info-base": "var(--lilac-light-9)", + "icon-on-info-hover": "var(--lilac-light-alpha-10)", + "icon-on-info-selected": "var(--lilac-light-alpha-11)", + "icon-diff-add-base": "var(--mint-light-11)", + "icon-diff-add-hover": "var(--mint-light-12)", + "icon-diff-add-active": "var(--mint-light-12)", + "icon-diff-delete-base": "var(--ember-light-10)", + "icon-diff-delete-hover": "var(--ember-light-11)", + "syntax-comment": "var(--text-weak)", + "syntax-regexp": "var(--text-base)", + "syntax-string": "#006656", + "syntax-keyword": "var(--text-weak)", + "syntax-primitive": "#fb4804", + "syntax-operator": "var(--text-base)", + "syntax-variable": "var(--text-strong)", + "syntax-property": "#ed6dc8", + "syntax-type": "#596600", + "syntax-constant": "#007b80", + "syntax-punctuation": "var(--text-base)", + "syntax-object": "var(--text-strong)", + "syntax-success": "var(--apple-light-10)", + "syntax-warning": "var(--amber-light-10)", + "syntax-critical": "var(--ember-light-10)", + "syntax-info": "#0092a8", + "syntax-diff-add": "var(--mint-light-11)", + "syntax-diff-delete": "var(--ember-light-11)", + "syntax-diff-unknown": "#ff0000", + "markdown-heading": "#d68c27", + "markdown-text": "#1a1a1a", + "markdown-link": "#3b7dd8", + "markdown-link-text": "#318795", + "markdown-code": "#3d9a57", + "markdown-block-quote": "#b0851f", + "markdown-emph": "#b0851f", + "markdown-strong": "#d68c27", + "markdown-horizontal-rule": "#8a8a8a", + "markdown-list-item": "#3b7dd8", + "markdown-list-enumeration": "#318795", + "markdown-image": "#3b7dd8", + "markdown-image-text": "#318795", + "markdown-code-block": "#1a1a1a", + "border-color": "#ffffff", + "border-weaker-base": "var(--gray-light-alpha-3)", + "border-weaker-hover": "var(--gray-light-alpha-4)", + "border-weaker-active": "var(--gray-light-alpha-6)", + "border-weaker-selected": "var(--cobalt-light-alpha-4)", + "border-weaker-disabled": "var(--gray-light-alpha-2)", + "border-weaker-focus": "var(--gray-light-alpha-6)", + "button-ghost-hover": "var(--gray-light-alpha-2)", + "button-ghost-hover2": "var(--gray-light-alpha-3)", + "avatar-background-pink": "#feeef8", + "avatar-background-mint": "#e1fbf4", + "avatar-background-orange": "#fff1e7", + "avatar-background-purple": "#f9f1fe", + "avatar-background-cyan": "#e7f9fb", + "avatar-background-lime": "#eefadc", + "avatar-text-pink": "#cd1d8d", + "avatar-text-mint": "#147d6f", + "avatar-text-orange": "#ed5f00", + "avatar-text-purple": "#8445bc", + "avatar-text-cyan": "#0894b3", + "avatar-text-lime": "#5d770d" + } + }, + "dark": { + "seeds": { + "neutral": "#716c6b", + "primary": "#fab283", + "success": "#12c905", + "warning": "#fcd53a", + "error": "#fc533a", + "info": "#edb2f1", + "interactive": "#034cff", + "diffAdd": "#c8ffc4", + "diffDelete": "#fc533a" + }, + "overrides": { + "background-base": "var(--gray-dark-1)", + "background-weak": "#1E1E1E", + "background-strong": "#181818", + "background-stronger": "#191919", + "surface-base": "var(--gray-dark-alpha-2)", + "base": "var(--gray-dark-alpha-2)", + "surface-base-hover": "#e0b7b716", + "surface-base-active": "var(--gray-dark-alpha-3)", + "surface-base-interactive-active": "var(--cobalt-dark-alpha-2)", + "base2": "var(--gray-dark-alpha-2)", + "base3": "var(--gray-dark-alpha-2)", + "surface-inset-base": "#0e0b0b7f", + "surface-inset-base-hover": "#0e0b0b7f", + "surface-inset-strong": "#060505cc", + "surface-inset-strong-hover": "#060505cc", + "surface-raised-base": "var(--gray-dark-alpha-3)", + "surface-float-base": "var(--gray-dark-1)", + "surface-float-base-hover": "var(--gray-dark-2)", + "surface-raised-base-hover": "var(--gray-dark-alpha-4)", + "surface-raised-base-active": "var(--gray-dark-alpha-5)", + "surface-raised-strong": "var(--gray-dark-alpha-4)", + "surface-raised-strong-hover": "var(--gray-dark-alpha-6)", + "surface-raised-stronger": "var(--gray-dark-alpha-6)", + "surface-raised-stronger-hover": "var(--gray-dark-alpha-7)", + "surface-weak": "var(--gray-dark-alpha-4)", + "surface-weaker": "var(--gray-dark-alpha-5)", + "surface-strong": "var(--gray-dark-alpha-7)", + "surface-raised-stronger-non-alpha": "var(--gray-dark-3)", + "surface-brand-base": "var(--yuzu-light-9)", + "surface-brand-hover": "var(--yuzu-light-10)", + "surface-interactive-base": "var(--cobalt-dark-3)", + "surface-interactive-hover": "#0A1D4D", + "surface-interactive-weak": "var(--cobalt-dark-2)", + "surface-interactive-weak-hover": "var(--cobalt-light-3)", + "surface-success-base": "var(--apple-dark-3)", + "surface-success-weak": "var(--apple-dark-2)", + "surface-success-strong": "var(--apple-dark-9)", + "surface-warning-base": "var(--solaris-light-3)", + "surface-warning-weak": "var(--solaris-light-2)", + "surface-warning-strong": "var(--solaris-light-9)", + "surface-critical-base": "var(--ember-dark-3)", + "surface-critical-weak": "var(--ember-dark-2)", + "surface-critical-strong": "var(--ember-dark-9)", + "surface-info-base": "var(--lilac-light-3)", + "surface-info-weak": "var(--lilac-light-2)", + "surface-info-strong": "var(--lilac-light-9)", + "surface-diff-unchanged-base": "var(--gray-dark-1)", + "surface-diff-skip-base": "var(--gray-dark-alpha-1)", + "surface-diff-hidden-base": "var(--blue-dark-2)", + "surface-diff-hidden-weak": "var(--blue-dark-1)", + "surface-diff-hidden-weaker": "var(--blue-dark-3)", + "surface-diff-hidden-strong": "var(--blue-dark-5)", + "surface-diff-hidden-stronger": "var(--blue-dark-11)", + "surface-diff-add-base": "var(--mint-dark-3)", + "surface-diff-add-weak": "var(--mint-dark-4)", + "surface-diff-add-weaker": "var(--mint-dark-3)", + "surface-diff-add-strong": "var(--mint-dark-5)", + "surface-diff-add-stronger": "var(--mint-dark-11)", + "surface-diff-delete-base": "var(--ember-dark-3)", + "surface-diff-delete-weak": "var(--ember-dark-4)", + "surface-diff-delete-weaker": "var(--ember-dark-3)", + "surface-diff-delete-strong": "var(--ember-dark-5)", + "surface-diff-delete-stronger": "var(--ember-dark-11)", + "input-base": "var(--gray-dark-2)", + "input-hover": "var(--gray-dark-2)", + "input-active": "var(--cobalt-dark-1)", + "input-selected": "var(--cobalt-dark-2)", + "input-focus": "var(--cobalt-dark-1)", + "input-disabled": "var(--gray-dark-4)", + "text-base": "var(--gray-dark-alpha-11)", + "text-weak": "var(--gray-dark-alpha-9)", + "text-weaker": "var(--gray-dark-alpha-8)", + "text-strong": "var(--gray-dark-alpha-12)", + "text-invert-base": "var(--gray-dark-alpha-11)", + "text-invert-weak": "var(--gray-dark-alpha-9)", + "text-invert-weaker": "var(--gray-dark-alpha-8)", + "text-invert-strong": "var(--gray-dark-alpha-12)", + "text-interactive-base": "var(--cobalt-dark-11)", + "text-on-brand-base": "var(--gray-dark-alpha-11)", + "text-on-interactive-base": "var(--gray-dark-12)", + "text-on-interactive-weak": "var(--gray-dark-alpha-11)", + "text-on-success-base": "var(--apple-dark-9)", + "text-on-critical-base": "var(--ember-dark-9)", + "text-on-critical-weak": "var(--ember-dark-8)", + "text-on-critical-strong": "var(--ember-dark-12)", + "text-on-warning-base": "var(--gray-dark-alpha-11)", + "text-on-info-base": "var(--gray-dark-alpha-11)", + "text-diff-add-base": "var(--mint-dark-11)", + "text-diff-delete-base": "var(--ember-dark-9)", + "text-diff-delete-strong": "var(--ember-dark-12)", + "text-diff-add-strong": "var(--mint-dark-8)", + "text-on-info-weak": "var(--gray-dark-alpha-9)", + "text-on-info-strong": "var(--gray-dark-alpha-12)", + "text-on-warning-weak": "var(--gray-dark-alpha-9)", + "text-on-warning-strong": "var(--gray-dark-alpha-12)", + "text-on-success-weak": "var(--apple-dark-8)", + "text-on-success-strong": "var(--apple-dark-12)", + "text-on-brand-weak": "var(--gray-dark-alpha-9)", + "text-on-brand-weaker": "var(--gray-dark-alpha-8)", + "text-on-brand-strong": "var(--gray-dark-alpha-12)", + "button-primary-base": "var(--gray-dark-12)", + "button-secondary-base": "#231f1f", + "button-secondary-hover": "#2a2727", + "border-base": "var(--gray-dark-alpha-7)", + "border-hover": "var(--gray-dark-alpha-8)", + "border-active": "var(--gray-dark-alpha-9)", + "border-selected": "var(--cobalt-dark-alpha-11)", + "border-disabled": "var(--gray-dark-alpha-8)", + "border-focus": "var(--gray-dark-alpha-9)", + "border-weak-base": "var(--gray-dark-alpha-4)", + "border-weak-hover": "var(--gray-dark-alpha-7)", + "border-weak-active": "var(--gray-dark-alpha-8)", + "border-weak-selected": "var(--cobalt-dark-alpha-6)", + "border-weak-disabled": "var(--gray-dark-alpha-6)", + "border-weak-focus": "var(--gray-dark-alpha-8)", + "border-strong-base": "var(--gray-dark-alpha-8)", + "border-interactive-base": "var(--cobalt-light-7)", + "border-interactive-hover": "var(--cobalt-light-8)", + "border-interactive-active": "var(--cobalt-light-9)", + "border-interactive-selected": "var(--cobalt-light-9)", + "border-interactive-disabled": "var(--gray-light-8)", + "border-interactive-focus": "var(--cobalt-light-9)", + "border-success-base": "var(--apple-light-6)", + "border-success-hover": "var(--apple-light-7)", + "border-success-selected": "var(--apple-light-9)", + "border-warning-base": "var(--solaris-light-6)", + "border-warning-hover": "var(--solaris-light-7)", + "border-warning-selected": "var(--solaris-light-9)", + "border-critical-base": "var(--ember-dark-5)", + "border-critical-hover": "var(--ember-dark-7)", + "border-critical-selected": "var(--ember-dark-9)", + "border-info-base": "var(--lilac-light-6)", + "border-info-hover": "var(--lilac-light-7)", + "border-info-selected": "var(--lilac-light-9)", + "icon-base": "var(--gray-dark-10)", + "icon-hover": "var(--gray-dark-11)", + "icon-active": "var(--gray-dark-12)", + "icon-selected": "var(--gray-dark-12)", + "icon-disabled": "var(--gray-dark-8)", + "icon-focus": "var(--gray-dark-12)", + "icon-invert-base": "var(--gray-dark-1)", + "icon-weak-base": "var(--gray-dark-6)", + "icon-weak-hover": "var(--gray-light-7)", + "icon-weak-active": "var(--gray-light-8)", + "icon-weak-selected": "var(--gray-light-9)", + "icon-weak-disabled": "var(--gray-light-4)", + "icon-weak-focus": "var(--gray-light-9)", + "icon-strong-base": "var(--gray-dark-12)", + "icon-strong-hover": "#F3F3F3", + "icon-strong-active": "#EBEBEB", + "icon-strong-selected": "#FCFCFC", + "icon-strong-disabled": "var(--gray-dark-8)", + "icon-strong-focus": "#FCFCFC", + "icon-brand-base": "var(--white)", + "icon-interactive-base": "var(--cobalt-dark-11)", + "icon-success-base": "var(--apple-dark-9)", + "icon-success-hover": "var(--apple-dark-10)", + "icon-success-active": "var(--apple-dark-11)", + "icon-warning-base": "var(--amber-dark-9)", + "icon-warning-hover": "var(--amber-dark-8)", + "icon-warning-active": "var(--amber-dark-11)", + "icon-critical-base": "var(--ember-dark-9)", + "icon-critical-hover": "var(--ember-dark-11)", + "icon-critical-active": "var(--ember-dark-12)", + "icon-info-base": "var(--lilac-dark-7)", + "icon-info-hover": "var(--lilac-dark-8)", + "icon-info-active": "var(--lilac-dark-11)", + "icon-on-brand-base": "var(--gray-light-alpha-11)", + "icon-on-brand-hover": "var(--gray-light-alpha-12)", + "icon-on-brand-selected": "var(--gray-light-alpha-12)", + "icon-on-interactive-base": "var(--gray-dark-12)", + "icon-agent-plan-base": "var(--purple-dark-9)", + "icon-agent-docs-base": "var(--amber-dark-9)", + "icon-agent-ask-base": "var(--cyan-dark-9)", + "icon-agent-build-base": "var(--cobalt-dark-11)", + "icon-on-success-base": "var(--apple-dark-alpha-9)", + "icon-on-success-hover": "var(--apple-dark-alpha-10)", + "icon-on-success-selected": "var(--apple-dark-alpha-11)", + "icon-on-warning-base": "var(--amber-darkalpha-9)", + "icon-on-warning-hover": "var(--amber-darkalpha-10)", + "icon-on-warning-selected": "var(--amber-darkalpha-11)", + "icon-on-critical-base": "var(--ember-dark-alpha-9)", + "icon-on-critical-hover": "var(--ember-dark-alpha-10)", + "icon-on-critical-selected": "var(--ember-dark-alpha-11)", + "icon-on-info-base": "var(--lilac-dark-9)", + "icon-on-info-hover": "var(--lilac-dark-alpha-10)", + "icon-on-info-selected": "var(--lilac-dark-alpha-11)", + "icon-diff-add-base": "var(--mint-dark-11)", + "icon-diff-add-hover": "var(--mint-dark-10)", + "icon-diff-add-active": "var(--mint-dark-11)", + "icon-diff-delete-base": "var(--ember-dark-9)", + "icon-diff-delete-hover": "var(--ember-dark-10)", + "syntax-comment": "var(--text-weak)", + "syntax-regexp": "var(--text-base)", + "syntax-string": "#00ceb9", + "syntax-keyword": "var(--text-weak)", + "syntax-primitive": "#ffba92", + "syntax-operator": "var(--text-weak)", + "syntax-variable": "var(--text-strong)", + "syntax-property": "#ff9ae2", + "syntax-type": "#ecf58c", + "syntax-constant": "#93e9f6", + "syntax-punctuation": "var(--text-weak)", + "syntax-object": "var(--text-strong)", + "syntax-success": "var(--apple-dark-10)", + "syntax-warning": "var(--amber-dark-10)", + "syntax-critical": "var(--ember-dark-10)", + "syntax-info": "#93e9f6", + "syntax-diff-add": "var(--mint-dark-11)", + "syntax-diff-delete": "var(--ember-dark-11)", + "syntax-diff-unknown": "#ff0000", + "markdown-heading": "#9d7cd8", + "markdown-text": "#eeeeee", + "markdown-link": "#fab283", + "markdown-link-text": "#56b6c2", + "markdown-code": "#7fd88f", + "markdown-block-quote": "#e5c07b", + "markdown-emph": "#e5c07b", + "markdown-strong": "#f5a742", + "markdown-horizontal-rule": "#808080", + "markdown-list-item": "#fab283", + "markdown-list-enumeration": "#56b6c2", + "markdown-image": "#fab283", + "markdown-image-text": "#56b6c2", + "markdown-code-block": "#eeeeee", + "border-color": "#ffffff", + "border-weaker-base": "var(--gray-dark-alpha-3)", + "border-weaker-hover": "var(--gray-dark-alpha-4)", + "border-weaker-active": "var(--gray-dark-alpha-6)", + "border-weaker-selected": "var(--cobalt-dark-alpha-3)", + "border-weaker-disabled": "var(--gray-dark-alpha-2)", + "border-weaker-focus": "var(--gray-dark-alpha-6)", + "button-ghost-hover": "var(--gray-dark-alpha-2)", + "button-ghost-hover2": "var(--gray-dark-alpha-3)", + "avatar-background-pink": "#501b3f", + "avatar-background-mint": "#033a34", + "avatar-background-orange": "#5f2a06", + "avatar-background-purple": "#432155", + "avatar-background-cyan": "#0f3058", + "avatar-background-lime": "#2b3711", + "avatar-text-pink": "#e34ba9", + "avatar-text-mint": "#95f3d9", + "avatar-text-orange": "#ff802b", + "avatar-text-purple": "#9d5bd2", + "avatar-text-cyan": "#369eff", + "avatar-text-lime": "#c4f042" + } + } +} From 5d69f00282376bdde4133459374593457ab33e83 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 16:36:40 +0000 Subject: [PATCH 165/255] button style tweaks --- packages/ui/src/components/button.css | 4 ---- packages/ui/src/components/icon-button.css | 4 ---- 2 files changed, 8 deletions(-) diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index 794002888a11..2d803d95c6b5 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -171,10 +171,6 @@ } } -[data-component="button"].titlebar-icon[data-variant="ghost"]:hover:not(:disabled) { - background-color: var(--surface-raised-base-active); -} - [data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] { background-color: var(--surface-base-active); } diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index ad8200ae6824..10337bbcc3c0 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -168,10 +168,6 @@ aspect-ratio: auto; } -[data-component="icon-button"].titlebar-icon[data-variant="ghost"]:hover:not(:disabled) { - background-color: var(--surface-raised-base-active); -} - [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] { background-color: var(--surface-base-active); } From 24ce49d9d7c225651eb04db49f4a92f57a0d3412 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 16:45:50 +0000 Subject: [PATCH 166/255] fix(ui): add previous smoke colors --- packages/ui/src/styles/colors.css | 196 +++++++++++++++++++++++++----- 1 file changed, 164 insertions(+), 32 deletions(-) diff --git a/packages/ui/src/styles/colors.css b/packages/ui/src/styles/colors.css index 5ef17d1cc518..893f252aa99c 100644 --- a/packages/ui/src/styles/colors.css +++ b/packages/ui/src/styles/colors.css @@ -1,52 +1,52 @@ :root { --gray-dark-1: #161616; - --gray-dark-2: #1C1C1C; + --gray-dark-2: #1c1c1c; --gray-dark-3: #232323; --gray-dark-4: #282828; - --gray-dark-5: #2E2E2E; + --gray-dark-5: #2e2e2e; --gray-dark-6: #343434; - --gray-dark-7: #3E3E3E; + --gray-dark-7: #3e3e3e; --gray-dark-8: #505050; --gray-dark-9: #707070; - --gray-dark-10: #7E7E7E; - --gray-dark-11: #A0A0A0; - --gray-dark-12: #EDEDED; - --gray-light-1: #FCFCFC; - --gray-light-2: #F8F8F8; - --gray-light-3: #F3F3F3; - --gray-light-4: #EDEDED; - --gray-light-5: #E8E8E8; - --gray-light-6: #E2E2E2; - --gray-light-7: #DBDBDB; - --gray-light-8: #C7C7C7; - --gray-light-9: #8F8F8F; + --gray-dark-10: #7e7e7e; + --gray-dark-11: #a0a0a0; + --gray-dark-12: #ededed; + --gray-light-1: #fcfcfc; + --gray-light-2: #f8f8f8; + --gray-light-3: #f3f3f3; + --gray-light-4: #ededed; + --gray-light-5: #e8e8e8; + --gray-light-6: #e2e2e2; + --gray-light-7: #dbdbdb; + --gray-light-8: #c7c7c7; + --gray-light-9: #8f8f8f; --gray-light-10: #858585; - --gray-light-11: #6F6F6F; + --gray-light-11: #6f6f6f; --gray-light-12: #171717; --gray-dark-alpha-1: #00000000; - --gray-dark-alpha-2: #FFFFFF08; - --gray-dark-alpha-3: #FFFFFF0F; - --gray-dark-alpha-4: #FFFFFF14; - --gray-dark-alpha-5: #FFFFFF1A; - --gray-dark-alpha-6: #FFFFFF21; - --gray-dark-alpha-7: #FFFFFF2B; - --gray-dark-alpha-8: #FFFFFF40; - --gray-dark-alpha-9: #FFFFFF63; - --gray-dark-alpha-10: #FFFFFF73; - --gray-dark-alpha-11: #FFFFFF96; - --gray-dark-alpha-12: #FFFFFFEB; + --gray-dark-alpha-2: #ffffff08; + --gray-dark-alpha-3: #ffffff0f; + --gray-dark-alpha-4: #ffffff14; + --gray-dark-alpha-5: #ffffff1a; + --gray-dark-alpha-6: #ffffff21; + --gray-dark-alpha-7: #ffffff2b; + --gray-dark-alpha-8: #ffffff40; + --gray-dark-alpha-9: #ffffff63; + --gray-dark-alpha-10: #ffffff73; + --gray-dark-alpha-11: #ffffff96; + --gray-dark-alpha-12: #ffffffeb; --gray-light-alpha-1: #00000003; --gray-light-alpha-2: #00000008; - --gray-light-alpha-3: #0000000D; + --gray-light-alpha-3: #0000000d; --gray-light-alpha-4: #00000012; --gray-light-alpha-5: #00000017; - --gray-light-alpha-6: #0000001C; + --gray-light-alpha-6: #0000001c; --gray-light-alpha-7: #00000024; --gray-light-alpha-8: #00000038; --gray-light-alpha-9: #00000070; - --gray-light-alpha-10: #0000007A; - --gray-light-alpha-11: #0000008F; - --gray-light-alpha-12: #000000E8; + --gray-light-alpha-10: #0000007a; + --gray-light-alpha-11: #0000008f; + --gray-light-alpha-12: #000000e8; --gray-dark-1: #131010; --gray-dark-2: #1b1818; --gray-dark-3: #252121; @@ -95,6 +95,54 @@ --gray-light-alpha-10: #0400009c; --gray-light-alpha-11: #0700007e; --gray-light-alpha-12: #020000df; + --smoke-dark-1: #131010; + --smoke-dark-2: #1b1818; + --smoke-dark-3: #252121; + --smoke-dark-4: #2d2828; + --smoke-dark-5: #343030; + --smoke-dark-6: #3e3939; + --smoke-dark-7: #4b4646; + --smoke-dark-8: #645f5f; + --smoke-dark-9: #716c6b; + --smoke-dark-10: #7f7979; + --smoke-dark-11: #b7b1b1; + --smoke-dark-12: #f1ecec; + --smoke-light-1: #fdfcfc; + --smoke-light-2: #f9f8f8; + --smoke-light-3: #f1f0f0; + --smoke-light-4: #e9e8e8; + --smoke-light-5: #e2e0e0; + --smoke-light-6: #dad9d9; + --smoke-light-7: #cfcecd; + --smoke-light-8: #bcbbbb; + --smoke-light-9: #8e8b8b; + --smoke-light-10: #848181; + --smoke-light-11: #656363; + --smoke-light-12: #211e1e; + --smoke-dark-alpha-1: #82383803; + --smoke-dark-alpha-2: #e6c6c60b; + --smoke-dark-alpha-3: #edd5d516; + --smoke-dark-alpha-4: #f2e1e11e; + --smoke-dark-alpha-5: #f5e8e826; + --smoke-dark-alpha-6: #f5e8e831; + --smoke-dark-alpha-7: #f7ecec3f; + --smoke-dark-alpha-8: #faf5f559; + --smoke-dark-alpha-9: #faf5f467; + --smoke-dark-alpha-10: #fbf5f576; + --smoke-dark-alpha-11: #fcf9f9b2; + --smoke-dark-alpha-12: #fdfbfbf0; + --smoke-light-alpha-1: #55000003; + --smoke-light-alpha-2: #25000007; + --smoke-light-alpha-3: #1100000f; + --smoke-light-alpha-4: #0c000017; + --smoke-light-alpha-5: #1100001f; + --smoke-light-alpha-6: #07000026; + --smoke-light-alpha-7: #0b060032; + --smoke-light-alpha-8: #04000044; + --smoke-light-alpha-9: #07000074; + --smoke-light-alpha-10: #0400009c; + --smoke-light-alpha-11: #0700007e; + --smoke-light-alpha-12: #020000df; --yuzu-dark-1: #11120c; --yuzu-light-1: #fdfdfb; --yuzu-light-2: #fbfceb; @@ -637,4 +685,88 @@ --amber-dark-alpha-10: #ffce48f9; --amber-dark-alpha-11: #ffab0eef; --amber-dark-alpha-12: #fff8e1f9; + + /* Legacy palette aliases (keeps older themes working) */ + --smoke-light-1: var(--gray-light-1); + --smoke-light-2: var(--gray-light-2); + --smoke-light-3: var(--gray-light-3); + --smoke-light-4: var(--gray-light-4); + --smoke-light-5: var(--gray-light-5); + --smoke-light-6: var(--gray-light-6); + --smoke-light-7: var(--gray-light-7); + --smoke-light-8: var(--gray-light-8); + --smoke-light-9: var(--gray-light-9); + --smoke-light-10: var(--gray-light-10); + --smoke-light-11: var(--gray-light-11); + --smoke-light-12: var(--gray-light-12); + + --smoke-dark-1: var(--gray-dark-1); + --smoke-dark-2: var(--gray-dark-2); + --smoke-dark-3: var(--gray-dark-3); + --smoke-dark-4: var(--gray-dark-4); + --smoke-dark-5: var(--gray-dark-5); + --smoke-dark-6: var(--gray-dark-6); + --smoke-dark-7: var(--gray-dark-7); + --smoke-dark-8: var(--gray-dark-8); + --smoke-dark-9: var(--gray-dark-9); + --smoke-dark-10: var(--gray-dark-10); + --smoke-dark-11: var(--gray-dark-11); + --smoke-dark-12: var(--gray-dark-12); + + --smoke-light-alpha-1: var(--gray-light-alpha-1); + --smoke-light-alpha-2: var(--gray-light-alpha-2); + --smoke-light-alpha-3: var(--gray-light-alpha-3); + --smoke-light-alpha-4: var(--gray-light-alpha-4); + --smoke-light-alpha-5: var(--gray-light-alpha-5); + --smoke-light-alpha-6: var(--gray-light-alpha-6); + --smoke-light-alpha-7: var(--gray-light-alpha-7); + --smoke-light-alpha-8: var(--gray-light-alpha-8); + --smoke-light-alpha-9: var(--gray-light-alpha-9); + --smoke-light-alpha-10: var(--gray-light-alpha-10); + --smoke-light-alpha-11: var(--gray-light-alpha-11); + --smoke-light-alpha-12: var(--gray-light-alpha-12); + + --smoke-dark-alpha-1: var(--gray-dark-alpha-1); + --smoke-dark-alpha-2: var(--gray-dark-alpha-2); + --smoke-dark-alpha-3: var(--gray-dark-alpha-3); + --smoke-dark-alpha-4: var(--gray-dark-alpha-4); + --smoke-dark-alpha-5: var(--gray-dark-alpha-5); + --smoke-dark-alpha-6: var(--gray-dark-alpha-6); + --smoke-dark-alpha-7: var(--gray-dark-alpha-7); + --smoke-dark-alpha-8: var(--gray-dark-alpha-8); + --smoke-dark-alpha-9: var(--gray-dark-alpha-9); + --smoke-dark-alpha-10: var(--gray-dark-alpha-10); + --smoke-dark-alpha-11: var(--gray-dark-alpha-11); + --smoke-dark-alpha-12: var(--gray-dark-alpha-12); + + --amber-lightalpha-1: var(--amber-light-alpha-1); + --amber-lightalpha-2: var(--amber-light-alpha-2); + --amber-lightalpha-3: var(--amber-light-alpha-3); + --amber-lightalpha-4: var(--amber-light-alpha-4); + --amber-lightalpha-5: var(--amber-light-alpha-5); + --amber-lightalpha-6: var(--amber-light-alpha-6); + --amber-lightalpha-7: var(--amber-light-alpha-7); + --amber-lightalpha-8: var(--amber-light-alpha-8); + --amber-lightalpha-9: var(--amber-light-alpha-9); + --amber-lightalpha-10: var(--amber-light-alpha-10); + --amber-lightalpha-11: var(--amber-light-alpha-11); + --amber-lightalpha-12: var(--amber-light-alpha-12); + + --amber-darkalpha-1: var(--amber-dark-alpha-1); + --amber-darkalpha-2: var(--amber-dark-alpha-2); + --amber-darkalpha-3: var(--amber-dark-alpha-3); + --amber-darkalpha-4: var(--amber-dark-alpha-4); + --amber-darkalpha-5: var(--amber-dark-alpha-5); + --amber-darkalpha-6: var(--amber-dark-alpha-6); + --amber-darkalpha-7: var(--amber-dark-alpha-7); + --amber-darkalpha-8: var(--amber-dark-alpha-8); + --amber-darkalpha-9: var(--amber-dark-alpha-9); + --amber-darkalpha-10: var(--amber-dark-alpha-10); + --amber-darkalpha-11: var(--amber-dark-alpha-11); + --amber-darkalpha-12: var(--amber-dark-alpha-12); + + --purple-light-9: var(--lilac-light-9); + --purple-dark-9: var(--lilac-dark-9); + --cyan-light-9: var(--blue-light-9); + --cyan-dark-9: var(--blue-dark-9); } From 0888c02379f0dd57a17b42788469d713c71ddb51 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 16:58:23 +0000 Subject: [PATCH 167/255] tweak(ui): file tree background color --- packages/app/src/pages/session/session-side-panel.tsx | 4 ++-- packages/ui/src/components/tabs.css | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 7f2d4fc76736..07b18f3146d5 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -354,7 +354,7 @@ export function SessionSidePanel(props: { {language.t("session.files.all")} - + - + Date: Wed, 18 Feb 2026 16:58:36 +0000 Subject: [PATCH 168/255] tweak(ui): share button border --- packages/app/src/components/session/session-header.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 3003d05148f9..58ffd15cd89c 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -449,7 +449,7 @@ export function SessionHeader() { triggerProps={{ variant: "ghost", class: - "rounded-md h-[24px] px-3 border border-border-base bg-surface-panel shadow-none data-[expanded]:bg-surface-raised-base-active", + "rounded-md h-[24px] px-3 border border-border-weak-base bg-surface-panel shadow-none data-[expanded]:bg-surface-raised-base-active", classList: { "rounded-r-none": share.shareUrl() !== undefined }, style: { scale: 1 }, }} @@ -522,7 +522,7 @@ export function SessionHeader() { share.copyLink((error) => showRequestError(language, error))} disabled={share.state.unshare} aria-label={ From f20c0bffd3e6701ce191ed49dab1fa29400e866f Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 17:02:10 +0000 Subject: [PATCH 169/255] tweak(ui): unify titlebar expanded button background --- packages/ui/src/components/button.css | 4 ++-- packages/ui/src/components/icon-button.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index 2d803d95c6b5..e22c7b2987da 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -172,7 +172,7 @@ } [data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] { - background-color: var(--surface-base-active); + background-color: var(--surface-raised-base-active); } [data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] [data-slot="icon-svg"] { @@ -180,5 +180,5 @@ } [data-component="button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"]:hover:not(:disabled) { - background-color: var(--surface-base-active); + background-color: var(--surface-raised-base-active); } diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index 10337bbcc3c0..01c555bfc7de 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -169,7 +169,7 @@ } [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] { - background-color: var(--surface-base-active); + background-color: var(--surface-raised-base-active); } [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"] [data-slot="icon-svg"] { @@ -177,5 +177,5 @@ } [data-component="icon-button"].titlebar-icon[data-variant="ghost"][aria-expanded="true"]:hover:not(:disabled) { - background-color: var(--surface-base-active); + background-color: var(--surface-raised-base-active); } From e5d52e4eb528055ffc0d461c451ff4c79fe7e99d Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 17:08:49 +0000 Subject: [PATCH 170/255] tweak(ui): align pill tabs pressed background --- packages/ui/src/components/tabs.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index fee929d3445e..2a75defa287c 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -257,6 +257,10 @@ color: var(--text-strong); } + &:active:not(:disabled) { + background-color: var(--surface-raised-base-active); + } + &:has([data-selected]) { background-color: var(--surface-raised-base-active); color: var(--text-strong); From 4db2d94854500cf939b95fb030e35c29982f1fdf Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 17:15:44 +0000 Subject: [PATCH 171/255] tweak(ui): shrink filetree tab height --- packages/ui/src/components/tabs.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index 2a75defa287c..de5e1750e96c 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -282,7 +282,7 @@ } [data-slot="tabs-trigger-wrapper"] { - height: 26px; + height: 24px; border-radius: 6px; color: var(--text-weak); From 08739080309bb84be71b5dd30ce6541e1bf9c029 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 17:25:50 +0000 Subject: [PATCH 172/255] tweak(ui): theme color updates --- packages/ui/src/theme/themes/oc-2.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json index d47322aef4d0..ab17d9b6f8c2 100644 --- a/packages/ui/src/theme/themes/oc-2.json +++ b/packages/ui/src/theme/themes/oc-2.json @@ -34,7 +34,7 @@ "surface-float-base": "var(--gray-dark-1)", "surface-float-base-hover": "var(--gray-dark-2)", "surface-raised-base-hover": "var(--gray-light-alpha-3)", - "surface-raised-base-active": "var(--gray-light-alpha-4)", + "surface-raised-base-active": "var(--gray-light-alpha-5)", "surface-raised-strong": "var(--gray-light-1)", "surface-raised-strong-hover": "var(--white)", "surface-raised-stronger": "var(--white)", @@ -281,10 +281,10 @@ "diffDelete": "#fc533a" }, "overrides": { - "background-base": "var(--gray-dark-1)", + "background-base": "#191919", "background-weak": "#1E1E1E", "background-strong": "#181818", - "background-stronger": "#191919", + "background-stronger": "#151515", "surface-base": "var(--gray-dark-alpha-2)", "base": "var(--gray-dark-alpha-2)", "surface-base-hover": "#e0b7b716", @@ -308,7 +308,7 @@ "surface-weak": "var(--gray-dark-alpha-4)", "surface-weaker": "var(--gray-dark-alpha-5)", "surface-strong": "var(--gray-dark-alpha-7)", - "surface-raised-stronger-non-alpha": "var(--gray-dark-3)", + "surface-raised-stronger-non-alpha": "var(--gray-dark-2)", "surface-brand-base": "var(--yuzu-light-9)", "surface-brand-hover": "var(--yuzu-light-10)", "surface-interactive-base": "var(--cobalt-dark-3)", From 1f9be63e962374e2a0c668d8098b4dccb4d0b79a Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 17:32:46 +0000 Subject: [PATCH 173/255] tweak(ui): use weak border and base icon color for secondary --- packages/ui/src/components/button.css | 4 ++-- packages/ui/src/components/icon-button.css | 2 +- packages/ui/src/components/session-review.tsx | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index e22c7b2987da..8219ec92b91f 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -76,7 +76,7 @@ border: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); - box-shadow: var(--shadow-xs-border); + box-shadow: var(--shadow-xs-border-base); &:hover:not(:disabled) { background-color: var(--button-secondary-hover); @@ -104,7 +104,7 @@ } [data-slot="icon-svg"] { - color: var(--icon-strong-base); + color: var(--icon-base); } } diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index 01c555bfc7de..de8aeb9d491b 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -45,7 +45,7 @@ border: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); - box-shadow: var(--shadow-xs-border); + box-shadow: var(--shadow-xs-border-base); &:hover:not(:disabled) { background-color: var(--button-secondary-hover); diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 6ab922262b77..1fdc2ede105d 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -306,7 +306,12 @@ export const SessionReview = (props: SessionReviewProps) => { />
- + )} From c7a79f1877d0f13f194db5e072f2ee4cef5e174a Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:36:38 +0000 Subject: [PATCH 183/255] Update icon-button.css --- packages/ui/src/components/icon-button.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index de8aeb9d491b..7a47270fe9ae 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -84,23 +84,23 @@ } &:hover:not(:disabled) { - background-color: var(--surface-raised-base-hover); + background-color: var(--surface-base-hover); /* [data-slot="icon-svg"] { */ /* color: var(--icon-hover); */ /* } */ } &:focus-visible:not(:disabled) { - background-color: var(--surface-raised-base-hover); + background-color: var(--surface-base-hover); } &:active:not(:disabled) { - background-color: var(--surface-raised-base-active); + background-color: var(--surface-base-active); /* [data-slot="icon-svg"] { */ /* color: var(--icon-active); */ /* } */ } &:selected:not(:disabled) { - background-color: var(--surface-raised-base-active); + background-color: var(--surface-base-active); /* [data-slot="icon-svg"] { */ /* color: var(--icon-selected); */ /* } */ From e42cc8511299ce1a9f311d3446b03747823a23fc Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:36:42 +0000 Subject: [PATCH 184/255] Update oc-2.json --- packages/ui/src/theme/themes/oc-2.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json index eae31e629eb9..e12ddc0b9ac7 100644 --- a/packages/ui/src/theme/themes/oc-2.json +++ b/packages/ui/src/theme/themes/oc-2.json @@ -284,9 +284,9 @@ "base": "var(--gray-dark-alpha-2)", "base2": "var(--gray-dark-alpha-2)", "base3": "var(--gray-dark-alpha-2)", - "background-base": "#191919", + "background-base": "#101010", "background-weak": "#1E1E1E", - "background-strong": "#181818", + "background-strong": "#121212", "background-stronger": "#151515", "surface-base": "var(--gray-dark-alpha-2)", "surface-base-hover": "#FFFFFF0A", From d730d8be01366999f4f453db3b7bddaf7970e0c1 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:38:12 +0000 Subject: [PATCH 185/255] tweak(ui): shrink review diff style toggle --- packages/ui/src/components/session-review.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 1fdc2ede105d..481379d45a8a 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -298,6 +298,7 @@ export const SessionReview = (props: SessionReviewProps) => { style} label={(style) => i18n.t(style === "unified" ? "ui.sessionReview.diffStyle.unified" : "ui.sessionReview.diffStyle.split") From 1571246ba8f9c0f41889de5516769116aee38692 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:39:14 +0000 Subject: [PATCH 186/255] tweak(ui): use default cursor for segmented control --- packages/ui/src/components/radio-group.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/radio-group.css b/packages/ui/src/components/radio-group.css index 9f7bd5a9ef73..1da562b1d5d5 100644 --- a/packages/ui/src/components/radio-group.css +++ b/packages/ui/src/components/radio-group.css @@ -80,7 +80,7 @@ [data-slot="radio-group-item-label"] { color: var(--text-weak); - cursor: pointer; + cursor: default; display: flex; align-items: center; justify-content: center; From 1b67339e4dd9902b4d59abc444df8d9b52a6b67e Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:46:33 +0000 Subject: [PATCH 187/255] Update radio-group.css --- packages/ui/src/components/radio-group.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/radio-group.css b/packages/ui/src/components/radio-group.css index 1da562b1d5d5..4faaa33f4342 100644 --- a/packages/ui/src/components/radio-group.css +++ b/packages/ui/src/components/radio-group.css @@ -38,9 +38,9 @@ } [data-slot="radio-group-indicator"] { - background: var(--surface-raised-stronger-non-alpha); + background: var(--button-secondary-base); border-radius: var(--radius-sm); - box-shadow: var(--shadow-xs-border); + box-shadow: var(--shadow-xs-border-base); content: ""; opacity: var(--indicator-opacity, 1); pointer-events: none; From 06b2304a5f66cc4f72c95900717b674a66b5f308 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:46:50 +0000 Subject: [PATCH 188/255] tweak(ui): override for the radio group in the review --- packages/ui/src/components/session-review.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 46473b75e5f2..d5ad346e6048 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -42,6 +42,14 @@ padding-right: 1px; } + [data-slot="session-review-actions"] [data-component="radio-group"] { + [data-slot="radio-group-wrapper"], + [data-slot="radio-group-indicator"], + [data-slot="radio-group-item-control"] { + border-radius: 6px; + } + } + [data-component="sticky-accordion-header"] { top: 40px; } From 31e964e7cf4f83abec80640bb1f70a950615c595 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:46:54 +0000 Subject: [PATCH 189/255] Update oc-2.json --- packages/ui/src/theme/themes/oc-2.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json index e12ddc0b9ac7..b6c0a3aa77ca 100644 --- a/packages/ui/src/theme/themes/oc-2.json +++ b/packages/ui/src/theme/themes/oc-2.json @@ -116,7 +116,7 @@ "text-on-brand-weaker": "var(--gray-light-alpha-8)", "text-on-brand-strong": "var(--gray-light-alpha-12)", "button-primary-base": "var(--gray-light-12)", - "button-secondary-base": "var(--gray-light-alpha-2)", + "button-secondary-base": "var(--gray-light-1)", "button-secondary-hover": "FFFFFF0A", "border-base": "var(--gray-light-alpha-7)", "border-hover": "var(--gray-light-alpha-8)", @@ -382,7 +382,7 @@ "text-on-brand-weaker": "var(--gray-dark-alpha-8)", "text-on-brand-strong": "var(--gray-dark-alpha-12)", "button-primary-base": "var(--gray-dark-12)", - "button-secondary-base": "var(--gray-dark-alpha-2)", + "button-secondary-base": "var(--gray-dark-2)", "button-secondary-hover": "#FFFFFF0A", "border-base": "var(--gray-dark-alpha-7)", "border-hover": "var(--gray-dark-alpha-8)", From bb6d1d502fb9afb034da2c20078e6d4b5f9c6e2f Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:49:50 +0000 Subject: [PATCH 190/255] tweak(ui): adjust review diff style hover radius --- packages/ui/src/components/session-review.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index d5ad346e6048..5f42d1dc16f8 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -48,6 +48,12 @@ [data-slot="radio-group-item-control"] { border-radius: 6px; } + + [data-slot="radio-group-item-input"]:not([data-checked], [data-disabled]) + + [data-slot="radio-group-item-label"]:hover + [data-slot="radio-group-item-control"] { + border-radius: 4px; + } } [data-component="sticky-accordion-header"] { From 47b4de3531ac8f32f90be3f867dba02120d2b83a Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:50:48 +0000 Subject: [PATCH 191/255] tweak(ui): tighten review header action spacing --- packages/ui/src/components/session-review.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 5f42d1dc16f8..c618ed58c8be 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -38,7 +38,7 @@ [data-slot="session-review-actions"] { display: flex; align-items: center; - column-gap: 16px; + column-gap: 12px; padding-right: 1px; } From ba919fb619312c4c77865d4506d5a400d2abca26 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 19:51:46 +0000 Subject: [PATCH 192/255] tweak(ui): shrink review expand/collapse width --- packages/ui/src/components/session-review.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 481379d45a8a..2a8685fac2b7 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -310,7 +310,7 @@ export const SessionReview = (props: SessionReviewProps) => { - + const openComment = (comment: SessionReviewComment) => { + setOpened({ file: comment.file, id: comment.id }) + setSelection({ file: comment.file, range: comment.selection }) + } + + const isCommentOpen = (comment: SessionReviewComment) => { + const current = opened() + if (!current) return false + return current.file === comment.file && current.id === comment.id + } + + return ( + + + +
+
+ +
+ + {`\u202A${getDirectory(diff.file)}\u202C`} + + {getFilename(diff.file)} + + + +
+
+
+ + + + {i18n.t("ui.sessionReview.change.added")} + + + + + {i18n.t("ui.sessionReview.change.removed")} + + + + + {i18n.t("ui.sessionReview.change.modified")} + + + + + + +
-
+ + + +
{ + wrapper = el + anchors.set(diff.file, el) + scheduleAnchors() + }} + > + - - - {i18n.t("ui.sessionReview.change.added")} - + +
+ {diff.file} +
- - - {i18n.t("ui.sessionReview.change.removed")} - + +
+ + {i18n.t("ui.sessionReview.change.removed")} + +
- - - {i18n.t("ui.sessionReview.change.modified")} - + +
+ + {imageStatus() === "loading" + ? i18n.t("ui.sessionReview.image.loading") + : i18n.t("ui.sessionReview.image.placeholder")} + +
+
+ +
+
+ {i18n.t("ui.sessionReview.largeDiff.title")} +
+
+ {i18n.t("ui.sessionReview.largeDiff.meta", { + limit: MAX_DIFF_CHANGED_LINES.toLocaleString(), + current: changedLines().toLocaleString(), + })} +
+
+ +
+
- - + + { + props.onDiffRendered?.() + scheduleAnchors() + }} + enableLineSelection={props.onLineComment != null} + onLineSelected={handleLineSelected} + onLineSelectionEnd={handleLineSelectionEnd} + selectedLines={selectedLines()} + commentedLines={commentedLines()} + before={{ + name: diff.file!, + contents: typeof diff.before === "string" ? diff.before : "", + }} + after={{ + name: diff.file!, + contents: typeof diff.after === "string" ? diff.after : "", + }} + />
- -
-
-
-
- -
{ - wrapper = el - anchors.set(diff.file, el) - scheduleAnchors() - }} - > - - - -
- {diff.file} -
-
- -
- - {i18n.t("ui.sessionReview.change.removed")} - -
-
- -
- - {imageStatus() === "loading" - ? i18n.t("ui.sessionReview.image.loading") - : i18n.t("ui.sessionReview.image.placeholder")} - -
-
- -
-
- {i18n.t("ui.sessionReview.largeDiff.title")} -
-
- {i18n.t("ui.sessionReview.largeDiff.meta", { - limit: MAX_DIFF_CHANGED_LINES.toLocaleString(), - current: changedLines().toLocaleString(), - })} -
-
- -
-
-
- - { - props.onDiffRendered?.() - scheduleAnchors() - }} - enableLineSelection={props.onLineComment != null} - onLineSelected={handleLineSelected} - onLineSelectionEnd={handleLineSelectionEnd} - selectedLines={selectedLines()} - commentedLines={commentedLines()} - before={{ - name: diff.file!, - contents: typeof diff.before === "string" ? diff.before : "", - }} - after={{ - name: diff.file!, - contents: typeof diff.after === "string" ? diff.after : "", - }} - /> - -
- - - {(comment) => ( - setSelection({ file: comment.file, range: comment.selection })} - onClick={() => { - if (isCommentOpen(comment)) { - setOpened(null) - return - } - - openComment(comment) - }} - open={isCommentOpen(comment)} - comment={comment.comment} - selection={selectionLabel(comment.selection)} - /> - )} - - - - {(range) => ( - - setCommenting(null)} - onSubmit={(comment) => { - props.onLineComment?.({ - file: diff.file, - selection: range(), - comment, - preview: selectionPreview(diff, range()), - }) - setCommenting(null) + + + {(comment) => ( + setSelection({ file: comment.file, range: comment.selection })} + onClick={() => { + if (isCommentOpen(comment)) { + setOpened(null) + return + } + + openComment(comment) }} + open={isCommentOpen(comment)} + comment={comment.comment} + selection={selectionLabel(comment.selection)} /> - - )} + )} + + + + {(range) => ( + + setCommenting(null)} + onSubmit={(comment) => { + props.onLineComment?.({ + file: diff.file, + selection: range(), + comment, + preview: selectionPreview(diff, range()), + }) + setCommenting(null) + }} + /> + + )} + -
-
-
-
- ) - }} - - +
+ + + ) + }} + + +
From 9c7629ce61b4525d0a773bf307e805b3a414dd34 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 23:36:05 +0000 Subject: [PATCH 232/255] Update oc-2.json --- packages/ui/src/theme/themes/oc-2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json index 8c12c33b2ea8..01ec1131a2e2 100644 --- a/packages/ui/src/theme/themes/oc-2.json +++ b/packages/ui/src/theme/themes/oc-2.json @@ -390,7 +390,7 @@ "border-selected": "var(--cobalt-dark-alpha-11)", "border-disabled": "var(--gray-dark-alpha-8)", "border-focus": "var(--gray-dark-alpha-9)", - "border-weak-base": "var(--gray-dark-alpha-4)", + "border-weak-base": "var(--gray-dark-alpha-5)", "border-weak-hover": "var(--gray-dark-alpha-7)", "border-weak-active": "var(--gray-dark-alpha-8)", "border-weak-selected": "var(--cobalt-dark-alpha-6)", From 4a8bdc3c7593f0444355edb5193744faaeeb76ed Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 23:51:25 +0000 Subject: [PATCH 233/255] tweak(ui): group edited files list styling --- packages/ui/src/components/session-review.css | 2 +- packages/ui/src/components/session-turn.css | 44 +++++- packages/ui/src/components/session-turn.tsx | 136 +++++++++--------- 3 files changed, 113 insertions(+), 69 deletions(-) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 7395da1b1af6..79c62d33445e 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -68,7 +68,7 @@ [data-slot="session-review-diffs-group"] { background-color: var(--background-stronger); border-radius: var(--radius-lg); - box-shadow: var(--shadow-xs-border-base); + border: 1px solid var(--border-weak-base); overflow: clip; [data-component="accordion"] { diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index e7da2b6f053f..9dbc1bf63a26 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -127,7 +127,49 @@ padding-top: 8px; display: flex; flex-direction: column; - gap: 12px; + } + + [data-slot="session-turn-diffs-group"] { + background-color: var(--background-stronger); + border-radius: var(--radius-lg); + border: 1px solid var(--border-weak-base); + overflow: clip; + + [data-component="accordion"] { + gap: 0; + } + + [data-component="accordion"] + [data-slot="accordion-item"] + [data-slot="accordion-header"] + [data-slot="accordion-trigger"] { + border: 0; + border-radius: 0; + box-shadow: none; + background-color: transparent; + + &:hover { + background-color: var(--surface-base-hover); + } + + &:active { + background-color: var(--surface-base-active); + } + } + + [data-component="accordion"] + [data-slot="accordion-item"] + + [data-slot="accordion-item"] + [data-slot="accordion-header"] + [data-slot="accordion-trigger"] { + border-top: 1px solid var(--border-weak-base); + } + + [data-component="accordion"] [data-slot="accordion-item"][data-expanded] [data-slot="accordion-content"] { + border: 0; + border-top: 1px solid var(--border-weak-base); + border-radius: 0; + } } [data-slot="session-turn-diff-trigger"] { diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index a418fddd90e4..191daa1e3b89 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -315,76 +315,78 @@ export function SessionTurn(
- setExpanded(Array.isArray(value) ? value : value ? [value] : [])} - > - - {(diff) => { - const active = createMemo(() => expanded().includes(diff.file)) - const [visible, setVisible] = createSignal(false) - - createEffect( - on( - active, - (value) => { - if (!value) { - setVisible(false) - return - } - - requestAnimationFrame(() => { - if (!active()) return - setVisible(true) - }) - }, - { defer: true }, - ), - ) - - return ( - - - -
- - - - {getDirectory(diff.file)} +
+ setExpanded(Array.isArray(value) ? value : value ? [value] : [])} + > + + {(diff) => { + const active = createMemo(() => expanded().includes(diff.file)) + const [visible, setVisible] = createSignal(false) + + createEffect( + on( + active, + (value) => { + if (!value) { + setVisible(false) + return + } + + requestAnimationFrame(() => { + if (!active()) return + setVisible(true) + }) + }, + { defer: true }, + ), + ) + + return ( + + + +
+ + + + {getDirectory(diff.file)} + + + + {getFilename(diff.file)} - - - {getFilename(diff.file)} - - -
- - - - - +
+ + + + + + +
+
+ + + + +
+
-
-
-
- - -
- -
-
-
-
- ) - }} -
-
+ + + + ) + }} + + +
From fd61be40788b53915f2b7f97ccefb0416327c452 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 23:56:21 +0000 Subject: [PATCH 234/255] tweak(ui): show added diff counts in review --- packages/ui/src/components/session-review.css | 6 ++++++ packages/ui/src/components/session-review.tsx | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 79c62d33445e..87957146ecaa 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -213,6 +213,12 @@ justify-content: flex-end; } + [data-slot="session-review-change-group"] { + display: inline-flex; + align-items: center; + gap: 12px; + } + [data-slot="session-review-change"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 99f38dbf37bc..9fd114cca2e5 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -549,9 +549,12 @@ export const SessionReview = (props: SessionReviewProps) => {
- - {i18n.t("ui.sessionReview.change.added")} - +
+ + {i18n.t("ui.sessionReview.change.added")} + + +
From a301051263187275afa25f62bfb4affe35776d4b Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 18 Feb 2026 23:57:59 +0000 Subject: [PATCH 235/255] tweak(ui): tighten review diff file info gap --- packages/ui/src/components/session-review.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 87957146ecaa..752a075b5782 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -157,7 +157,7 @@ flex-grow: 1; display: flex; align-items: center; - gap: 20px; + gap: 12px; min-width: 0; } From 40f00ccc1c269a31a761617d42f47330eb6ade8d Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:02:02 +0000 Subject: [PATCH 236/255] tweak(ui): use chevron icons for review diff rows --- packages/ui/src/components/session-review.css | 11 +++++++++++ packages/ui/src/components/session-review.tsx | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 752a075b5782..5d9bf494124e 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -213,6 +213,17 @@ justify-content: flex-end; } + [data-slot="session-review-diff-chevron"] { + display: inline-flex; + color: var(--icon-weaker); + transform: rotate(-90deg); + transition: transform 0.15s ease; + } + + [data-slot="accordion-item"][data-expanded] [data-slot="session-review-diff-chevron"] { + transform: rotate(0deg); + } + [data-slot="session-review-change-group"] { display: inline-flex; align-items: center; diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 9fd114cca2e5..537f43522b8e 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -570,7 +570,9 @@ export const SessionReview = (props: SessionReviewProps) => {
- + + +
From 44049540b06d1abcd5d3de17308802e96614cb7f Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:15:14 +0000 Subject: [PATCH 237/255] tweak(ui): add open-file tooltip icon --- packages/ui/src/components/icon.tsx | 1 + packages/ui/src/components/session-review.css | 3 +++ packages/ui/src/components/session-review.tsx | 24 +++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index d131770db9d5..6486da851907 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -50,6 +50,7 @@ const icons = { "layout-right-partial": ``, "layout-right-full": ``, "square-arrow-top-right": ``, + "open-file": ``, "speech-bubble": ``, comment: ``, "folder-add-left": ``, diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 5d9bf494124e..bef8f4f0ec3c 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -163,6 +163,7 @@ [data-slot="session-review-file-name-container"] { display: flex; + align-items: center; flex-grow: 1; min-width: 0; } @@ -193,6 +194,8 @@ cursor: pointer; border-radius: 4px; opacity: 0; + will-change: opacity; + transform: translateZ(0); transition: opacity 0.15s ease; &:hover { diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 537f43522b8e..815d8129da92 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -6,6 +6,7 @@ import { FileIcon } from "./file-icon" import { Icon } from "./icon" import { LineComment, LineCommentEditor } from "./line-comment" import { StickyAccordionHeader } from "./sticky-accordion-header" +import { Tooltip } from "./tooltip" import { useDiffComponent } from "../context/diff" import { useI18n } from "../context/i18n" import { getDirectory, getFilename } from "@opencode-ai/util/path" @@ -533,16 +534,19 @@ export const SessionReview = (props: SessionReviewProps) => {
{getFilename(diff.file)} - + + +
From 3d0f24067c14bb8b4815c45ebd22f3f34c87a446 Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:20:18 +0000 Subject: [PATCH 238/255] tweak(app): tighten prompt dock padding --- packages/app/src/pages/session/session-prompt-dock.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app/src/pages/session/session-prompt-dock.tsx b/packages/app/src/pages/session/session-prompt-dock.tsx index abe12bcb00ba..0e0d06071b81 100644 --- a/packages/app/src/pages/session/session-prompt-dock.tsx +++ b/packages/app/src/pages/session/session-prompt-dock.tsx @@ -174,11 +174,11 @@ export function SessionPromptDock(props: {
From 5d8664c13eae3328eddf3177028e6d332dbc865c Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:25:06 +0000 Subject: [PATCH 239/255] tweak(app): adjust session turn horizontal padding --- packages/app/src/pages/session/message-timeline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index c65e2600e8f3..a7db4e83eb0e 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -535,7 +535,7 @@ export function MessageTimeline(props: { classes={{ root: "min-w-0 w-full relative", content: "flex flex-col justify-between !overflow-visible", - container: "w-full px-4 md:px-6", + container: "w-full px-4 md:px-5", }} />
From 6042785c57d9488568da0cda5267510d969b1316 Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:28:22 +0000 Subject: [PATCH 240/255] tweak(ui): rtl-truncate edited file paths --- packages/ui/src/components/session-turn.css | 12 +++++++++--- packages/ui/src/components/session-turn.tsx | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 9dbc1bf63a26..f952f6aad2f0 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -182,12 +182,11 @@ } [data-slot="session-turn-diff-path"] { - display: inline-flex; + display: flex; min-width: 0; align-items: baseline; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + font-family: var(--font-family-sans); font-size: var(--font-size-small); line-height: var(--line-height-large); @@ -195,6 +194,13 @@ [data-slot="session-turn-diff-directory"] { color: var(--text-weak); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + direction: rtl; + unicode-bidi: plaintext; + text-align: left; } [data-slot="session-turn-diff-filename"] { diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 191daa1e3b89..e0f934cd5e02 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -352,7 +352,7 @@ export function SessionTurn( - {getDirectory(diff.file)} + {`\u202A${getDirectory(diff.file)}\u202C`} From 802ccd37888b355dcd779be48b4994efc92168fa Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 19 Feb 2026 00:35:12 +0000 Subject: [PATCH 241/255] tweak(ui): rotate collapsible chevron icon --- packages/ui/src/components/collapsible.css | 15 +++++---------- packages/ui/src/components/collapsible.tsx | 5 +---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/ui/src/components/collapsible.css b/packages/ui/src/components/collapsible.css index 88f37ea7ffa3..6408cfb5efb1 100644 --- a/packages/ui/src/components/collapsible.css +++ b/packages/ui/src/components/collapsible.css @@ -29,11 +29,10 @@ } [data-slot="collapsible-arrow-icon"] { - display: none; - } - - [data-slot="collapsible-arrow-icon"][data-direction="right"] { display: inline-flex; + color: var(--icon-weaker); + transform: rotate(-90deg); + transition: transform 0.15s ease; } &:hover [data-slot="collapsible-arrow"] { @@ -74,12 +73,8 @@ opacity: 1; } - [data-slot="collapsible-arrow-icon"][data-direction="right"] { - display: none; - } - - [data-slot="collapsible-arrow-icon"][data-direction="down"] { - display: inline-flex; + [data-slot="collapsible-arrow-icon"] { + transform: rotate(0deg); } } diff --git a/packages/ui/src/components/collapsible.tsx b/packages/ui/src/components/collapsible.tsx index 548088287871..8b5cd825cecc 100644 --- a/packages/ui/src/components/collapsible.tsx +++ b/packages/ui/src/components/collapsible.tsx @@ -34,10 +34,7 @@ function CollapsibleContent(props: ComponentProps) { function CollapsibleArrow(props?: ComponentProps<"div">) { return (
- - - - +
From 3a07dd8d96e3e4cbc6787ae14add19b2d58023be Mon Sep 17 00:00:00 2001 From: Dax Date: Wed, 18 Feb 2026 19:37:10 -0500 Subject: [PATCH 242/255] refactor: migrate src/project/project.ts from Bun.file() to Filesystem/stat modules (#14126) --- packages/opencode/src/project/project.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 8fa0f6c6f00d..63c1c4cad3e4 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -86,8 +86,7 @@ export namespace Project { const gitBinary = Bun.which("git") // cached id calculation - let id = await Bun.file(path.join(dotgit, "opencode")) - .text() + let id = await Filesystem.readText(path.join(dotgit, "opencode")) .then((x) => x.trim()) .catch(() => undefined) @@ -125,9 +124,7 @@ export namespace Project { id = roots[0] if (id) { - void Bun.file(path.join(dotgit, "opencode")) - .write(id) - .catch(() => undefined) + void Filesystem.write(path.join(dotgit, "opencode"), id).catch(() => undefined) } } @@ -277,10 +274,9 @@ export namespace Project { ) const shortest = matches.sort((a, b) => a.length - b.length)[0] if (!shortest) return - const file = Bun.file(shortest) - const buffer = await file.arrayBuffer() - const base64 = Buffer.from(buffer).toString("base64") - const mime = file.type || "image/png" + const buffer = await Filesystem.readBytes(shortest) + const base64 = buffer.toString("base64") + const mime = Filesystem.mimeType(shortest) || "image/png" const url = `data:${mime};base64,${base64}` await update({ projectID: input.id, @@ -381,10 +377,8 @@ export namespace Project { const data = fromRow(row) const valid: string[] = [] for (const dir of data.sandboxes) { - const stat = await Bun.file(dir) - .stat() - .catch(() => undefined) - if (stat?.isDirectory()) valid.push(dir) + const s = Filesystem.stat(dir) + if (s?.isDirectory()) valid.push(dir) } return valid } From 568eccb4c654e83382253eb0c1478d24585288aa Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 18 Feb 2026 19:41:14 -0500 Subject: [PATCH 243/255] Revert: all refactor commits migrating from Bun.file() to Filesystem module --- .opencode/skill/bun-file-io/SKILL.md | 42 ++++++ packages/opencode/src/cli/cmd/tui/thread.ts | 4 +- packages/opencode/src/lsp/client.ts | 3 +- packages/opencode/src/lsp/server.ts | 95 +++++++------- packages/opencode/src/mcp/auth.ts | 10 +- packages/opencode/src/project/project.ts | 20 ++- packages/opencode/src/provider/models.ts | 7 +- packages/opencode/src/provider/provider.ts | 6 +- packages/opencode/src/session/instruction.ts | 12 +- packages/opencode/src/session/prompt.ts | 16 ++- packages/opencode/src/shell/shell.ts | 3 +- packages/opencode/src/skill/discovery.ts | 7 +- packages/opencode/src/storage/db.ts | 4 +- .../opencode/src/storage/json-migration.ts | 3 +- packages/opencode/src/storage/storage.ts | 68 +++++----- packages/opencode/src/tool/edit.ts | 13 +- packages/opencode/src/tool/glob.ts | 6 +- packages/opencode/src/tool/grep.ts | 4 +- packages/opencode/src/tool/lsp.ts | 3 +- packages/opencode/src/tool/read.ts | 15 ++- packages/opencode/src/tool/truncation.ts | 3 +- packages/opencode/src/tool/write.ts | 7 +- packages/opencode/src/util/filesystem.ts | 25 +--- packages/opencode/src/util/log.ts | 13 +- .../opencode/test/util/filesystem.test.ts | 121 ------------------ 25 files changed, 216 insertions(+), 294 deletions(-) create mode 100644 .opencode/skill/bun-file-io/SKILL.md diff --git a/.opencode/skill/bun-file-io/SKILL.md b/.opencode/skill/bun-file-io/SKILL.md new file mode 100644 index 000000000000..f78de330943e --- /dev/null +++ b/.opencode/skill/bun-file-io/SKILL.md @@ -0,0 +1,42 @@ +--- +name: bun-file-io +description: Use this when you are working on file operations like reading, writing, scanning, or deleting files. It summarizes the preferred file APIs and patterns used in this repo. It also notes when to use filesystem helpers for directories. +--- + +## Use this when + +- Editing file I/O or scans in `packages/opencode` +- Handling directory operations or external tools + +## Bun file APIs (from Bun docs) + +- `Bun.file(path)` is lazy; call `text`, `json`, `stream`, `arrayBuffer`, `bytes`, `exists` to read. +- Metadata: `file.size`, `file.type`, `file.name`. +- `Bun.write(dest, input)` writes strings, buffers, Blobs, Responses, or files. +- `Bun.file(...).delete()` deletes a file. +- `file.writer()` returns a FileSink for incremental writes. +- `Bun.Glob` + `Array.fromAsync(glob.scan({ cwd, absolute, onlyFiles, dot }))` for scans. +- Use `Bun.which` to find a binary, then `Bun.spawn` to run it. +- `Bun.readableStreamToText/Bytes/JSON` for stream output. + +## When to use node:fs + +- Use `node:fs/promises` for directories (`mkdir`, `readdir`, recursive operations). + +## Repo patterns + +- Prefer Bun APIs over Node `fs` for file access. +- Check `Bun.file(...).exists()` before reading. +- For binary/large files use `arrayBuffer()` and MIME checks via `file.type`. +- Use `Bun.Glob` + `Array.fromAsync` for scans. +- Decode tool stderr with `Bun.readableStreamToText`. +- For large writes, use `Bun.write(Bun.file(path), text)`. + +NOTE: Bun.file(...).exists() will return `false` if the value is a directory. +Use Filesystem.exists(...) instead if path can be file or directory + +## Quick checklist + +- Use Bun APIs first. +- Use `path.join`/`path.resolve` for paths. +- Prefer promise `.catch(...)` over `try/catch` when possible. diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 50f63c3dfbd1..9eb296032732 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -3,12 +3,10 @@ import { tui } from "./app" import { Rpc } from "@/util/rpc" import { type rpc } from "./worker" import path from "path" -import { fileURLToPath } from "url" import { UI } from "@/cli/ui" import { iife } from "@/util/iife" import { Log } from "@/util/log" import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network" -import { Filesystem } from "@/util/filesystem" import type { Event } from "@opencode-ai/sdk/v2" import type { EventSource } from "./context/sdk" import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32" @@ -101,7 +99,7 @@ export const TuiThreadCommand = cmd({ const distWorker = new URL("./cli/cmd/tui/worker.js", import.meta.url) const workerPath = await iife(async () => { if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH - if (await Filesystem.exists(fileURLToPath(distWorker))) return distWorker + if (await Bun.file(distWorker).exists()) return distWorker return localWorker }) try { diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 084ccf831eec..8704b65acb5b 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -147,7 +147,8 @@ export namespace LSPClient { notify: { async open(input: { path: string }) { input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path) - const text = await Filesystem.readText(input.path) + const file = Bun.file(input.path) + const text = await file.text() const extension = path.extname(input.path) const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext" diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index a4ebeb5a2567..0200be2260c7 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -131,7 +131,7 @@ export namespace LSPServer { "bin", "vue-language-server.js", ) - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "@vue/language-server"], { cwd: Global.Path.bin, @@ -173,14 +173,14 @@ export namespace LSPServer { if (!eslint) return log.info("spawning eslint server") const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js") - if (!(await Filesystem.exists(serverPath))) { + if (!(await Bun.file(serverPath).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return log.info("downloading and building VS Code ESLint server") const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip") if (!response.ok) return const zipPath = path.join(Global.Path.bin, "vscode-eslint.zip") - if (response.body) await Filesystem.writeStream(zipPath, response.body) + await Bun.file(zipPath).write(response) const ok = await Archive.extractZip(zipPath, Global.Path.bin) .then(() => true) @@ -242,7 +242,7 @@ export namespace LSPServer { const resolveBin = async (target: string) => { const localBin = path.join(root, target) - if (await Filesystem.exists(localBin)) return localBin + if (await Bun.file(localBin).exists()) return localBin const candidates = Filesystem.up({ targets: [target], @@ -326,7 +326,7 @@ export namespace LSPServer { async spawn(root) { const localBin = path.join(root, "node_modules", ".bin", "biome") let bin: string | undefined - if (await Filesystem.exists(localBin)) bin = localBin + if (await Bun.file(localBin).exists()) bin = localBin if (!bin) { const found = Bun.which("biome") if (found) bin = found @@ -467,7 +467,7 @@ export namespace LSPServer { const potentialPythonPath = isWindows ? path.join(venvPath, "Scripts", "python.exe") : path.join(venvPath, "bin", "python") - if (await Filesystem.exists(potentialPythonPath)) { + if (await Bun.file(potentialPythonPath).exists()) { initialization["pythonPath"] = potentialPythonPath break } @@ -479,7 +479,7 @@ export namespace LSPServer { const potentialTyPath = isWindows ? path.join(venvPath, "Scripts", "ty.exe") : path.join(venvPath, "bin", "ty") - if (await Filesystem.exists(potentialTyPath)) { + if (await Bun.file(potentialTyPath).exists()) { binary = potentialTyPath break } @@ -511,7 +511,7 @@ export namespace LSPServer { const args = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "pyright", "dist", "pyright-langserver.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "pyright"], { cwd: Global.Path.bin, @@ -536,7 +536,7 @@ export namespace LSPServer { const potentialPythonPath = isWindows ? path.join(venvPath, "Scripts", "python.exe") : path.join(venvPath, "bin", "python") - if (await Filesystem.exists(potentialPythonPath)) { + if (await Bun.file(potentialPythonPath).exists()) { initialization["pythonPath"] = potentialPythonPath break } @@ -571,7 +571,7 @@ export namespace LSPServer { process.platform === "win32" ? "language_server.bat" : "language_server.sh", ) - if (!(await Filesystem.exists(binary))) { + if (!(await Bun.file(binary).exists())) { const elixir = Bun.which("elixir") if (!elixir) { log.error("elixir is required to run elixir-ls") @@ -584,7 +584,7 @@ export namespace LSPServer { const response = await fetch("https://github.com/elixir-lsp/elixir-ls/archive/refs/heads/master.zip") if (!response.ok) return const zipPath = path.join(Global.Path.bin, "elixir-ls.zip") - if (response.body) await Filesystem.writeStream(zipPath, response.body) + await Bun.file(zipPath).write(response) const ok = await Archive.extractZip(zipPath, Global.Path.bin) .then(() => true) @@ -692,7 +692,7 @@ export namespace LSPServer { } const tempPath = path.join(Global.Path.bin, assetName) - if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body) + await Bun.file(tempPath).write(downloadResponse) if (ext === "zip") { const ok = await Archive.extractZip(tempPath, Global.Path.bin) @@ -710,7 +710,7 @@ export namespace LSPServer { bin = path.join(Global.Path.bin, "zls" + (platform === "win32" ? ".exe" : "")) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract zls binary") return } @@ -857,7 +857,7 @@ export namespace LSPServer { // Stop at filesystem root const cargoTomlPath = path.join(currentDir, "Cargo.toml") try { - const cargoTomlContent = await Filesystem.readText(cargoTomlPath) + const cargoTomlContent = await Bun.file(cargoTomlPath).text() if (cargoTomlContent.includes("[workspace]")) { return currentDir } @@ -907,7 +907,7 @@ export namespace LSPServer { const ext = process.platform === "win32" ? ".exe" : "" const direct = path.join(Global.Path.bin, "clangd" + ext) - if (await Filesystem.exists(direct)) { + if (await Bun.file(direct).exists()) { return { process: spawn(direct, args, { cwd: root, @@ -920,7 +920,7 @@ export namespace LSPServer { if (!entry.isDirectory()) continue if (!entry.name.startsWith("clangd_")) continue const candidate = path.join(Global.Path.bin, entry.name, "bin", "clangd" + ext) - if (await Filesystem.exists(candidate)) { + if (await Bun.file(candidate).exists()) { return { process: spawn(candidate, args, { cwd: root, @@ -990,7 +990,7 @@ export namespace LSPServer { log.error("Failed to write clangd archive") return } - await Filesystem.write(archive, Buffer.from(buf)) + await Bun.write(archive, buf) const zip = name.endsWith(".zip") const tar = name.endsWith(".tar.xz") @@ -1014,7 +1014,7 @@ export namespace LSPServer { await fs.rm(archive, { force: true }) const bin = path.join(Global.Path.bin, "clangd_" + tag, "bin", "clangd" + ext) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract clangd binary") return } @@ -1045,7 +1045,7 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "svelte-language-server", "bin", "server.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "svelte-language-server"], { cwd: Global.Path.bin, @@ -1092,7 +1092,7 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "@astrojs", "language-server", "bin", "nodeServer.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "@astrojs/language-server"], { cwd: Global.Path.bin, @@ -1248,7 +1248,7 @@ export namespace LSPServer { const distPath = path.join(Global.Path.bin, "kotlin-ls") const launcherScript = process.platform === "win32" ? path.join(distPath, "kotlin-lsp.cmd") : path.join(distPath, "kotlin-lsp.sh") - const installed = await Filesystem.exists(launcherScript) + const installed = await Bun.file(launcherScript).exists() if (!installed) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return log.info("Downloading Kotlin Language Server from GitHub.") @@ -1307,7 +1307,7 @@ export namespace LSPServer { } log.info("Installed Kotlin Language Server", { path: launcherScript }) } - if (!(await Filesystem.exists(launcherScript))) { + if (!(await Bun.file(launcherScript).exists())) { log.error(`Failed to locate the Kotlin LS launcher script in the installed directory: ${distPath}.`) return } @@ -1336,7 +1336,7 @@ export namespace LSPServer { "src", "server.js", ) - const exists = await Filesystem.exists(js) + const exists = await Bun.file(js).exists() if (!exists) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "yaml-language-server"], { @@ -1443,7 +1443,7 @@ export namespace LSPServer { } const tempPath = path.join(Global.Path.bin, assetName) - if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body) + await Bun.file(tempPath).write(downloadResponse) // Unlike zls which is a single self-contained binary, // lua-language-server needs supporting files (meta/, locale/, etc.) @@ -1482,7 +1482,7 @@ export namespace LSPServer { // Binary is located in bin/ subdirectory within the extracted archive bin = path.join(installDir, "bin", "lua-language-server" + (platform === "win32" ? ".exe" : "")) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract lua-language-server binary") return } @@ -1516,7 +1516,7 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "intelephense", "lib", "intelephense.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "intelephense"], { cwd: Global.Path.bin, @@ -1613,7 +1613,7 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "bash-language-server", "out", "cli.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "bash-language-server"], { cwd: Global.Path.bin, @@ -1654,17 +1654,22 @@ export namespace LSPServer { if (!bin) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - log.info("downloading terraform-ls from HashiCorp releases") + log.info("downloading terraform-ls from GitHub releases") - const releaseResponse = await fetch("https://api.releases.hashicorp.com/v1/releases/terraform-ls/latest") + const releaseResponse = await fetch("https://api.github.com/repos/hashicorp/terraform-ls/releases/latest") if (!releaseResponse.ok) { log.error("Failed to fetch terraform-ls release info") return } const release = (await releaseResponse.json()) as { - version?: string - builds?: { arch?: string; os?: string; url?: string }[] + tag_name?: string + assets?: { name?: string; browser_download_url?: string }[] + } + const version = release.tag_name?.replace("v", "") + if (!version) { + log.error("terraform-ls release did not include a version tag") + return } const platform = process.platform @@ -1673,21 +1678,23 @@ export namespace LSPServer { const tfArch = arch === "arm64" ? "arm64" : "amd64" const tfPlatform = platform === "win32" ? "windows" : platform - const builds = release.builds ?? [] - const build = builds.find((b) => b.arch === tfArch && b.os === tfPlatform) - if (!build?.url) { - log.error(`Could not find build for ${tfPlatform}/${tfArch} terraform-ls release version ${release.version}`) + const assetName = `terraform-ls_${version}_${tfPlatform}_${tfArch}.zip` + + const assets = release.assets ?? [] + const asset = assets.find((a) => a.name === assetName) + if (!asset?.browser_download_url) { + log.error(`Could not find asset ${assetName} in terraform-ls release`) return } - const downloadResponse = await fetch(build.url) + const downloadResponse = await fetch(asset.browser_download_url) if (!downloadResponse.ok) { log.error("Failed to download terraform-ls") return } - const tempPath = path.join(Global.Path.bin, "terraform-ls.zip") - if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body) + const tempPath = path.join(Global.Path.bin, assetName) + await Bun.file(tempPath).write(downloadResponse) const ok = await Archive.extractZip(tempPath, Global.Path.bin) .then(() => true) @@ -1700,7 +1707,7 @@ export namespace LSPServer { bin = path.join(Global.Path.bin, "terraform-ls" + (platform === "win32" ? ".exe" : "")) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract terraform-ls binary") return } @@ -1777,7 +1784,7 @@ export namespace LSPServer { } const tempPath = path.join(Global.Path.bin, assetName) - if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body) + await Bun.file(tempPath).write(downloadResponse) if (ext === "zip") { const ok = await Archive.extractZip(tempPath, Global.Path.bin) @@ -1796,7 +1803,7 @@ export namespace LSPServer { bin = path.join(Global.Path.bin, "texlab" + (platform === "win32" ? ".exe" : "")) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract texlab binary") return } @@ -1825,7 +1832,7 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { const js = path.join(Global.Path.bin, "node_modules", "dockerfile-language-server-nodejs", "lib", "server.js") - if (!(await Filesystem.exists(js))) { + if (!(await Bun.file(js).exists())) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return await Bun.spawn([BunProc.which(), "install", "dockerfile-language-server-nodejs"], { cwd: Global.Path.bin, @@ -1983,7 +1990,7 @@ export namespace LSPServer { } const tempPath = path.join(Global.Path.bin, assetName) - if (downloadResponse.body) await Filesystem.writeStream(tempPath, downloadResponse.body) + await Bun.file(tempPath).write(downloadResponse) if (ext === "zip") { const ok = await Archive.extractZip(tempPath, Global.Path.bin) @@ -2001,7 +2008,7 @@ export namespace LSPServer { bin = path.join(Global.Path.bin, "tinymist" + (platform === "win32" ? ".exe" : "")) - if (!(await Filesystem.exists(bin))) { + if (!(await Bun.file(bin).exists())) { log.error("Failed to extract tinymist binary") return } diff --git a/packages/opencode/src/mcp/auth.ts b/packages/opencode/src/mcp/auth.ts index 399986376d12..0f91a35b8754 100644 --- a/packages/opencode/src/mcp/auth.ts +++ b/packages/opencode/src/mcp/auth.ts @@ -1,7 +1,6 @@ import path from "path" import z from "zod" import { Global } from "../global" -import { Filesystem } from "../util/filesystem" export namespace McpAuth { export const Tokens = z.object({ @@ -54,22 +53,25 @@ export namespace McpAuth { } export async function all(): Promise> { - return Filesystem.readJson>(filepath).catch(() => ({})) + const file = Bun.file(filepath) + return file.json().catch(() => ({})) } export async function set(mcpName: string, entry: Entry, serverUrl?: string): Promise { + const file = Bun.file(filepath) const data = await all() // Always update serverUrl if provided if (serverUrl) { entry.serverUrl = serverUrl } - await Filesystem.writeJson(filepath, { ...data, [mcpName]: entry }, 0o600) + await Bun.write(file, JSON.stringify({ ...data, [mcpName]: entry }, null, 2), { mode: 0o600 }) } export async function remove(mcpName: string): Promise { + const file = Bun.file(filepath) const data = await all() delete data[mcpName] - await Filesystem.writeJson(filepath, data, 0o600) + await Bun.write(file, JSON.stringify(data, null, 2), { mode: 0o600 }) } export async function updateTokens(mcpName: string, tokens: Tokens, serverUrl?: string): Promise { diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 63c1c4cad3e4..8fa0f6c6f00d 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -86,7 +86,8 @@ export namespace Project { const gitBinary = Bun.which("git") // cached id calculation - let id = await Filesystem.readText(path.join(dotgit, "opencode")) + let id = await Bun.file(path.join(dotgit, "opencode")) + .text() .then((x) => x.trim()) .catch(() => undefined) @@ -124,7 +125,9 @@ export namespace Project { id = roots[0] if (id) { - void Filesystem.write(path.join(dotgit, "opencode"), id).catch(() => undefined) + void Bun.file(path.join(dotgit, "opencode")) + .write(id) + .catch(() => undefined) } } @@ -274,9 +277,10 @@ export namespace Project { ) const shortest = matches.sort((a, b) => a.length - b.length)[0] if (!shortest) return - const buffer = await Filesystem.readBytes(shortest) - const base64 = buffer.toString("base64") - const mime = Filesystem.mimeType(shortest) || "image/png" + const file = Bun.file(shortest) + const buffer = await file.arrayBuffer() + const base64 = Buffer.from(buffer).toString("base64") + const mime = file.type || "image/png" const url = `data:${mime};base64,${base64}` await update({ projectID: input.id, @@ -377,8 +381,10 @@ export namespace Project { const data = fromRow(row) const valid: string[] = [] for (const dir of data.sandboxes) { - const s = Filesystem.stat(dir) - if (s?.isDirectory()) valid.push(dir) + const stat = await Bun.file(dir) + .stat() + .catch(() => undefined) + if (stat?.isDirectory()) valid.push(dir) } return valid } diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index bae33178467e..0960176e2022 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -5,7 +5,6 @@ import z from "zod" import { Installation } from "../installation" import { Flag } from "../flag/flag" import { lazy } from "@/util/lazy" -import { Filesystem } from "../util/filesystem" // Try to import bundled snapshot (generated at build time) // Falls back to undefined in dev mode when snapshot doesn't exist @@ -86,7 +85,8 @@ export namespace ModelsDev { } export const Data = lazy(async () => { - const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {}) + const file = Bun.file(Flag.OPENCODE_MODELS_PATH ?? filepath) + const result = await file.json().catch(() => {}) if (result) return result // @ts-ignore const snapshot = await import("./models-snapshot") @@ -104,6 +104,7 @@ export namespace ModelsDev { } export async function refresh() { + const file = Bun.file(filepath) const result = await fetch(`${url()}/api.json`, { headers: { "User-Agent": Installation.USER_AGENT, @@ -115,7 +116,7 @@ export namespace ModelsDev { }) }) if (result && result.ok) { - await Filesystem.write(filepath, await result.text()) + await Bun.write(file, await result.text()) ModelsDev.Data.reset() } } diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 6480625e9f1e..d94d0cbb2239 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -16,7 +16,6 @@ import { Flag } from "../flag/flag" import { iife } from "@/util/iife" import { Global } from "../global" import path from "path" -import { Filesystem } from "../util/filesystem" // Direct imports for bundled providers import { createAmazonBedrock, type AmazonBedrockProviderSettings } from "@ai-sdk/amazon-bedrock" @@ -1292,9 +1291,8 @@ export namespace Provider { if (cfg.model) return parseModel(cfg.model) const providers = await list() - const recent = (await Filesystem.readJson<{ recent?: { providerID: string; modelID: string }[] }>( - path.join(Global.Path.state, "model.json"), - ) + const recent = (await Bun.file(path.join(Global.Path.state, "model.json")) + .json() .then((x) => (Array.isArray(x.recent) ? x.recent : [])) .catch(() => [])) as { providerID: string; modelID: string }[] for (const entry of recent) { diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts index d65ada278570..6fb2a7aeb57f 100644 --- a/packages/opencode/src/session/instruction.ts +++ b/packages/opencode/src/session/instruction.ts @@ -85,7 +85,7 @@ export namespace InstructionPrompt { } for (const file of globalFiles()) { - if (await Filesystem.exists(file)) { + if (await Bun.file(file).exists()) { paths.add(path.resolve(file)) break } @@ -120,7 +120,9 @@ export namespace InstructionPrompt { const paths = await systemPaths() const files = Array.from(paths).map(async (p) => { - const content = await Filesystem.readText(p).catch(() => "") + const content = await Bun.file(p) + .text() + .catch(() => "") return content ? "Instructions from: " + p + "\n" + content : "" }) @@ -162,7 +164,7 @@ export namespace InstructionPrompt { export async function find(dir: string) { for (const file of FILES) { const filepath = path.resolve(path.join(dir, file)) - if (await Filesystem.exists(filepath)) return filepath + if (await Bun.file(filepath).exists()) return filepath } } @@ -180,7 +182,9 @@ export namespace InstructionPrompt { if (found && found !== target && !system.has(found) && !already.has(found) && !isClaimed(messageID, found)) { claim(messageID, found) - const content = await Filesystem.readText(found).catch(() => undefined) + const content = await Bun.file(found) + .text() + .catch(() => undefined) if (content) { results.push({ filepath: found, content: "Instructions from: " + found + "\n" + content }) } diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 6ca93979e306..d1f4072586e4 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -2,7 +2,6 @@ import path from "path" import os from "os" import fs from "fs/promises" import z from "zod" -import { Filesystem } from "../util/filesystem" import { Identifier } from "../id/id" import { MessageV2 } from "./message-v2" import { Log } from "../util/log" @@ -1083,9 +1082,11 @@ export namespace SessionPrompt { // have to normalize, symbol search returns absolute paths // Decode the pathname since URL constructor doesn't automatically decode it const filepath = fileURLToPath(part.url) - const s = Filesystem.stat(filepath) + const stat = await Bun.file(filepath) + .stat() + .catch(() => undefined) - if (s?.isDirectory()) { + if (stat?.isDirectory()) { part.mime = "application/x-directory" } @@ -1232,13 +1233,14 @@ export namespace SessionPrompt { ] } + const file = Bun.file(filepath) FileTime.read(input.sessionID, filepath) return [ { messageID: info.id, sessionID: input.sessionID, type: "text", - text: `Called the Read tool with the following input: {"filePath":"${filepath}"}`, + text: `Called the Read tool with the following input: {\"filePath\":\"${filepath}\"}`, synthetic: true, }, { @@ -1246,7 +1248,7 @@ export namespace SessionPrompt { messageID: info.id, sessionID: input.sessionID, type: "file", - url: `data:${part.mime};base64,` + (await Filesystem.readBytes(filepath)).toString("base64"), + url: `data:${part.mime};base64,` + Buffer.from(await file.bytes()).toString("base64"), mime: part.mime, filename: part.filename!, source: part.source, @@ -1352,7 +1354,7 @@ export namespace SessionPrompt { // Switching from plan mode to build mode if (input.agent.name !== "plan" && assistantMessage?.info.agent === "plan") { const plan = Session.plan(input.session) - const exists = await Filesystem.exists(plan) + const exists = await Bun.file(plan).exists() if (exists) { const part = await Session.updatePart({ id: Identifier.ascending("part"), @@ -1371,7 +1373,7 @@ export namespace SessionPrompt { // Entering plan mode if (input.agent.name === "plan" && assistantMessage?.info.agent !== "plan") { const plan = Session.plan(input.session) - const exists = await Filesystem.exists(plan) + const exists = await Bun.file(plan).exists() if (!exists) await fs.mkdir(path.dirname(plan), { recursive: true }) const part = await Session.updatePart({ id: Identifier.ascending("part"), diff --git a/packages/opencode/src/shell/shell.ts b/packages/opencode/src/shell/shell.ts index e7b7cdb3e4da..2e8d48bfd921 100644 --- a/packages/opencode/src/shell/shell.ts +++ b/packages/opencode/src/shell/shell.ts @@ -1,6 +1,5 @@ import { Flag } from "@/flag/flag" import { lazy } from "@/util/lazy" -import { Filesystem } from "@/util/filesystem" import path from "path" import { spawn, type ChildProcess } from "child_process" @@ -44,7 +43,7 @@ export namespace Shell { // git.exe is typically at: C:\Program Files\Git\cmd\git.exe // bash.exe is at: C:\Program Files\Git\bin\bash.exe const bash = path.join(git, "..", "..", "bin", "bash.exe") - if (Filesystem.stat(bash)?.size) return bash + if (Bun.file(bash).size) return bash } return process.env.COMSPEC || "cmd.exe" } diff --git a/packages/opencode/src/skill/discovery.ts b/packages/opencode/src/skill/discovery.ts index 846002cdaee2..a4bf97d7a1b4 100644 --- a/packages/opencode/src/skill/discovery.ts +++ b/packages/opencode/src/skill/discovery.ts @@ -2,7 +2,6 @@ import path from "path" import { mkdir } from "fs/promises" import { Log } from "../util/log" import { Global } from "../global" -import { Filesystem } from "../util/filesystem" export namespace Discovery { const log = Log.create({ service: "skill-discovery" }) @@ -20,14 +19,14 @@ export namespace Discovery { } async function get(url: string, dest: string): Promise { - if (await Filesystem.exists(dest)) return true + if (await Bun.file(dest).exists()) return true return fetch(url) .then(async (response) => { if (!response.ok) { log.error("failed to download", { url, status: response.status }) return false } - if (response.body) await Filesystem.writeStream(dest, response.body) + await Bun.write(dest, await response.text()) return true }) .catch((err) => { @@ -89,7 +88,7 @@ export namespace Discovery { ) const md = path.join(root, "SKILL.md") - if (await Filesystem.exists(md)) result.push(root) + if (await Bun.file(md).exists()) result.push(root) }), ) diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts index 6d7bfd728102..0974cbe7be44 100644 --- a/packages/opencode/src/storage/db.ts +++ b/packages/opencode/src/storage/db.ts @@ -10,7 +10,7 @@ import { Log } from "../util/log" import { NamedError } from "@opencode-ai/util/error" import z from "zod" import path from "path" -import { readFileSync, readdirSync, existsSync } from "fs" +import { readFileSync, readdirSync } from "fs" import * as schema from "./schema" declare const OPENCODE_MIGRATIONS: { sql: string; timestamp: number }[] | undefined @@ -54,7 +54,7 @@ export namespace Database { const sql = dirs .map((name) => { const file = path.join(dir, name, "migration.sql") - if (!existsSync(file)) return + if (!Bun.file(file).size) return return { sql: readFileSync(file, "utf-8"), timestamp: time(name), diff --git a/packages/opencode/src/storage/json-migration.ts b/packages/opencode/src/storage/json-migration.ts index 268442dcf6a4..e0684ce3c199 100644 --- a/packages/opencode/src/storage/json-migration.ts +++ b/packages/opencode/src/storage/json-migration.ts @@ -7,7 +7,6 @@ import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } fro import { SessionShareTable } from "../share/share.sql" import path from "path" import { existsSync } from "fs" -import { Filesystem } from "../util/filesystem" export namespace JsonMigration { const log = Log.create({ service: "json-migration" }) @@ -83,7 +82,7 @@ export namespace JsonMigration { const count = end - start const tasks = new Array(count) for (let i = 0; i < count; i++) { - tasks[i] = Filesystem.readJson(files[start + i]) + tasks[i] = Bun.file(files[start + i]).json() } const results = await Promise.allSettled(tasks) const items = new Array(count) diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index f5459ee498d7..18f2d67e7ac0 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -39,7 +39,7 @@ export namespace Storage { cwd: path.join(project, projectDir), absolute: true, })) { - const json = await Filesystem.readJson(msgFile) + const json = await Bun.file(msgFile).json() worktree = json.path?.root if (worktree) break } @@ -60,15 +60,18 @@ export namespace Storage { if (!id) continue projectID = id - await Filesystem.writeJson(path.join(dir, "project", projectID + ".json"), { - id, - vcs: "git", - worktree, - time: { - created: Date.now(), - initialized: Date.now(), - }, - }) + await Bun.write( + path.join(dir, "project", projectID + ".json"), + JSON.stringify({ + id, + vcs: "git", + worktree, + time: { + created: Date.now(), + initialized: Date.now(), + }, + }), + ) log.info(`migrating sessions for project ${projectID}`) for await (const sessionFile of new Bun.Glob("storage/session/info/*.json").scan({ @@ -80,8 +83,8 @@ export namespace Storage { sessionFile, dest, }) - const session = await Filesystem.readJson(sessionFile) - await Filesystem.writeJson(dest, session) + const session = await Bun.file(sessionFile).json() + await Bun.write(dest, JSON.stringify(session)) log.info(`migrating messages for session ${session.id}`) for await (const msgFile of new Bun.Glob(`storage/session/message/${session.id}/*.json`).scan({ cwd: fullProjectDir, @@ -92,8 +95,8 @@ export namespace Storage { msgFile, dest, }) - const message = await Filesystem.readJson(msgFile) - await Filesystem.writeJson(dest, message) + const message = await Bun.file(msgFile).json() + await Bun.write(dest, JSON.stringify(message)) log.info(`migrating parts for message ${message.id}`) for await (const partFile of new Bun.Glob(`storage/session/part/${session.id}/${message.id}/*.json`).scan( @@ -120,32 +123,35 @@ export namespace Storage { cwd: dir, absolute: true, })) { - const session = await Filesystem.readJson(item) + const session = await Bun.file(item).json() if (!session.projectID) continue if (!session.summary?.diffs) continue const { diffs } = session.summary - await Filesystem.write(path.join(dir, "session_diff", session.id + ".json"), JSON.stringify(diffs)) - await Filesystem.writeJson(path.join(dir, "session", session.projectID, session.id + ".json"), { - ...session, - summary: { - additions: diffs.reduce((sum: any, x: any) => sum + x.additions, 0), - deletions: diffs.reduce((sum: any, x: any) => sum + x.deletions, 0), - }, - }) + await Bun.file(path.join(dir, "session_diff", session.id + ".json")).write(JSON.stringify(diffs)) + await Bun.file(path.join(dir, "session", session.projectID, session.id + ".json")).write( + JSON.stringify({ + ...session, + summary: { + additions: diffs.reduce((sum: any, x: any) => sum + x.additions, 0), + deletions: diffs.reduce((sum: any, x: any) => sum + x.deletions, 0), + }, + }), + ) } }, ] const state = lazy(async () => { const dir = path.join(Global.Path.data, "storage") - const migration = await Filesystem.readJson(path.join(dir, "migration")) + const migration = await Bun.file(path.join(dir, "migration")) + .json() .then((x) => parseInt(x)) .catch(() => 0) for (let index = migration; index < MIGRATIONS.length; index++) { log.info("running migration", { index }) const migration = MIGRATIONS[index] await migration(dir).catch(() => log.error("failed to run migration", { index })) - await Filesystem.write(path.join(dir, "migration"), (index + 1).toString()) + await Bun.write(path.join(dir, "migration"), (index + 1).toString()) } return { dir, @@ -165,7 +171,7 @@ export namespace Storage { const target = path.join(dir, ...key) + ".json" return withErrorHandling(async () => { using _ = await Lock.read(target) - const result = await Filesystem.readJson(target) + const result = await Bun.file(target).json() return result as T }) } @@ -175,10 +181,10 @@ export namespace Storage { const target = path.join(dir, ...key) + ".json" return withErrorHandling(async () => { using _ = await Lock.write(target) - const content = await Filesystem.readJson(target) - fn(content as T) - await Filesystem.writeJson(target, content) - return content + const content = await Bun.file(target).json() + fn(content) + await Bun.write(target, JSON.stringify(content, null, 2)) + return content as T }) } @@ -187,7 +193,7 @@ export namespace Storage { const target = path.join(dir, ...key) + ".json" return withErrorHandling(async () => { using _ = await Lock.write(target) - await Filesystem.writeJson(target, content) + await Bun.write(target, JSON.stringify(content, null, 2)) }) } diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index 7a097d3fe19d..d84f6ec3499f 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -49,7 +49,7 @@ export const EditTool = Tool.define("edit", { let contentNew = "" await FileTime.withLock(filePath, async () => { if (params.oldString === "") { - const existed = await Filesystem.exists(filePath) + const existed = await Bun.file(filePath).exists() contentNew = params.newString diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew)) await ctx.ask({ @@ -61,7 +61,7 @@ export const EditTool = Tool.define("edit", { diff, }, }) - await Filesystem.write(filePath, params.newString) + await Bun.write(filePath, params.newString) await Bus.publish(File.Event.Edited, { file: filePath, }) @@ -73,11 +73,12 @@ export const EditTool = Tool.define("edit", { return } - const stats = Filesystem.stat(filePath) + const file = Bun.file(filePath) + const stats = await file.stat().catch(() => {}) if (!stats) throw new Error(`File ${filePath} not found`) if (stats.isDirectory()) throw new Error(`Path is a directory, not a file: ${filePath}`) await FileTime.assert(ctx.sessionID, filePath) - contentOld = await Filesystem.readText(filePath) + contentOld = await file.text() contentNew = replace(contentOld, params.oldString, params.newString, params.replaceAll) diff = trimDiff( @@ -93,7 +94,7 @@ export const EditTool = Tool.define("edit", { }, }) - await Filesystem.write(filePath, contentNew) + await file.write(contentNew) await Bus.publish(File.Event.Edited, { file: filePath, }) @@ -101,7 +102,7 @@ export const EditTool = Tool.define("edit", { file: filePath, event: "change", }) - contentNew = await Filesystem.readText(filePath) + contentNew = await file.text() diff = trimDiff( createTwoFilesPatch(filePath, filePath, normalizeLineEndings(contentOld), normalizeLineEndings(contentNew)), ) diff --git a/packages/opencode/src/tool/glob.ts b/packages/opencode/src/tool/glob.ts index a2611246c66f..9df1eedca449 100644 --- a/packages/opencode/src/tool/glob.ts +++ b/packages/opencode/src/tool/glob.ts @@ -1,7 +1,6 @@ import z from "zod" import path from "path" import { Tool } from "./tool" -import { Filesystem } from "../util/filesystem" import DESCRIPTION from "./glob.txt" import { Ripgrep } from "../file/ripgrep" import { Instance } from "../project/instance" @@ -46,7 +45,10 @@ export const GlobTool = Tool.define("glob", { break } const full = path.resolve(search, file) - const stats = Filesystem.stat(full)?.mtime.getTime() ?? 0 + const stats = await Bun.file(full) + .stat() + .then((x) => x.mtime.getTime()) + .catch(() => 0) files.push({ path: full, mtime: stats, diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 00497d4e3fd8..41ed494de923 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -1,6 +1,5 @@ import z from "zod" import { Tool } from "./tool" -import { Filesystem } from "../util/filesystem" import { Ripgrep } from "../file/ripgrep" import DESCRIPTION from "./grep.txt" @@ -84,7 +83,8 @@ export const GrepTool = Tool.define("grep", { const lineNum = parseInt(lineNumStr, 10) const lineText = lineTextParts.join("|") - const stats = Filesystem.stat(filePath) + const file = Bun.file(filePath) + const stats = await file.stat().catch(() => null) if (!stats) continue matches.push({ diff --git a/packages/opencode/src/tool/lsp.ts b/packages/opencode/src/tool/lsp.ts index 52aef0f9e3f2..ca352280b2a9 100644 --- a/packages/opencode/src/tool/lsp.ts +++ b/packages/opencode/src/tool/lsp.ts @@ -6,7 +6,6 @@ import DESCRIPTION from "./lsp.txt" import { Instance } from "../project/instance" import { pathToFileURL } from "url" import { assertExternalDirectory } from "./external-directory" -import { Filesystem } from "../util/filesystem" const operations = [ "goToDefinition", @@ -48,7 +47,7 @@ export const LspTool = Tool.define("lsp", { const relPath = path.relative(Instance.worktree, file) const title = `${args.operation} ${relPath}:${args.line}:${args.character}` - const exists = await Filesystem.exists(file) + const exists = await Bun.file(file).exists() if (!exists) { throw new Error(`File not found: ${file}`) } diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index c981ac16e43c..80ca95900cc3 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -10,7 +10,6 @@ import DESCRIPTION from "./read.txt" import { Instance } from "../project/instance" import { assertExternalDirectory } from "./external-directory" import { InstructionPrompt } from "../session/instruction" -import { Filesystem } from "../util/filesystem" const DEFAULT_READ_LIMIT = 2000 const MAX_LINE_LENGTH = 2000 @@ -35,7 +34,8 @@ export const ReadTool = Tool.define("read", { } const title = path.relative(Instance.worktree, filepath) - const stat = Filesystem.stat(filepath) + const file = Bun.file(filepath) + const stat = await file.stat().catch(() => undefined) await assertExternalDirectory(ctx, filepath, { bypass: Boolean(ctx.extra?.["bypassCwdCheck"]), @@ -118,10 +118,11 @@ export const ReadTool = Tool.define("read", { const instructions = await InstructionPrompt.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" + const isImage = + file.type.startsWith("image/") && file.type !== "image/svg+xml" && file.type !== "image/vnd.fastbidsheet" + const isPdf = file.type === "application/pdf" if (isImage || isPdf) { + const mime = file.type const msg = `${isImage ? "Image" : "PDF"} read successfully` return { title, @@ -135,13 +136,13 @@ export const ReadTool = Tool.define("read", { { type: "file", mime, - url: `data:${mime};base64,${Buffer.from(await Filesystem.readBytes(filepath)).toString("base64")}`, + url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`, }, ], } } - const isBinary = await isBinaryFile(filepath, Number(stat.size)) + const isBinary = await isBinaryFile(filepath, stat.size) if (isBinary) throw new Error(`Cannot read binary file: ${filepath}`) const stream = createReadStream(filepath, { encoding: "utf8" }) diff --git a/packages/opencode/src/tool/truncation.ts b/packages/opencode/src/tool/truncation.ts index 4cc524aeee14..84e799c1310e 100644 --- a/packages/opencode/src/tool/truncation.ts +++ b/packages/opencode/src/tool/truncation.ts @@ -5,7 +5,6 @@ import { Identifier } from "../id/id" import { PermissionNext } from "../permission/next" import type { Agent } from "../agent/agent" import { Scheduler } from "../scheduler" -import { Filesystem } from "../util/filesystem" export namespace Truncate { export const MAX_LINES = 2000 @@ -92,7 +91,7 @@ export namespace Truncate { const id = Identifier.ascending("tool") const filepath = path.join(DIR, id) - await Filesystem.write(filepath, text) + await Bun.write(Bun.file(filepath), text) const hint = hasTaskTool(agent) ? `The tool call succeeded but the output was truncated. Full output saved to: ${filepath}\nUse the Task tool to have explore agent process this file with Grep and Read (with offset/limit). Do NOT read the full file yourself - delegate to save context.` diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts index 8c1e53ccaf3a..eca64d30374d 100644 --- a/packages/opencode/src/tool/write.ts +++ b/packages/opencode/src/tool/write.ts @@ -26,8 +26,9 @@ export const WriteTool = Tool.define("write", { const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath) await assertExternalDirectory(ctx, filepath) - const exists = await Filesystem.exists(filepath) - const contentOld = exists ? await Filesystem.readText(filepath) : "" + const file = Bun.file(filepath) + const exists = await file.exists() + const contentOld = exists ? await file.text() : "" if (exists) await FileTime.assert(ctx.sessionID, filepath) const diff = trimDiff(createTwoFilesPatch(filepath, filepath, contentOld, params.content)) @@ -41,7 +42,7 @@ export const WriteTool = Tool.define("write", { }, }) - await Filesystem.write(filepath, params.content) + await Bun.write(filepath, params.content) await Bus.publish(File.Event.Edited, { file: filepath, }) diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts index b60b06e08edf..7b196eb84b82 100644 --- a/packages/opencode/src/util/filesystem.ts +++ b/packages/opencode/src/util/filesystem.ts @@ -1,10 +1,8 @@ -import { chmod, mkdir, readFile, writeFile } from "fs/promises" -import { createWriteStream, existsSync, statSync } from "fs" +import { mkdir, readFile, writeFile } from "fs/promises" +import { existsSync, statSync } from "fs" import { lookup } from "mime-types" import { realpathSync } from "fs" import { dirname, join, relative } from "path" -import { Readable } from "stream" -import { pipeline } from "stream/promises" export namespace Filesystem { // Fast sync version for metadata checks @@ -70,25 +68,6 @@ export namespace Filesystem { return write(p, JSON.stringify(data, null, 2), mode) } - export async function writeStream( - p: string, - stream: ReadableStream | Readable, - mode?: number, - ): Promise { - const dir = dirname(p) - if (!existsSync(dir)) { - await mkdir(dir, { recursive: true }) - } - - const nodeStream = stream instanceof ReadableStream ? Readable.fromWeb(stream as any) : stream - const writeStream = createWriteStream(p) - await pipeline(nodeStream, writeStream) - - if (mode) { - await chmod(p, mode) - } - } - export function mimeType(p: string): string { return lookup(p) || "application/octet-stream" } diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index c62d592997a4..6941310bbbde 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -1,6 +1,5 @@ import path from "path" import fs from "fs/promises" -import { createWriteStream } from "fs" import { Global } from "../global" import z from "zod" @@ -64,15 +63,13 @@ export namespace Log { Global.Path.log, options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log", ) + const logfile = Bun.file(logpath) await fs.truncate(logpath).catch(() => {}) - const stream = createWriteStream(logpath, { flags: "a" }) + const writer = logfile.writer() write = async (msg: any) => { - return new Promise((resolve, reject) => { - stream.write(msg, (err) => { - if (err) reject(err) - else resolve(msg.length) - }) - }) + const num = writer.write(msg) + writer.flush() + return num } } diff --git a/packages/opencode/test/util/filesystem.test.ts b/packages/opencode/test/util/filesystem.test.ts index 0f544793734e..3c3da0fc7916 100644 --- a/packages/opencode/test/util/filesystem.test.ts +++ b/packages/opencode/test/util/filesystem.test.ts @@ -285,125 +285,4 @@ describe("filesystem", () => { expect(Filesystem.mimeType("Makefile")).toBe("application/octet-stream") }) }) - - describe("writeStream()", () => { - test("writes from Web ReadableStream", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "streamed.txt") - const content = "Hello from stream!" - const encoder = new TextEncoder() - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(encoder.encode(content)) - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream) - - expect(await fs.readFile(filepath, "utf-8")).toBe(content) - }) - - test("writes from Node.js Readable stream", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "node-streamed.txt") - const content = "Hello from Node stream!" - const { Readable } = await import("stream") - const stream = Readable.from([content]) - - await Filesystem.writeStream(filepath, stream) - - expect(await fs.readFile(filepath, "utf-8")).toBe(content) - }) - - test("writes binary data from Web ReadableStream", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "binary.dat") - const binaryData = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0xff]) - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(binaryData) - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream) - - const read = await fs.readFile(filepath) - expect(Buffer.from(read)).toEqual(Buffer.from(binaryData)) - }) - - test("writes large content in chunks", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "large.txt") - const chunks = ["chunk1", "chunk2", "chunk3", "chunk4", "chunk5"] - const stream = new ReadableStream({ - start(controller) { - for (const chunk of chunks) { - controller.enqueue(new TextEncoder().encode(chunk)) - } - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream) - - expect(await fs.readFile(filepath, "utf-8")).toBe(chunks.join("")) - }) - - test("creates parent directories", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "nested", "deep", "streamed.txt") - const content = "nested stream content" - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(content)) - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream) - - expect(await fs.readFile(filepath, "utf-8")).toBe(content) - }) - - test("writes with permissions", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "protected-stream.txt") - const content = "secret stream content" - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(content)) - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream, 0o600) - - const stats = await fs.stat(filepath) - if (process.platform !== "win32") { - expect(stats.mode & 0o777).toBe(0o600) - } - }) - - test("writes executable with permissions", async () => { - await using tmp = await tmpdir() - const filepath = path.join(tmp.path, "script.sh") - const content = "#!/bin/bash\necho hello" - const stream = new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(content)) - controller.close() - }, - }) - - await Filesystem.writeStream(filepath, stream, 0o755) - - const stats = await fs.stat(filepath) - if (process.platform !== "win32") { - expect(stats.mode & 0o777).toBe(0o755) - } - expect(await fs.readFile(filepath, "utf-8")).toBe(content) - }) - }) }) From d620455531443340d2719510d37e80af433cef7e Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 19 Feb 2026 09:34:23 +0800 Subject: [PATCH 244/255] app: deduplicate allServers list --- packages/app/src/components/dialog-select-server.tsx | 7 ++++--- packages/app/src/context/server.tsx | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index fa5d2d36c7a4..76c8ff60ef06 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/packages/app/src/components/dialog-select-server.tsx @@ -427,6 +427,7 @@ export function DialogSelectServer() { } > {(i) => { + const key = ServerConnection.key(i) return (
@@ -460,7 +461,7 @@ export function DialogSelectServer() {
- +

{language.t("dialog.server.current")}

diff --git a/packages/app/src/context/server.tsx b/packages/app/src/context/server.tsx index 336f8aa98cb2..389371702eb8 100644 --- a/packages/app/src/context/server.tsx +++ b/packages/app/src/context/server.tsx @@ -102,15 +102,19 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext( }), ) - const allServers = createMemo( - (): Array => [ + const allServers = createMemo((): Array => { + const servers = [ ...(props.servers ?? []), ...store.list.map((value) => ({ type: "http" as const, http: typeof value === "string" ? { url: value } : value, })), - ], - ) + ] + + const deduped = new Map(servers.map((conn) => [ServerConnection.key(conn), conn])) + + return [...deduped.values()] + }) const [state, setState] = createStore({ active: props.defaultServer, From 11a37834c2afd5a1ba88f8417701472234caaa3a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 18 Feb 2026 20:36:57 -0500 Subject: [PATCH 245/255] tui: ensure onExit callback fires after terminal output is written --- packages/opencode/src/cli/cmd/tui/context/exit.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/exit.tsx b/packages/opencode/src/cli/cmd/tui/context/exit.tsx index 3eb2edf72777..a6f775913a47 100644 --- a/packages/opencode/src/cli/cmd/tui/context/exit.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/exit.tsx @@ -34,7 +34,6 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({ renderer.setTerminalTitle("") renderer.destroy() win32FlushInputBuffer() - await input.onExit?.() if (reason) { const formatted = FormatError(reason) ?? FormatUnknownError(reason) if (formatted) { @@ -43,7 +42,7 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({ } const text = store.get() if (text) process.stdout.write(text + "\n") - process.exit(0) + await input.onExit?.() }, { message: store, From 3a416f6f33254e541de05cb2d661bdc0d010dd9e Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 18 Feb 2026 20:40:35 -0500 Subject: [PATCH 246/255] sdk: fix nested exports transformation in publish script The publish script now recursively transforms export paths to handle nested export objects. This ensures all SDK entry points are correctly mapped to their compiled dist/ locations and type definitions when publishing to npm. --- packages/sdk/js/script/publish.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/sdk/js/script/publish.ts b/packages/sdk/js/script/publish.ts index 46dd42b700d1..c21f06230d18 100755 --- a/packages/sdk/js/script/publish.ts +++ b/packages/sdk/js/script/publish.ts @@ -6,15 +6,24 @@ import { $ } from "bun" const dir = new URL("..", import.meta.url).pathname process.chdir(dir) -const pkg = await import("../package.json").then((m) => m.default) +const pkg = (await import("../package.json").then((m) => m.default)) as { + exports: Record +} const original = JSON.parse(JSON.stringify(pkg)) -for (const [key, value] of Object.entries(pkg.exports)) { - const file = value.replace("./src/", "./dist/").replace(".ts", "") - pkg.exports[key] = { - import: file + ".js", - types: file + ".d.ts", +function transformExports(exports: Record) { + for (const [key, value] of Object.entries(exports)) { + if (typeof value === "object" && value !== null) { + transformExports(value as Record) + } else if (typeof value === "string") { + const file = value.replace("./src/", "./dist/").replace(".ts", "") + exports[key] = { + import: file + ".js", + types: file + ".d.ts", + } + } } } +transformExports(pkg.exports) await Bun.write("package.json", JSON.stringify(pkg, null, 2)) await $`bun pm pack` await $`npm publish *.tgz --tag ${Script.channel} --access public` From 1893473148e90e98e49759b58bfe88d97ff9f7d3 Mon Sep 17 00:00:00 2001 From: Ariane Emory <97994360+ariane-emory@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:18:24 -0500 Subject: [PATCH 247/255] fix: token substitution in OPENCODE_CONFIG_CONTENT (alternate take) (#14047) --- packages/opencode/src/config/config.ts | 76 ++++++++++++-------- packages/opencode/test/config/config.test.ts | 63 ++++++++++++++++ 2 files changed, 111 insertions(+), 28 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index dfdcb0343ef3..3493d232555d 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -89,7 +89,13 @@ export namespace Config { const remoteConfig = wellknown.config ?? {} // Add $schema to prevent load() from trying to write back to a non-existent file if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json" - result = merge(result, await load(JSON.stringify(remoteConfig), `${key}/.well-known/opencode`)) + result = merge( + result, + await load(JSON.stringify(remoteConfig), { + dir: path.dirname(`${key}/.well-known/opencode`), + source: `${key}/.well-known/opencode`, + }), + ) log.debug("loaded remote config from well-known", { url: key }) } } @@ -177,8 +183,14 @@ export namespace Config { } // Inline config content overrides all non-managed config sources. - if (Flag.OPENCODE_CONFIG_CONTENT) { - result = merge(result, JSON.parse(Flag.OPENCODE_CONFIG_CONTENT)) + if (process.env.OPENCODE_CONFIG_CONTENT) { + result = merge( + result, + await load(process.env.OPENCODE_CONFIG_CONTENT, { + dir: Instance.directory, + source: "OPENCODE_CONFIG_CONTENT", + }), + ) log.debug("loaded custom config from OPENCODE_CONFIG_CONTENT") } @@ -1236,24 +1248,32 @@ export namespace Config { throw new JsonError({ path: filepath }, { cause: err }) }) if (!text) return {} - return load(text, filepath) + return load(text, { path: filepath }) } - async function load(text: string, configFilepath: string) { + async function load( + text: string, + options: + | { path: string } + | { dir: string; source: string }, + ) { const original = text + const configDir = "path" in options ? path.dirname(options.path) : options.dir + const source = "path" in options ? options.path : options.source + const isFile = "path" in options + text = text.replace(/\{env:([^}]+)\}/g, (_, varName) => { return process.env[varName] || "" }) const fileMatches = text.match(/\{file:[^}]+\}/g) if (fileMatches) { - const configDir = path.dirname(configFilepath) const lines = text.split("\n") for (const match of fileMatches) { const lineIndex = lines.findIndex((line) => line.includes(match)) if (lineIndex !== -1 && lines[lineIndex].trim().startsWith("//")) { - continue // Skip if line is commented + continue } let filePath = match.replace(/^\{file:/, "").replace(/\}$/, "") if (filePath.startsWith("~/")) { @@ -1261,21 +1281,22 @@ export namespace Config { } const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath) const fileContent = ( - await Filesystem.readText(resolvedPath).catch((error: any) => { - const errMsg = `bad file reference: "${match}"` - if (error.code === "ENOENT") { - throw new InvalidError( - { - path: configFilepath, - message: errMsg + ` ${resolvedPath} does not exist`, - }, - { cause: error }, - ) - } - throw new InvalidError({ path: configFilepath, message: errMsg }, { cause: error }) - }) + await Bun.file(resolvedPath) + .text() + .catch((error) => { + const errMsg = `bad file reference: "${match}"` + if (error.code === "ENOENT") { + throw new InvalidError( + { + path: source, + message: errMsg + ` ${resolvedPath} does not exist`, + }, + { cause: error }, + ) + } + throw new InvalidError({ path: source, message: errMsg }, { cause: error }) + }) ).trim() - // escape newlines/quotes, strip outer quotes text = text.replace(match, () => JSON.stringify(fileContent).slice(1, -1)) } } @@ -1299,25 +1320,24 @@ export namespace Config { .join("\n") throw new JsonError({ - path: configFilepath, + path: source, message: `\n--- JSONC Input ---\n${text}\n--- Errors ---\n${errorDetails}\n--- End ---`, }) } const parsed = Info.safeParse(data) if (parsed.success) { - if (!parsed.data.$schema) { + if (!parsed.data.$schema && isFile) { parsed.data.$schema = "https://opencode.ai/config.json" - // Write the $schema to the original text to preserve variables like {env:VAR} const updated = original.replace(/^\s*\{/, '{\n "$schema": "https://opencode.ai/config.json",') - await Filesystem.write(configFilepath, updated).catch(() => {}) + await Bun.write(options.path, updated).catch(() => {}) } const data = parsed.data - if (data.plugin) { + if (data.plugin && isFile) { for (let i = 0; i < data.plugin.length; i++) { const plugin = data.plugin[i] try { - data.plugin[i] = import.meta.resolve!(plugin, configFilepath) + data.plugin[i] = import.meta.resolve!(plugin, options.path) } catch (err) {} } } @@ -1325,7 +1345,7 @@ export namespace Config { } throw new InvalidError({ - path: configFilepath, + path: source, issues: parsed.error.issues, }) } diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 91b87f6498c4..836a3f5d152d 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -1800,3 +1800,66 @@ describe("OPENCODE_DISABLE_PROJECT_CONFIG", () => { } }) }) + +describe("OPENCODE_CONFIG_CONTENT token substitution", () => { + test("substitutes {env:} tokens in OPENCODE_CONFIG_CONTENT", async () => { + const originalEnv = process.env["OPENCODE_CONFIG_CONTENT"] + const originalTestVar = process.env["TEST_CONFIG_VAR"] + process.env["TEST_CONFIG_VAR"] = "test_api_key_12345" + process.env["OPENCODE_CONFIG_CONTENT"] = JSON.stringify({ + $schema: "https://opencode.ai/config.json", + theme: "{env:TEST_CONFIG_VAR}", + }) + + try { + await using tmp = await tmpdir() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.theme).toBe("test_api_key_12345") + }, + }) + } finally { + if (originalEnv !== undefined) { + process.env["OPENCODE_CONFIG_CONTENT"] = originalEnv + } else { + delete process.env["OPENCODE_CONFIG_CONTENT"] + } + if (originalTestVar !== undefined) { + process.env["TEST_CONFIG_VAR"] = originalTestVar + } else { + delete process.env["TEST_CONFIG_VAR"] + } + } + }) + + test("substitutes {file:} tokens in OPENCODE_CONFIG_CONTENT", async () => { + const originalEnv = process.env["OPENCODE_CONFIG_CONTENT"] + + try { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "api_key.txt"), "secret_key_from_file") + process.env["OPENCODE_CONFIG_CONTENT"] = JSON.stringify({ + $schema: "https://opencode.ai/config.json", + theme: "{file:./api_key.txt}", + }) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.theme).toBe("secret_key_from_file") + }, + }) + } finally { + if (originalEnv !== undefined) { + process.env["OPENCODE_CONFIG_CONTENT"] = originalEnv + } else { + delete process.env["OPENCODE_CONFIG_CONTENT"] + } + } + }) +}) From 4b878f6aebb089244d69aa7cb7806e65e61bfbed Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Thu, 19 Feb 2026 05:19:18 +0000 Subject: [PATCH 248/255] chore: generate --- packages/opencode/src/config/config.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 3493d232555d..36f6c762bb3b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1251,12 +1251,7 @@ export namespace Config { return load(text, { path: filepath }) } - async function load( - text: string, - options: - | { path: string } - | { dir: string; source: string }, - ) { + async function load(text: string, options: { path: string } | { dir: string; source: string }) { const original = text const configDir = "path" in options ? path.dirname(options.path) : options.dir const source = "path" in options ? options.path : options.source From 308e5008326df36e23ed97106f1acbfcac247c45 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:31:33 -0600 Subject: [PATCH 249/255] tweak: bake in the aws and google auth pkgs (#14241) --- bun.lock | 254 ++++++++++++++++++++- packages/opencode/package.json | 2 + packages/opencode/src/provider/provider.ts | 8 +- 3 files changed, 250 insertions(+), 14 deletions(-) diff --git a/bun.lock b/bun.lock index bd340ea6e5e6..2df39fa547b5 100644 --- a/bun.lock +++ b/bun.lock @@ -288,6 +288,7 @@ "@ai-sdk/togetherai": "1.0.34", "@ai-sdk/vercel": "1.0.33", "@ai-sdk/xai": "2.0.51", + "@aws-sdk/credential-providers": "3.993.0", "@clack/prompts": "1.0.0-alpha.1", "@gitlab/gitlab-ai-provider": "3.6.0", "@gitlab/opencode-gitlab-auth": "1.3.3", @@ -320,6 +321,7 @@ "diff": "catalog:", "drizzle-orm": "1.0.0-beta.12-a5629fb", "fuzzysort": "3.1.0", + "google-auth-library": "10.5.0", "gray-matter": "4.0.3", "hono": "catalog:", "hono-openapi": "catalog:", @@ -670,27 +672,35 @@ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="], + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.933.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-node": "3.933.0", "@aws-sdk/middleware-bucket-endpoint": "3.930.0", "@aws-sdk/middleware-expect-continue": "3.930.0", "@aws-sdk/middleware-flexible-checksums": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-location-constraint": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/middleware-ssec": "3.930.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/signature-v4-multi-region": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-KxwZvdxdCeWK6o8mpnb+kk7Kgb8V+8AjTwSXUWH1UAD85B0tjdo1cSfE5zoR5fWGol4Ml5RLez12a6LPhsoTqA=="], - "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="], + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-VLUN+wIeNX24fg12SCbzTUBnBENlL014yMKZvRhPkcn4wHR6LKgNrjsG3fZ03Xs0XoKaGtNFi1VVrq666sGBoQ=="], "@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/credential-provider-node": "3.782.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-Q1QLY3xE2z1trgriusP/6w40mI/yJjM524bN4gs+g6YX4sZGufpa7+Dj+JjL4fz8f9BCJ3ZlI+p4WxFxH7qvdQ=="], "@aws-sdk/core": ["@aws-sdk/core@3.932.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.2", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-AS8gypYQCbNojwgjvZGkJocC2CoEICDx9ZJ15ILsv+MlcCVLtUJSRSx3VzJOUY2EEIaGLRrPNlIqyn/9/fySvA=="], - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ozge/c7NdHUDyHqro6+P5oHt8wfKSUBN+olttiVfBe9Mw3wBMpPa3gQ0pZnG+gwBkKskBuip2bMR16tqYvUSEA=="], + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.3", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.980.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dW/DqTk90XW7hIngqntAVtJJyrkS51wcLhGz39lOMe0TlSmZl+5R/UGnAZqNbXmWuJHLzxe+MLgagxH41aTsAQ=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.9", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ZptrOwQynfupubvcngLkbdIq/aXvl/czdpEG8XJ8mN8Nb19BR0jaK0bR+tfuMU36Ez9q4xv7GGkHFqEEP2hUUQ=="], - "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-b6N9Nnlg8JInQwzBkUq5spNaXssM3h3zLxGzpPrnw0nHSIWPJPTbZzA5Ca285fcDUFuKP+qf3qkuqlAjGOdWhg=="], + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.11", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.10", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" } }, "sha512-hECWoOoH386bGr89NQc9vA/abkGf5TJrMREt+lhNcnSNmoBS04fK7vc3LrJBSQAUGGVj0Tz3f4dHB3w5veovig=="], - "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-env": "3.932.0", "@aws-sdk/credential-provider-http": "3.932.0", "@aws-sdk/credential-provider-process": "3.932.0", "@aws-sdk/credential-provider-sso": "3.933.0", "@aws-sdk/credential-provider-web-identity": "3.933.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HygGyKuMG5AaGXsmM0d81miWDon55xwalRHB3UmDg3QBhtunbNIoIaWUbNTKuBZXcIN6emeeEZw/YgSMqLc0YA=="], + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.9", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-login": "^3.972.9", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-zr1csEu9n4eDiHMTYJabX1mDGuGLgjgUnNckIivvk43DocJC9/f6DefFrnUPZXE+GHtbW50YuXb+JIxKykU74A=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.9", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-m4RIpVgZChv0vWS/HKChg1xLgZPpx8Z+ly9Fv7FwA8SOfuC6I3htcSaBz2Ch4bneRIiBUhwP4ziUo0UZgtJStQ=="], "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.933.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.932.0", "@aws-sdk/credential-provider-http": "3.932.0", "@aws-sdk/credential-provider-ini": "3.933.0", "@aws-sdk/credential-provider-process": "3.932.0", "@aws-sdk/credential-provider-sso": "3.933.0", "@aws-sdk/credential-provider-web-identity": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-L2dE0Y7iMLammQewPKNeEh1z/fdJyYEU+/QsLBD9VEh+SXcN/FIyTi21Isw8wPZN6lMB9PDVtISzBnF8HuSFrw=="], - "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-BodZYKvT4p/Dkm28Ql/FhDdS1+p51bcZeMMu2TRtU8PoMDHnVDhHz27zASEKSZwmhvquxHrZHB0IGuVqjZUtSQ=="], + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.9", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-gOWl0Fe2gETj5Bk151+LYKpeGi2lBDLNu+NMNpHRlIrKHdBmVun8/AalwMK8ci4uRfG5a3/+zvZBMpuen1SZ0A=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.9", "", { "dependencies": { "@aws-sdk/client-sso": "3.993.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/token-providers": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ey7S686foGTArvFhi3ifQXmgptKYvLSGE2250BAQceMSXZddz7sUSNERGJT2S7u5KIe/kgugxrt01hntXVln6w=="], - "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.933.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.933.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/token-providers": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/R1DBR7xNcuZIhS2RirU+P2o8E8/fOk+iLAhbqeSTq+g09fP/F6W7ouFpS5eVE2NIfWG7YBFoVddOhvuqpn51g=="], + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.9", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8LnfS76nHXoEc9aRRiMMpxZxJeDG0yusdyo3NvPhCgESmBUgpMa4luhGbClW5NoX/qRcGxxM6Z/esqANSNMTow=="], - "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-c7Eccw2lhFx2/+qJn3g+uIDWRuWi2A6Sz3PVvckFUEzPsP0dPUo19hlvtarwP5GzrsXn0yEPRVhpewsIaSCGaQ=="], + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.993.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.993.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-cognito-identity": "^3.972.3", "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-login": "^3.972.9", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-1M/nukgPSLqe9krzOKHnE8OylUaKAiokAV3xRLdeExVHcRE7WG5uzCTKWTj1imKvPjDqXq/FWhlbbdWIn7xIwA=="], "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA=="], @@ -712,13 +722,13 @@ "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@smithy/core": "^3.18.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-9BGTbJyA/4PTdwQWE9hAFIJGpsYkyEW20WON3i15aDqo5oRZwZmqaVageOD57YYqG8JDJjvcwKyDdR4cc38dvg=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iOq86f2H67924kQUIPOAvlmMaOAvOLoDOIb66I2YqSUpMYB6ufiuJW3RlREgskxv86S5qKzMnfy/X6CqMjK6XQ=="], "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-KL2JZqH6aYeQssu1g1KuWsReupdfOoxD6f1as2VC+rdwYFUu4LfzMsFfXnBvvQWWqQ7rZHWOw1T+o5gJmg7Dzw=="], "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.932.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-NCIRJvoRc9246RZHIusY1+n/neeG2yGhBGdKhghmrNdM+mLLN6Ii7CKFZjx3DhxtpHMpl1HWLTMhdVrGwP2upw=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qzq7zj9yXUgAAJEbbmqRhm0jmUndl8nHG0AbxFEfCfQRVZWL96Qzx0mf8lYwT9hIMrXncLwy31HOthmbXwFRwQ=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.993.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-+35g4c+8r7sB9Sjp1KPdM8qxGn6B/shBjJtEUN4e+Edw9UEQlZKIzioOGu3UAbyE0a/s450LdLZr4wbJChtmww=="], "@aws-sdk/types": ["@aws-sdk/types@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-we/vaAgwlEFW7IeftmCLlLMw+6hFs3DzZPJw7lVHbj/5HJ0bz9gndxEsS2lQoeJ1zhiiLqAqvXxmM43s0MBg0A=="], @@ -4252,6 +4262,48 @@ "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + "@aws-sdk/client-cognito-identity/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.10", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.11", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="], + + "@aws-sdk/client-cognito-identity/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.9", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA=="], + + "@aws-sdk/client-sso/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="], + + "@aws-sdk/client-sso/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.11", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw=="], + + "@aws-sdk/client-sso/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="], + + "@aws-sdk/client-sso/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/client-sso/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + + "@aws-sdk/client-sso/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="], + + "@aws-sdk/client-sso/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.9", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA=="], + "@aws-sdk/client-sts/@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.782.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-ini": "3.782.0", "@aws-sdk/credential-provider-process": "3.775.0", "@aws-sdk/credential-provider-sso": "3.782.0", "@aws-sdk/credential-provider-web-identity": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw=="], @@ -4274,6 +4326,80 @@ "@aws-sdk/client-sts/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.782.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.980.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.5", "@aws-sdk/credential-provider-node": "^3.972.4", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.5", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.980.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.3", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.12", "@smithy/middleware-retry": "^4.4.29", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.28", "@smithy/util-defaults-mode-node": "^4.2.31", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-nLgMW2drTzv+dTo3ORCcotQPcrUaTQ+xoaDTdSaUXdZO7zbbVyk7ysE5GDTnJdZWcUjHOSB8xfNQhOTTNVPhFw=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-env/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-env/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ozge/c7NdHUDyHqro6+P5oHt8wfKSUBN+olttiVfBe9Mw3wBMpPa3gQ0pZnG+gwBkKskBuip2bMR16tqYvUSEA=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-b6N9Nnlg8JInQwzBkUq5spNaXssM3h3zLxGzpPrnw0nHSIWPJPTbZzA5Ca285fcDUFuKP+qf3qkuqlAjGOdWhg=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-env": "3.932.0", "@aws-sdk/credential-provider-http": "3.932.0", "@aws-sdk/credential-provider-process": "3.932.0", "@aws-sdk/credential-provider-sso": "3.933.0", "@aws-sdk/credential-provider-web-identity": "3.933.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HygGyKuMG5AaGXsmM0d81miWDon55xwalRHB3UmDg3QBhtunbNIoIaWUbNTKuBZXcIN6emeeEZw/YgSMqLc0YA=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.932.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-BodZYKvT4p/Dkm28Ql/FhDdS1+p51bcZeMMu2TRtU8PoMDHnVDhHz27zASEKSZwmhvquxHrZHB0IGuVqjZUtSQ=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.933.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.933.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/token-providers": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-/R1DBR7xNcuZIhS2RirU+P2o8E8/fOk+iLAhbqeSTq+g09fP/F6W7ouFpS5eVE2NIfWG7YBFoVddOhvuqpn51g=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-c7Eccw2lhFx2/+qJn3g+uIDWRuWi2A6Sz3PVvckFUEzPsP0dPUo19hlvtarwP5GzrsXn0yEPRVhpewsIaSCGaQ=="], + + "@aws-sdk/credential-provider-process/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-process/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/credential-providers/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-providers/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.10", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA=="], + + "@aws-sdk/credential-providers/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="], + + "@aws-sdk/nested-clients/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.11", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw=="], + + "@aws-sdk/nested-clients/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="], + + "@aws-sdk/nested-clients/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + + "@aws-sdk/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + + "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="], + + "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.9", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA=="], + + "@aws-sdk/token-providers/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/token-providers/@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], + "@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], "@azure/core-http/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="], @@ -4804,6 +4930,10 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/client-sso/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="], @@ -4816,6 +4946,54 @@ "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/core": ["@aws-sdk/core@3.973.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.5", "@smithy/core": "^3.23.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.10", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.11", "", { "dependencies": { "@aws-sdk/core": "^3.973.11", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@smithy/core": "^3.23.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.980.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-AjKBNEc+rjOZQE1HwcD9aCELqg1GmUj1rtICKuY8cgwB73xJ4U/kNyqKKpN2k9emGqlfDY2D8itIp/vDc6OKpw=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.9", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA=="], + + "@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.933.0", "", { "dependencies": { "@aws-sdk/core": "3.932.0", "@aws-sdk/nested-clients": "3.933.0", "@aws-sdk/types": "3.930.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qzq7zj9yXUgAAJEbbmqRhm0jmUndl8nHG0AbxFEfCfQRVZWL96Qzx0mf8lYwT9hIMrXncLwy31HOthmbXwFRwQ=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="], + + "@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + "@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], "@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], @@ -5254,6 +5432,10 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/client-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="], @@ -5262,6 +5444,32 @@ "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.5", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA=="], + + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.993.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw=="], + + "@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="], + + "@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + "@jsx-email/cli/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "@jsx-email/cli/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], @@ -5412,8 +5620,34 @@ "@astrojs/check/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/client-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="], + + "@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + + "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + "@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="], @@ -5448,6 +5682,8 @@ "tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + "archiver-utils/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "archiver-utils/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index dc9bfdaac86f..fb2d4d8151bd 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -55,6 +55,7 @@ "@actions/core": "1.11.1", "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.14.1", + "@aws-sdk/credential-providers": "3.993.0", "@ai-sdk/amazon-bedrock": "3.0.79", "@ai-sdk/anthropic": "2.0.62", "@ai-sdk/azure": "2.0.91", @@ -107,6 +108,7 @@ "drizzle-orm": "1.0.0-beta.12-a5629fb", "fuzzysort": "3.1.0", "gray-matter": "4.0.3", + "google-auth-library": "10.5.0", "hono": "catalog:", "hono-openapi": "catalog:", "ignore": "7.0.5", diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index d94d0cbb2239..f1871ddb696e 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -39,6 +39,8 @@ import { createTogetherAI } from "@ai-sdk/togetherai" import { createPerplexity } from "@ai-sdk/perplexity" import { createVercel } from "@ai-sdk/vercel" import { createGitLab, VERSION as GITLAB_PROVIDER_VERSION } from "@gitlab/gitlab-ai-provider" +import { fromNodeProviderChain } from "@aws-sdk/credential-providers" +import { GoogleAuth } from "google-auth-library" import { ProviderTransform } from "./transform" import { Installation } from "../installation" @@ -251,8 +253,6 @@ export namespace Provider { // Only use credential chain if no bearer token exists // Bearer token takes precedence over credential chain (profiles, access keys, IAM roles, web identity tokens) if (!awsBearerToken) { - const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers")) - // Build credential provider options (only pass profile if specified) const credentialProviderOptions = profile ? { profile } : {} @@ -395,11 +395,9 @@ export namespace Provider { project, location, fetch: async (input: RequestInfo | URL, init?: RequestInit) => { - const { GoogleAuth } = await import(await BunProc.install("google-auth-library")) const auth = new GoogleAuth() const client = await auth.getApplicationDefault() - const credentials = await client.credential - const token = await credentials.getAccessToken() + const token = await client.credential.getAccessToken() const headers = new Headers(init?.headers) headers.set("Authorization", `Bearer ${token.token}`) From c7b35342ddca083b2a2b9668778b4cccb6b5f602 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Thu, 19 Feb 2026 06:40:44 +0000 Subject: [PATCH 250/255] 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 d0e314a74dd3..8441e5a36385 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-7y6gQyIxyrdp2DaG/0oOEpuL+1n9oa8arUn1CuDiDhA=", - "aarch64-linux": "sha256-7dnHO2WqQZ9A8cG3EC8p7408YR9n2F5C6DG5rNWHqNY=", - "aarch64-darwin": "sha256-jxjhnVfE61RVOHaWvDO4mGLk6guQ8jHeXv/pbu5nbaE=", - "x86_64-darwin": "sha256-22yM4FEtVxGWRug6H0rKog86Q/cYE3QsADrRbLeJKVQ=" + "x86_64-linux": "sha256-zs3o4OrLGqECnOxzbawP1UC+a7U3pZKr9QE+36qW+iA=", + "aarch64-linux": "sha256-bg0xtNJBbaZpDleCw+S6aay9Ntcil/h4HW7a1jGfc8Q=", + "aarch64-darwin": "sha256-alEZaFnNgd/7evGv+HLUieeRr8+YVN/FxhH2sNQBMcQ=", + "x86_64-darwin": "sha256-NMBZX6Y7JCUqK6ntCoaf7/a6tFArzDSV/TnBCTtwGMw=" } } From d07f09925fae3dd0eac245b1817ace5eee19f0aa Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 19 Feb 2026 06:35:14 -0600 Subject: [PATCH 251/255] fix(app): terminal rework (#14217) --- packages/app/src/components/terminal.tsx | 58 +++++++++-------- .../app/src/pages/session/terminal-panel.tsx | 60 ++++++++++++++---- packages/opencode/src/pty/index.ts | 63 +++++++++++-------- packages/opencode/src/server/routes/pty.ts | 13 ++-- .../test/pty/pty-output-isolation.test.ts | 46 -------------- patches/ghostty-web@0.3.0.patch | 40 ------------ 6 files changed, 122 insertions(+), 158 deletions(-) delete mode 100644 patches/ghostty-web@0.3.0.patch diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index 085a796134b5..bd7ab24475a6 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -320,8 +320,6 @@ export const Terminal = (props: TerminalProps) => { const mod = loaded.mod const g = loaded.ghostty - const once = { value: false } - const restore = typeof local.pty.buffer === "string" ? local.pty.buffer : "" const restoreSize = restore && @@ -416,20 +414,28 @@ export const Terminal = (props: TerminalProps) => { cleanups.push(() => window.removeEventListener("resize", handleResize)) } - if (restore && restoreSize) { - t.write(restore, () => { - fit.fit() - scheduleSize(t.cols, t.rows) - if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY) - startResize() + const write = (data: string) => + new Promise((resolve) => { + if (!output) { + resolve() + return + } + output.push(data) + output.flush(resolve) }) + + if (restore && restoreSize) { + await write(restore) + fit.fit() + scheduleSize(t.cols, t.rows) + if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY) + startResize() } else { fit.fit() scheduleSize(t.cols, t.rows) if (restore) { - t.write(restore, () => { - if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY) - }) + await write(restore) + if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY) } startResize() } @@ -438,38 +444,32 @@ export const Terminal = (props: TerminalProps) => { // console.log("Scroll position:", ydisp) // }) + const once = { value: false } + let closing = false + const url = new URL(sdk.url + `/pty/${local.pty.id}/connect`) url.searchParams.set("directory", sdk.directory) url.searchParams.set("cursor", String(start !== undefined ? start : local.pty.buffer ? -1 : 0)) url.protocol = url.protocol === "https:" ? "wss:" : "ws:" url.username = server.current?.http.username ?? "" url.password = server.current?.http.password ?? "" + const socket = new WebSocket(url) socket.binaryType = "arraybuffer" ws = socket - cleanups.push(() => { - if (socket.readyState !== WebSocket.CLOSED && socket.readyState !== WebSocket.CLOSING) socket.close() - }) - if (disposed) { - cleanup() - return - } const handleOpen = () => { local.onConnect?.() scheduleSize(t.cols, t.rows) } socket.addEventListener("open", handleOpen) - cleanups.push(() => socket.removeEventListener("open", handleOpen)) - if (socket.readyState === WebSocket.OPEN) handleOpen() const decoder = new TextDecoder() - const handleMessage = (event: MessageEvent) => { if (disposed) return + if (closing) return if (event.data instanceof ArrayBuffer) { - // WebSocket control frame: 0x00 + UTF-8 JSON (currently { cursor }). const bytes = new Uint8Array(event.data) if (bytes[0] !== 0) return const json = decoder.decode(bytes.subarray(1)) @@ -491,20 +491,20 @@ export const Terminal = (props: TerminalProps) => { cursor += data.length } socket.addEventListener("message", handleMessage) - cleanups.push(() => socket.removeEventListener("message", handleMessage)) const handleError = (error: Event) => { if (disposed) return + if (closing) return if (once.value) return once.value = true console.error("WebSocket error:", error) local.onConnectError?.(error) } socket.addEventListener("error", handleError) - cleanups.push(() => socket.removeEventListener("error", handleError)) const handleClose = (event: CloseEvent) => { if (disposed) return + if (closing) return // Normal closure (code 1000) means PTY process exited - server event handles cleanup // For other codes (network issues, server restart), trigger error handler if (event.code !== 1000) { @@ -514,7 +514,15 @@ export const Terminal = (props: TerminalProps) => { } } socket.addEventListener("close", handleClose) - cleanups.push(() => socket.removeEventListener("close", handleClose)) + + cleanups.push(() => { + closing = true + socket.removeEventListener("open", handleOpen) + socket.removeEventListener("message", handleMessage) + socket.removeEventListener("error", handleError) + socket.removeEventListener("close", handleClose) + if (socket.readyState !== WebSocket.CLOSED && socket.readyState !== WebSocket.CLOSING) socket.close(1000) + }) } void run().catch((err) => { diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx index 33421c3869ab..73f61ab054f5 100644 --- a/packages/app/src/pages/session/terminal-panel.tsx +++ b/packages/app/src/pages/session/terminal-panel.tsx @@ -38,9 +38,34 @@ export function TerminalPanel() { const [store, setStore] = createStore({ autoCreated: false, + everOpened: false, activeDraggable: undefined as string | undefined, }) + const rendered = createMemo(() => isDesktop() && (opened() || store.everOpened)) + + createEffect( + on(open, (isOpen, prev) => { + if (isOpen) { + if (!store.everOpened) setStore("everOpened", true) + const activeId = terminal.active() + if (!activeId) return + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur() + } + setTimeout(() => focusTerminalById(activeId), 0) + return + } + + if (!prev) return + const panel = document.getElementById("terminal-panel") + const activeElement = document.activeElement + if (!panel || !(activeElement instanceof HTMLElement)) return + if (!panel.contains(activeElement)) return + activeElement.blur() + }), + ) + createEffect(() => { if (!opened()) { setStore("autoCreated", false) @@ -67,7 +92,7 @@ export function TerminalPanel() { on( () => terminal.active(), (activeId) => { - if (!activeId || !opened()) return + if (!activeId || !open()) return if (document.activeElement instanceof HTMLElement) { document.activeElement.blur() } @@ -133,23 +158,32 @@ export function TerminalPanel() { } return ( - +
- + + + | ArrayBuffer) => void + send: (data: string | Uint8Array | ArrayBuffer) => void close: (code?: number, reason?: string) => void } - // Bun's ServerWebSocket has a per-connection `.data` object (set during - // `server.upgrade`) that changes when the underlying connection is recycled. - // We keep a reference to a stable part of it so output can't leak even when - // websocket objects are reused. - const token = (ws: Socket) => { - const data = ws.data - const events = (data as { events?: unknown }).events - if (events && typeof events === "object") return events + type Subscriber = { + id: number + } - const url = (data as { url?: unknown }).url - if (url && typeof url === "object") return url + const sockets = new WeakMap() + const owners = new WeakMap() + let socketCounter = 0 - return data + const tagSocket = (ws: Socket) => { + if (!ws || typeof ws !== "object") return + const next = (socketCounter = (socketCounter + 1) % Number.MAX_SAFE_INTEGER) + sockets.set(ws, next) + return next } - // WebSocket control frame: 0x00 + UTF-8 JSON (currently { cursor }). + // WebSocket control frame: 0x00 + UTF-8 JSON. const meta = (cursor: number) => { const json = JSON.stringify({ cursor }) const bytes = encoder.encode(json) @@ -102,7 +101,7 @@ export namespace Pty { buffer: string bufferCursor: number cursor: number - subscribers: Map + subscribers: Map } const state = Instance.state( @@ -185,13 +184,13 @@ export namespace Pty { ptyProcess.onData((chunk) => { session.cursor += chunk.length - for (const [ws, data] of session.subscribers) { + for (const [ws, sub] of session.subscribers) { if (ws.readyState !== 1) { session.subscribers.delete(ws) continue } - if (token(ws) !== data) { + if (typeof ws === "object" && sockets.get(ws) !== sub.id) { session.subscribers.delete(ws) continue } @@ -280,6 +279,25 @@ export namespace Pty { } log.info("client connected to session", { id }) + const socketId = tagSocket(ws) + if (socketId === undefined) { + ws.close() + return + } + + const previous = owners.get(ws) + if (previous && previous !== id) { + state().get(previous)?.subscribers.delete(ws) + } + + owners.set(ws, id) + session.subscribers.set(ws, { id: socketId }) + + const cleanup = () => { + session.subscribers.delete(ws) + if (owners.get(ws) === id) owners.delete(ws) + } + const start = session.bufferCursor const end = session.cursor @@ -300,6 +318,7 @@ export namespace Pty { ws.send(data.slice(i, i + BUFFER_CHUNK)) } } catch { + cleanup() ws.close() return } @@ -308,23 +327,17 @@ export namespace Pty { try { ws.send(meta(end)) } catch { + cleanup() ws.close() return } - - if (!ws.data || typeof ws.data !== "object") { - ws.close() - return - } - - session.subscribers.set(ws, token(ws)) return { onMessage: (message: string | ArrayBuffer) => { session.process.write(String(message)) }, onClose: () => { log.info("client disconnected from session", { id }) - session.subscribers.delete(ws) + cleanup() }, } } diff --git a/packages/opencode/src/server/routes/pty.ts b/packages/opencode/src/server/routes/pty.ts index d516859f7f76..368c9612bf42 100644 --- a/packages/opencode/src/server/routes/pty.ts +++ b/packages/opencode/src/server/routes/pty.ts @@ -163,18 +163,13 @@ export const PtyRoutes = lazy(() => type Socket = { readyState: number - data: object - send: (data: string | Uint8Array | ArrayBuffer) => void + send: (data: string | Uint8Array | ArrayBuffer) => void close: (code?: number, reason?: string) => void } const isSocket = (value: unknown): value is Socket => { if (!value || typeof value !== "object") return false if (!("readyState" in value)) return false - if (!("data" in value)) return false - if (!((value as { data?: unknown }).data && typeof (value as { data?: unknown }).data === "object")) { - return false - } if (!("send" in value) || typeof (value as { send?: unknown }).send !== "function") return false if (!("close" in value) || typeof (value as { close?: unknown }).close !== "function") return false return typeof (value as { readyState?: unknown }).readyState === "number" @@ -182,12 +177,12 @@ export const PtyRoutes = lazy(() => return { onOpen(_event, ws) { - const raw = ws.raw - if (!isSocket(raw)) { + const socket = ws.raw + if (!isSocket(socket)) { ws.close() return } - handler = Pty.connect(id, raw, cursor) + handler = Pty.connect(id, socket, cursor) }, onMessage(event) { if (typeof event.data !== "string") return diff --git a/packages/opencode/test/pty/pty-output-isolation.test.ts b/packages/opencode/test/pty/pty-output-isolation.test.ts index 337280d18de8..b80d373458f4 100644 --- a/packages/opencode/test/pty/pty-output-isolation.test.ts +++ b/packages/opencode/test/pty/pty-output-isolation.test.ts @@ -18,7 +18,6 @@ describe("pty", () => { const ws = { readyState: 1, - data: { events: { connection: "a" } }, send: (data: unknown) => { outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) }, @@ -31,7 +30,6 @@ describe("pty", () => { Pty.connect(a.id, ws as any) // Now "reuse" the same ws object for another connection. - ws.data = { events: { connection: "b" } } ws.send = (data: unknown) => { outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) } @@ -53,48 +51,4 @@ describe("pty", () => { }, }) }) - - test("does not leak output when Bun recycles websocket objects before re-connect", async () => { - await using dir = await tmpdir({ git: true }) - - await Instance.provide({ - directory: dir.path, - fn: async () => { - const a = await Pty.create({ command: "cat", title: "a" }) - try { - const outA: string[] = [] - const outB: string[] = [] - - const ws = { - readyState: 1, - data: { events: { connection: "a" } }, - send: (data: unknown) => { - outA.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - }, - close: () => { - // no-op (simulate abrupt drop) - }, - } - - // Connect "a" first. - Pty.connect(a.id, ws as any) - outA.length = 0 - - // Simulate Bun reusing the same websocket object for another connection - // before the new onOpen handler has a chance to tag it. - ws.data = { events: { connection: "b" } } - ws.send = (data: unknown) => { - outB.push(typeof data === "string" ? data : Buffer.from(data as Uint8Array).toString("utf8")) - } - - Pty.write(a.id, "AAA\n") - await Bun.sleep(100) - - expect(outB.join("")).not.toContain("AAA") - } finally { - await Pty.remove(a.id) - } - }, - }) - }) }) diff --git a/patches/ghostty-web@0.3.0.patch b/patches/ghostty-web@0.3.0.patch deleted file mode 100644 index d63a693b88bb..000000000000 --- a/patches/ghostty-web@0.3.0.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/dist/ghostty-web.js b/dist/ghostty-web.js -index 7c9d64a617bbeb29d757a1acd54686e582868313..2d61098cdb77fa66cbb162897c5590f35cfcf791 100644 ---- a/dist/ghostty-web.js -+++ b/dist/ghostty-web.js -@@ -1285,7 +1285,7 @@ const e = class H { - continue; - } - const C = g.getCodepoint(); -- C === 0 || C < 32 ? B.push(" ") : B.push(String.fromCodePoint(C)); -+ C === 0 || C < 32 || C > 1114111 || (C >= 55296 && C <= 57343) ? B.push(" ") : B.push(String.fromCodePoint(C)); - } - return B.join(""); - } -@@ -1484,7 +1484,7 @@ class _ { - return; - let J = ""; - A.flags & U.ITALIC && (J += "italic "), A.flags & U.BOLD && (J += "bold "), this.ctx.font = `${J}${this.fontSize}px ${this.fontFamily}`, this.ctx.fillStyle = this.rgbToCSS(w, o, i), A.flags & U.FAINT && (this.ctx.globalAlpha = 0.5); -- const s = g, F = C + this.metrics.baseline, a = String.fromCodePoint(A.codepoint || 32); -+ const s = g, F = C + this.metrics.baseline, a = (A.codepoint === 0 || A.codepoint == null || A.codepoint < 0 || A.codepoint > 1114111 || (A.codepoint >= 55296 && A.codepoint <= 57343)) ? " " : String.fromCodePoint(A.codepoint); - if (this.ctx.fillText(a, s, F), A.flags & U.FAINT && (this.ctx.globalAlpha = 1), A.flags & U.UNDERLINE) { - const N = C + this.metrics.baseline + 2; - this.ctx.strokeStyle = this.ctx.fillStyle, this.ctx.lineWidth = 1, this.ctx.beginPath(), this.ctx.moveTo(g, N), this.ctx.lineTo(g + I, N), this.ctx.stroke(); -@@ -1730,7 +1730,7 @@ const L = class R { - let G = ""; - for (let J = M; J <= k; J++) { - const s = o[J]; -- if (s && s.codepoint !== 0) { -+ if (s && s.codepoint !== 0 && s.codepoint <= 1114111 && !(s.codepoint >= 55296 && s.codepoint <= 57343)) { - const F = String.fromCodePoint(s.codepoint); - G += F, F.trim() && (i = G.length); - } else -@@ -1995,7 +1995,7 @@ const L = class R { - if (!Q) - return null; - const g = (w) => { -- if (!w || w.codepoint === 0) -+ if (!w || w.codepoint === 0 || w.codepoint > 1114111 || (w.codepoint >= 55296 && w.codepoint <= 57343)) - return !1; - const o = String.fromCodePoint(w.codepoint); - return /[\w-]/.test(o); From 885d71636f99074dcc87ba6527f0c9beaba5f623 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 19 Feb 2026 21:14:59 +0800 Subject: [PATCH 252/255] desktop: fetch defaultServer at top level --- packages/app/src/context/platform.tsx | 4 ++-- packages/desktop/src/index.tsx | 16 +++++++++++++--- packages/opencode/package.json | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx index 6d4464258a06..86f3321e4645 100644 --- a/packages/app/src/context/platform.tsx +++ b/packages/app/src/context/platform.tsx @@ -1,5 +1,5 @@ import { createSimpleContext } from "@opencode-ai/ui/context" -import { AsyncStorage, SyncStorage } from "@solid-primitives/storage" +import type { AsyncStorage, SyncStorage } from "@solid-primitives/storage" import type { Accessor } from "solid-js" type PickerPaths = string | string[] | null @@ -58,7 +58,7 @@ export type Platform = { fetch?: typeof fetch /** Get the configured default server URL (platform-specific) */ - getDefaultServerUrl?(): Promise | string | null + getDefaultServerUrl?(): Promise /** Set the default server URL to use on app startup (platform-specific) */ setDefaultServerUrl?(url: string | null): Promise | void diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index f84e1a6a85e1..4e0bb8b200bf 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -426,6 +426,12 @@ void listenForDeepLinks() render(() => { const platform = createPlatform() + const [defaultServer] = createResource(() => + platform.getDefaultServerUrl?.().then((url) => { + if (url) return ServerConnection.key({ type: "http", http: { url } }) + }), + ) + function handleClick(e: MouseEvent) { const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null if (link?.href) { @@ -466,9 +472,13 @@ render(() => { } return ( - - - + + {(defaultServer) => ( + + + + )} + ) }} diff --git a/packages/opencode/package.json b/packages/opencode/package.json index fb2d4d8151bd..21af8f85a549 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -55,7 +55,6 @@ "@actions/core": "1.11.1", "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.14.1", - "@aws-sdk/credential-providers": "3.993.0", "@ai-sdk/amazon-bedrock": "3.0.79", "@ai-sdk/anthropic": "2.0.62", "@ai-sdk/azure": "2.0.91", @@ -75,6 +74,7 @@ "@ai-sdk/togetherai": "1.0.34", "@ai-sdk/vercel": "1.0.33", "@ai-sdk/xai": "2.0.51", + "@aws-sdk/credential-providers": "3.993.0", "@clack/prompts": "1.0.0-alpha.1", "@gitlab/gitlab-ai-provider": "3.6.0", "@gitlab/opencode-gitlab-auth": "1.3.3", @@ -107,8 +107,8 @@ "diff": "catalog:", "drizzle-orm": "1.0.0-beta.12-a5629fb", "fuzzysort": "3.1.0", - "gray-matter": "4.0.3", "google-auth-library": "10.5.0", + "gray-matter": "4.0.3", "hono": "catalog:", "hono-openapi": "catalog:", "ignore": "7.0.5", From d2d5f3c04b09228d2d94e00695de8ca3a4d58a16 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 19 Feb 2026 21:27:44 +0800 Subject: [PATCH 253/255] app: fix typecheck --- packages/app/src/entry.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx index 82e4fc0ebbe2..e9c0a4397c99 100644 --- a/packages/app/src/entry.tsx +++ b/packages/app/src/entry.tsx @@ -106,7 +106,7 @@ const platform: Platform = { forward, restart, notify, - getDefaultServerUrl: readDefaultServerUrl, + getDefaultServerUrl: async () => readDefaultServerUrl(), setDefaultServerUrl: writeDefaultServerUrl, } From 38f7071da95075bce7029eff52ec7153046dd318 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:22:56 -0600 Subject: [PATCH 254/255] chore: cleanup --- packages/ui/src/pierre/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/pierre/index.ts b/packages/ui/src/pierre/index.ts index dc9d857bf871..f226a9ae1361 100644 --- a/packages/ui/src/pierre/index.ts +++ b/packages/ui/src/pierre/index.ts @@ -104,7 +104,8 @@ const unsafeCSS = ` } [data-diff-header], -[data-diff] { +[data-diff], +[data-file] { [data-separator] { height: 24px; } @@ -122,6 +123,7 @@ const unsafeCSS = ` } [data-code] { overflow-x: auto !important; + overflow-y: hidden !important; } }` From 8ebdbe0ea2bbf4b2ca7499d59ff9549d3e291557 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 19 Feb 2026 07:23:37 -0600 Subject: [PATCH 255/255] fix(core): text files missclassified as binary --- packages/opencode/src/file/index.ts | 73 ++++++++++++++++++++++- packages/opencode/test/file/index.test.ts | 60 +++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index bfe120f134c7..d1d24c3645ad 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -166,7 +166,6 @@ export namespace File { "efi", "rom", "com", - "bat", "cmd", "ps1", "sh", @@ -203,11 +202,77 @@ export namespace File { "x3f", ]) + const textExtensions = new Set([ + "ts", + "tsx", + "mts", + "cts", + "mtsx", + "ctsx", + "js", + "jsx", + "mjs", + "cjs", + "sh", + "bash", + "zsh", + "fish", + "ps1", + "psm1", + "cmd", + "bat", + "json", + "jsonc", + "json5", + "yaml", + "yml", + "toml", + "md", + "mdx", + "txt", + "xml", + "html", + "htm", + "css", + "scss", + "sass", + "less", + "graphql", + "gql", + "sql", + "ini", + "cfg", + "conf", + "env", + ]) + + const textNames = new Set([ + "dockerfile", + "makefile", + ".gitignore", + ".gitattributes", + ".editorconfig", + ".npmrc", + ".nvmrc", + ".prettierrc", + ".eslintrc", + ]) + function isImageByExtension(filepath: string): boolean { const ext = path.extname(filepath).toLowerCase().slice(1) return imageExtensions.has(ext) } + function isTextByExtension(filepath: string): boolean { + const ext = path.extname(filepath).toLowerCase().slice(1) + return textExtensions.has(ext) + } + + function isTextByName(filepath: string): boolean { + const name = path.basename(filepath).toLowerCase() + return textNames.has(name) + } + function getImageMimeType(filepath: string): string { const ext = path.extname(filepath).toLowerCase().slice(1) const mimeTypes: Record = { @@ -445,7 +510,9 @@ export namespace File { return { type: "text", content: "" } } - if (isBinaryByExtension(file)) { + const text = isTextByExtension(file) || isTextByName(file) + + if (isBinaryByExtension(file) && !text) { return { type: "binary", content: "" } } @@ -454,7 +521,7 @@ export namespace File { } const mimeType = Filesystem.mimeType(full) - const encode = await shouldEncode(mimeType) + const encode = text ? false : await shouldEncode(mimeType) if (encode && !isImage(mimeType)) { return { type: "binary", content: "", mimeType } diff --git a/packages/opencode/test/file/index.test.ts b/packages/opencode/test/file/index.test.ts index 758886bd5bac..053a64e20d7f 100644 --- a/packages/opencode/test/file/index.test.ts +++ b/packages/opencode/test/file/index.test.ts @@ -283,6 +283,66 @@ describe("file/index Bun.file patterns", () => { }) describe("shouldEncode() logic", () => { + test("treats .ts files as text", async () => { + await using tmp = await tmpdir() + const filepath = path.join(tmp.path, "test.ts") + await fs.writeFile(filepath, "export const value = 1", "utf-8") + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const result = await File.read("test.ts") + expect(result.type).toBe("text") + expect(result.content).toBe("export const value = 1") + }, + }) + }) + + test("treats .mts files as text", async () => { + await using tmp = await tmpdir() + const filepath = path.join(tmp.path, "test.mts") + await fs.writeFile(filepath, "export const value = 1", "utf-8") + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const result = await File.read("test.mts") + expect(result.type).toBe("text") + expect(result.content).toBe("export const value = 1") + }, + }) + }) + + test("treats .sh files as text", async () => { + await using tmp = await tmpdir() + const filepath = path.join(tmp.path, "test.sh") + await fs.writeFile(filepath, "#!/usr/bin/env bash\necho hello", "utf-8") + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const result = await File.read("test.sh") + expect(result.type).toBe("text") + expect(result.content).toBe("#!/usr/bin/env bash\necho hello") + }, + }) + }) + + test("treats Dockerfile as text", async () => { + await using tmp = await tmpdir() + const filepath = path.join(tmp.path, "Dockerfile") + await fs.writeFile(filepath, "FROM alpine:3.20", "utf-8") + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const result = await File.read("Dockerfile") + expect(result.type).toBe("text") + expect(result.content).toBe("FROM alpine:3.20") + }, + }) + }) + test("returns encoding info for text files", async () => { await using tmp = await tmpdir() const filepath = path.join(tmp.path, "test.txt")