diff --git a/.opencode/.gitignore b/.opencode/.gitignore
index 00bfdfda29..03445edaf2 100644
--- a/.opencode/.gitignore
+++ b/.opencode/.gitignore
@@ -1,3 +1,4 @@
plans/
bun.lock
package.json
+package-lock.json
diff --git a/README.zh.md b/README.zh.md
index b11d9857c9..0859ed11d0 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -137,4 +137,4 @@ OpenCode 内置两种 Agent,可用 `Tab` 键快速切换:
---
-**加入我们的社区** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+**加入我们的社区** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=de8k6664-1b5e-43f2-8efd-21d6772647b5&qr_code=true) | [X.com](https://x.com/opencode)
diff --git a/README.zht.md b/README.zht.md
index 573ca85ab4..b7d8b8fc47 100644
--- a/README.zht.md
+++ b/README.zht.md
@@ -137,4 +137,4 @@ OpenCode 內建了兩種 Agent,您可以使用 `Tab` 鍵快速切換。
---
-**加入我們的社群** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+**加入我們的社群** [飞书](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=de8k6664-1b5e-43f2-8efd-21d6772647b5&qr_code=true) | [X.com](https://x.com/opencode)
diff --git a/packages/app/e2e/prompt/prompt-slash-terminal.spec.ts b/packages/app/e2e/prompt/prompt-slash-terminal.spec.ts
index eefce19dc0..bf9f96b47f 100644
--- a/packages/app/e2e/prompt/prompt-slash-terminal.spec.ts
+++ b/packages/app/e2e/prompt/prompt-slash-terminal.spec.ts
@@ -9,14 +9,12 @@ test("/terminal toggles the terminal panel", async ({ page, gotoSession }) => {
await expect(terminal).not.toBeVisible()
- await prompt.click()
- await page.keyboard.type("/terminal")
+ await prompt.fill("/terminal")
await expect(page.locator('[data-slash-id="terminal.toggle"]').first()).toBeVisible()
await page.keyboard.press("Enter")
await expect(terminal).toBeVisible()
- await prompt.click()
- await page.keyboard.type("/terminal")
+ await prompt.fill("/terminal")
await expect(page.locator('[data-slash-id="terminal.toggle"]').first()).toBeVisible()
await page.keyboard.press("Enter")
await expect(terminal).not.toBeVisible()
diff --git a/packages/app/e2e/selectors.ts b/packages/app/e2e/selectors.ts
index 2061a11284..64b7bfe545 100644
--- a/packages/app/e2e/selectors.ts
+++ b/packages/app/e2e/selectors.ts
@@ -1,5 +1,6 @@
export const promptSelector = '[data-component="prompt-input"]'
-export const terminalSelector = '[data-component="terminal"]'
+export const terminalPanelSelector = '#terminal-panel[aria-hidden="false"]'
+export const terminalSelector = `${terminalPanelSelector} [data-component="terminal"]`
export const sessionComposerDockSelector = '[data-component="session-prompt-dock"]'
export const questionDockSelector = '[data-component="dock-prompt"][data-kind="question"]'
export const permissionDockSelector = '[data-component="dock-prompt"][data-kind="permission"]'
diff --git a/packages/console/app/src/component/footer.tsx b/packages/console/app/src/component/footer.tsx
index d81bf32476..0ea370ac78 100644
--- a/packages/console/app/src/component/footer.tsx
+++ b/packages/console/app/src/component/footer.tsx
@@ -8,6 +8,12 @@ import { useI18n } from "~/context/i18n"
export function Footer() {
const language = useLanguage()
const i18n = useI18n()
+ const community = createMemo(() => {
+ const locale = language.locale()
+ return locale === "zh" || locale === "zht"
+ ? ({ key: "footer.feishu", link: language.route("/feishu") } as const)
+ : ({ key: "footer.discord", link: language.route("/discord") } as const)
+ })
const githubData = createAsync(() => github())
const starCount = createMemo(() =>
githubData()?.stars
@@ -32,7 +38,7 @@ export function Footer() {
{i18n.t("footer.changelog")}
{i18n.t("footer.x")}
diff --git a/packages/console/app/src/i18n/en.ts b/packages/console/app/src/i18n/en.ts
index 2a279757b3..02d6126c4a 100644
--- a/packages/console/app/src/i18n/en.ts
+++ b/packages/console/app/src/i18n/en.ts
@@ -21,6 +21,7 @@ export const dict = {
"footer.github": "GitHub",
"footer.docs": "Docs",
"footer.changelog": "Changelog",
+ "footer.feishu": "Feishu",
"footer.discord": "Discord",
"footer.x": "X",
diff --git a/packages/console/app/src/i18n/zh.ts b/packages/console/app/src/i18n/zh.ts
index 87ba1b2450..e12fe77494 100644
--- a/packages/console/app/src/i18n/zh.ts
+++ b/packages/console/app/src/i18n/zh.ts
@@ -24,6 +24,7 @@ export const dict = {
"footer.github": "GitHub",
"footer.docs": "文档",
"footer.changelog": "更新日志",
+ "footer.feishu": "飞书",
"footer.discord": "Discord",
"footer.x": "X",
diff --git a/packages/console/app/src/i18n/zht.ts b/packages/console/app/src/i18n/zht.ts
index b3f1db0124..b45f871566 100644
--- a/packages/console/app/src/i18n/zht.ts
+++ b/packages/console/app/src/i18n/zht.ts
@@ -24,6 +24,7 @@ export const dict = {
"footer.github": "GitHub",
"footer.docs": "文件",
"footer.changelog": "更新日誌",
+ "footer.feishu": "飞书",
"footer.discord": "Discord",
"footer.x": "X",
diff --git a/packages/console/app/src/routes/feishu.ts b/packages/console/app/src/routes/feishu.ts
new file mode 100644
index 0000000000..35d2fcb0e2
--- /dev/null
+++ b/packages/console/app/src/routes/feishu.ts
@@ -0,0 +1,7 @@
+import { redirect } from "@solidjs/router"
+
+export async function GET() {
+ return redirect(
+ "https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=de8k6664-1b5e-43f2-8efd-21d6772647b5&qr_code=true",
+ )
+}
diff --git a/packages/desktop-electron/src/main/cli.ts b/packages/desktop-electron/src/main/cli.ts
index e338d39138..fba301f36c 100644
--- a/packages/desktop-electron/src/main/cli.ts
+++ b/packages/desktop-electron/src/main/cli.ts
@@ -107,7 +107,7 @@ export function syncCli() {
let version = ""
try {
- version = execFileSync(installPath, ["--version"]).toString().trim()
+ version = execFileSync(installPath, ["--version"], { windowsHide: true }).toString().trim()
} catch {
return
}
@@ -147,7 +147,7 @@ export function spawnCommand(args: string, extraEnv: Record
) {
console.log(`[cli] Executing: ${cmd} ${cmdArgs.join(" ")}`)
const child = spawn(cmd, cmdArgs, {
env: envs,
- detached: true,
+ detached: process.platform !== "win32",
windowsHide: true,
stdio: ["ignore", "pipe", "pipe"],
})
diff --git a/packages/opencode/src/account/service.ts b/packages/opencode/src/account/service.ts
index c57bb459a0..ab1de72557 100644
--- a/packages/opencode/src/account/service.ts
+++ b/packages/opencode/src/account/service.ts
@@ -192,11 +192,17 @@ export class AccountService extends ServiceMap.Service<
const orgsByAccount = Effect.fn("AccountService.orgsByAccount")(function* () {
const accounts = yield* repo.list()
- return yield* Effect.forEach(
+ const [errors, results] = yield* Effect.partition(
accounts,
(account) => orgs(account.id).pipe(Effect.map((orgs) => ({ account, orgs }))),
{ concurrency: 3 },
)
+ for (const error of errors) {
+ yield* Effect.logWarning("failed to fetch orgs for account").pipe(
+ Effect.annotateLogs({ error: String(error) }),
+ )
+ }
+ return results
})
const orgs = Effect.fn("AccountService.orgs")(function* (accountID: AccountID) {
diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts
index 9d7d30632a..6ea7554c09 100644
--- a/packages/opencode/src/lsp/index.ts
+++ b/packages/opencode/src/lsp/index.ts
@@ -114,6 +114,7 @@ export namespace LSP {
return {
process: spawn(item.command[0], item.command.slice(1), {
cwd: root,
+ windowsHide: true,
env: {
...process.env,
...item.env,
diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts
index 71f50c94b1..8f93213ea1 100644
--- a/packages/opencode/src/lsp/server.ts
+++ b/packages/opencode/src/lsp/server.ts
@@ -1,4 +1,4 @@
-import { spawn, type ChildProcessWithoutNullStreams } from "child_process"
+import { spawn as launch, type ChildProcessWithoutNullStreams } from "child_process"
import path from "path"
import os from "os"
import { Global } from "../global"
@@ -14,6 +14,11 @@ import { Process } from "../util/process"
import { which } from "../util/which"
import { Module } from "@opencode-ai/util/module"
+const spawn = ((cmd, args, opts) => {
+ if (Array.isArray(args)) return launch(cmd, [...args], { ...(opts ?? {}), windowsHide: true })
+ return launch(cmd, { ...(args ?? {}), windowsHide: true })
+}) as typeof launch
+
export namespace LSPServer {
const log = Log.create({ service: "lsp.server" })
const pathExists = async (p: string) =>
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 6fa617557f..58fdc0f987 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -67,7 +67,11 @@ export namespace Provider {
const project =
options["project"] ?? Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT")
const location =
- options["location"] ?? Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-central1"
+ options["location"] ??
+ Env.get("GOOGLE_VERTEX_LOCATION") ??
+ Env.get("GOOGLE_CLOUD_LOCATION") ??
+ Env.get("VERTEX_LOCATION") ??
+ "us-central1"
const endpoint = location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`
return {
@@ -443,7 +447,11 @@ export namespace Provider {
Env.get("GCLOUD_PROJECT")
const location =
- provider.options?.location ?? Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-central1"
+ provider.options?.location ??
+ Env.get("GOOGLE_VERTEX_LOCATION") ??
+ Env.get("GOOGLE_CLOUD_LOCATION") ??
+ Env.get("VERTEX_LOCATION") ??
+ "us-central1"
const autoload = Boolean(project)
if (!autoload) return { autoload: false }
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 7698b78bab..655afd2b14 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -1629,6 +1629,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
const proc = spawn(shell, args, {
cwd,
detached: process.platform !== "win32",
+ windowsHide: process.platform === "win32",
stdio: ["ignore", "pipe", "pipe"],
env: {
...process.env,
diff --git a/packages/opencode/src/shell/shell.ts b/packages/opencode/src/shell/shell.ts
index 60ae46f5ee..a30889d699 100644
--- a/packages/opencode/src/shell/shell.ts
+++ b/packages/opencode/src/shell/shell.ts
@@ -15,7 +15,10 @@ export namespace Shell {
if (process.platform === "win32") {
await new Promise((resolve) => {
- const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], { stdio: "ignore" })
+ const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
+ stdio: "ignore",
+ windowsHide: true,
+ })
killer.once("exit", () => resolve())
killer.once("error", () => resolve())
})
diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts
index 3be2734863..109a665363 100644
--- a/packages/opencode/src/tool/bash.ts
+++ b/packages/opencode/src/tool/bash.ts
@@ -173,6 +173,7 @@ export const BashTool = Tool.define("bash", async () => {
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
+ windowsHide: process.platform === "win32",
})
let output = ""
diff --git a/packages/opencode/src/util/process.ts b/packages/opencode/src/util/process.ts
index 473ee27dc9..0490969370 100644
--- a/packages/opencode/src/util/process.ts
+++ b/packages/opencode/src/util/process.ts
@@ -60,6 +60,7 @@ export namespace Process {
cwd: opts.cwd,
env: opts.env === null ? {} : opts.env ? { ...process.env, ...opts.env } : undefined,
stdio: [opts.stdin ?? "ignore", opts.stdout ?? "ignore", opts.stderr ?? "ignore"],
+ windowsHide: process.platform === "win32",
})
let closed = false