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.discord")} + {i18n.t(community().key)}
{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