From 8cc0e2012549f3e551c87fa34999c21f12781dea Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 7 Apr 2026 15:20:56 +0800 Subject: [PATCH] remove more bun apis + cut bundle size --- bun.lock | 33 ++++++++++++++++-------- packages/desktop-electron/package.json | 20 ++++++-------- packages/opencode/src/server/instance.ts | 13 ++++++---- packages/opencode/src/server/proxy.ts | 19 ++++++++------ packages/opencode/src/server/router.ts | 2 +- 5 files changed, 50 insertions(+), 37 deletions(-) diff --git a/bun.lock b/bun.lock index 1c6bcd4716..f399347b1b 100644 --- a/bun.lock +++ b/bun.lock @@ -224,12 +224,6 @@ "name": "@opencode-ai/desktop-electron", "version": "1.3.17", "dependencies": { - "@opencode-ai/app": "workspace:*", - "@opencode-ai/ui": "workspace:*", - "@solid-primitives/i18n": "2.2.1", - "@solid-primitives/storage": "catalog:", - "@solidjs/meta": "catalog:", - "@solidjs/router": "0.15.4", "effect": "catalog:", "electron-context-menu": "4.1.2", "electron-log": "^5", @@ -237,20 +231,34 @@ "electron-updater": "^6", "electron-window-state": "^5.0.3", "marked": "^15", - "solid-js": "catalog:", - "tree-kill": "^1.2.2", }, "devDependencies": { "@actions/artifact": "4.0.0", + "@lydell/node-pty": "catalog:", + "@opencode-ai/app": "workspace:*", + "@opencode-ai/ui": "workspace:*", + "@solid-primitives/i18n": "2.2.1", + "@solid-primitives/storage": "catalog:", + "@solidjs/meta": "catalog:", + "@solidjs/router": "0.15.4", "@types/bun": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", "electron": "40.4.1", "electron-builder": "^26", "electron-vite": "^5", + "solid-js": "catalog:", "typescript": "~5.6.2", "vite": "catalog:", }, + "optionalDependencies": { + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.10", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.10", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.10", + "@lydell/node-pty-linux-x64": "1.2.0-beta.10", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.10", + "@lydell/node-pty-win32-x64": "1.2.0-beta.10", + }, }, "packages/enterprise": { "name": "@opencode-ai/enterprise", @@ -335,7 +343,7 @@ "@hono/node-ws": "1.3.0", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", - "@lydell/node-pty": "1.2.0-beta.10", + "@lydell/node-pty": "catalog:", "@modelcontextprotocol/sdk": "1.27.1", "@npmcli/arborist": "9.4.0", "@octokit/graphql": "9.0.2", @@ -631,6 +639,7 @@ "@effect/platform-node": "4.0.0-beta.43", "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", + "@lydell/node-pty": "1.2.0-beta.10", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", "@pierre/diffs": "1.1.0-beta.18", @@ -2311,6 +2320,8 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="], + "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], @@ -4651,8 +4662,6 @@ "traverse": ["traverse@0.3.9", "", {}, "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ=="], - "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], - "tree-sitter-bash": ["tree-sitter-bash@0.25.0", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-gZtlj9+qFS81qKxpLfD6H0UssQ3QBc/F0nKkPsiFDyfQF2YBqYvglFJUzchrPpVhZe9kLZTrJ9n2J6lmka69Vg=="], "tree-sitter-powershell": ["tree-sitter-powershell@0.25.10", "", { "dependencies": { "node-addon-api": "^7.1.0", "node-gyp-build": "^4.8.0" }, "peerDependencies": { "tree-sitter": "^0.25.0" }, "optionalPeers": ["tree-sitter"] }, "sha512-bEt8QoySpGFnU3aa8WedQyNMaN6aTwy/WUbvIVt0JSKF+BbJoSHNHu+wCbhj7xLMsfB0AuffmiJm+B8gzva8Lg=="], @@ -4807,6 +4816,8 @@ "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + "valibot": ["valibot@1.3.1", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg=="], + "validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index e4c05f0b3e..fdbcf38a87 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -24,34 +24,30 @@ }, "main": "./out/main/index.js", "dependencies": { - "@opencode-ai/app": "workspace:*", - "@opencode-ai/ui": "workspace:*", - "@solid-primitives/i18n": "2.2.1", - "@solid-primitives/storage": "catalog:", - "@solidjs/meta": "catalog:", - "@solidjs/router": "0.15.4", - "@valibot/to-json-schema": "1.6.0", "effect": "catalog:", "electron-context-menu": "4.1.2", "electron-log": "^5", "electron-store": "^10", "electron-updater": "^6", "electron-window-state": "^5.0.3", - "marked": "^15", - "solid-js": "catalog:", - "sury": "11.0.0-alpha.4", - "tree-kill": "^1.2.2", - "zod-openapi": "5.4.6" + "marked": "^15" }, "devDependencies": { "@actions/artifact": "4.0.0", "@lydell/node-pty": "catalog:", + "@opencode-ai/app": "workspace:*", + "@opencode-ai/ui": "workspace:*", + "@solid-primitives/i18n": "2.2.1", + "@solid-primitives/storage": "catalog:", + "@solidjs/meta": "catalog:", + "@solidjs/router": "0.15.4", "@types/bun": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", "electron": "40.4.1", "electron-builder": "^26", "electron-vite": "^5", + "solid-js": "catalog:", "typescript": "~5.6.2", "vite": "catalog:" }, diff --git a/packages/opencode/src/server/instance.ts b/packages/opencode/src/server/instance.ts index 7cc7886b04..503bad966b 100644 --- a/packages/opencode/src/server/instance.ts +++ b/packages/opencode/src/server/instance.ts @@ -4,6 +4,7 @@ import { proxy } from "hono/proxy" import type { UpgradeWebSocket } from "hono/ws" import z from "zod" import { createHash } from "node:crypto" +import * as fs from "node:fs/promises" import { Log } from "../util/log" import { Format } from "../format" import { TuiRoutes } from "./routes/tui" @@ -28,6 +29,7 @@ import { ExperimentalRoutes } from "./routes/experimental" import { ProviderRoutes } from "./routes/provider" import { EventRoutes } from "./routes/event" import { errorHandler } from "./middleware" +import { getMimeType } from "hono/utils/mime" const log = Log.create({ service: "server" }) @@ -285,13 +287,14 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket, app: Hono = new Hono() if (embeddedWebUI) { const match = embeddedWebUI[path.replace(/^\//, "")] ?? embeddedWebUI["index.html"] ?? null if (!match) return c.json({ error: "Not Found" }, 404) - const file = Bun.file(match) - if (await file.exists()) { - c.header("Content-Type", file.type) - if (file.type.startsWith("text/html")) { + + if (await fs.exists(match)) { + const mime = getMimeType(match) ?? "text/plain" + c.header("Content-Type", mime) + if (mime.startsWith("text/html")) { c.header("Content-Security-Policy", DEFAULT_CSP) } - return c.body(await file.arrayBuffer()) + return c.body(new Uint8Array(await fs.readFile(match))) } else { return c.json({ error: "Not Found" }, 404) } diff --git a/packages/opencode/src/server/proxy.ts b/packages/opencode/src/server/proxy.ts index c489c6b42b..c90a657dc2 100644 --- a/packages/opencode/src/server/proxy.ts +++ b/packages/opencode/src/server/proxy.ts @@ -1,7 +1,6 @@ import type { Target } from "@/control-plane/types" -import { lazy } from "@/util/lazy" import { Hono } from "hono" -import { upgradeWebSocket } from "hono/bun" +import type { UpgradeWebSocket } from "hono/ws" const hop = new Set([ "connection", @@ -53,10 +52,10 @@ function send(ws: { send(data: string | ArrayBuffer | Uint8Array): void }, data: return ws.send(data) } -const app = lazy(() => +const app = (upgrade: UpgradeWebSocket) => new Hono().get( "/__workspace_ws", - upgradeWebSocket((c) => { + upgrade((c) => { const url = c.req.header("x-opencode-proxy-url") const queue: Msg[] = [] let remote: WebSocket | undefined @@ -96,8 +95,7 @@ const app = lazy(() => }, } }), - ), -) + ) export namespace ServerProxy { export function http(target: Extract, req: Request) { @@ -112,13 +110,18 @@ export namespace ServerProxy { ) } - export function websocket(target: Extract, req: Request, env: unknown) { + export function websocket( + upgrade: UpgradeWebSocket, + target: Extract, + req: Request, + env: unknown, + ) { const url = new URL(req.url) url.pathname = "/__workspace_ws" url.search = "" const next = new Headers(req.headers) next.set("x-opencode-proxy-url", socket(target.url)) - return app().fetch( + return app(upgrade).fetch( new Request(url, { method: req.method, headers: next, diff --git a/packages/opencode/src/server/router.ts b/packages/opencode/src/server/router.ts index b6f99ec73b..55853d974d 100644 --- a/packages/opencode/src/server/router.ts +++ b/packages/opencode/src/server/router.ts @@ -89,7 +89,7 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware } if (c.req.header("upgrade")?.toLowerCase() === "websocket") { - return ServerProxy.websocket(target, c.req.raw, c.env) + return ServerProxy.websocket(upgrade, target, c.req.raw, c.env) } const headers = new Headers(c.req.raw.headers)