diff --git a/bun.lock b/bun.lock index e43b4fd8a814..d14111f8d6ee 100644 --- a/bun.lock +++ b/bun.lock @@ -143,12 +143,12 @@ "@ff-labs/fff-bin-linux-x64-gnu": "0.10.5", "@ff-labs/fff-bin-win32-arm64": "0.10.5", "@ff-labs/fff-bin-win32-x64": "0.10.5", - "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", - "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", - "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", - "@lydell/node-pty-linux-x64": "1.2.0-beta.12", - "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", - "@lydell/node-pty-win32-x64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.15", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.15", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.15", + "@lydell/node-pty-linux-x64": "1.2.0-beta.15", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.15", + "@lydell/node-pty-win32-x64": "1.2.0-beta.15", "@opencode-ai/protocol": "workspace:*", "@opencode-ai/script": "workspace:*", "@parcel/watcher-darwin-arm64": "2.5.1", @@ -440,12 +440,12 @@ "vite": "8.2.2", }, "optionalDependencies": { - "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", - "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", - "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", - "@lydell/node-pty-linux-x64": "1.2.0-beta.12", - "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", - "@lydell/node-pty-win32-x64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.15", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.15", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.15", + "@lydell/node-pty-linux-x64": "1.2.0-beta.15", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.15", + "@lydell/node-pty-win32-x64": "1.2.0-beta.15", "msgpackr-extract": "3.0.4", }, }, @@ -1068,7 +1068,7 @@ "@hono/standard-validator": "0.2.0", "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.13", - "@lydell/node-pty": "1.2.0-beta.12", + "@lydell/node-pty": "1.2.0-beta.15", "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", @@ -1953,19 +1953,19 @@ "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], - "@lydell/node-pty": ["@lydell/node-pty@1.2.0-beta.12", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", "@lydell/node-pty-linux-x64": "1.2.0-beta.12", "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", "@lydell/node-pty-win32-x64": "1.2.0-beta.12" } }, "sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g=="], + "@lydell/node-pty": ["@lydell/node-pty@1.2.0-beta.15", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.15", "@lydell/node-pty-darwin-x64": "1.2.0-beta.15", "@lydell/node-pty-linux-arm64": "1.2.0-beta.15", "@lydell/node-pty-linux-x64": "1.2.0-beta.15", "@lydell/node-pty-win32-arm64": "1.2.0-beta.15", "@lydell/node-pty-win32-x64": "1.2.0-beta.15" } }, "sha512-Br8wBxzbxFwdWgk9uQ+rdzE0xfoxOK4QuGH54swhRwc5IxP6H9Y1/bcyazRGvNUs6XkB5qNVkezuKSRxUwZe7A=="], - "@lydell/node-pty-darwin-arm64": ["@lydell/node-pty-darwin-arm64@1.2.0-beta.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ=="], + "@lydell/node-pty-darwin-arm64": ["@lydell/node-pty-darwin-arm64@1.2.0-beta.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6TSBbzdcLiNTHl1mTuzflqXrkmcC36USVGvERoDgvHk2ItEDaMaFZuAJ1CqPmwYj0DyhCS16TVS8OGK9xZnjyQ=="], - "@lydell/node-pty-darwin-x64": ["@lydell/node-pty-darwin-x64@1.2.0-beta.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q=="], + "@lydell/node-pty-darwin-x64": ["@lydell/node-pty-darwin-x64@1.2.0-beta.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-yDT2oqPqYMBScyuk1U9Rg5VKcrbMOD9o9jWYYamDADA3NSbUISroPChrqYRQ74Y7BQtNH4gqYAiWOZRi5uQZ0Q=="], - "@lydell/node-pty-linux-arm64": ["@lydell/node-pty-linux-arm64@1.2.0-beta.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw=="], + "@lydell/node-pty-linux-arm64": ["@lydell/node-pty-linux-arm64@1.2.0-beta.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-wkbNF7dYAmtJv+o2+iztVlNwnUB4B0uX0wh/UD+mwMcmE2gNMnW9GChXO7fEE5XJokD0vB5idiHpGegaN+G/sg=="], - "@lydell/node-pty-linux-x64": ["@lydell/node-pty-linux-x64@1.2.0-beta.12", "", { "os": "linux", "cpu": "x64" }, "sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ=="], + "@lydell/node-pty-linux-x64": ["@lydell/node-pty-linux-x64@1.2.0-beta.15", "", { "os": "linux", "cpu": "x64" }, "sha512-+U/5AVvHT6W+8OCYcnJgN0Qgc0ycO3TfD6aaFJHK+WHij797f8gsi5dV1HEO9l6YQmWCD+VL5gaLDhx3mxHwCA=="], - "@lydell/node-pty-win32-arm64": ["@lydell/node-pty-win32-arm64@1.2.0-beta.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g=="], + "@lydell/node-pty-win32-arm64": ["@lydell/node-pty-win32-arm64@1.2.0-beta.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-pyAk91w7wnnKrD4mrHXtIXRfmzSWV5bEzvRhurXcMCtCc2TJ424ciUskIgWMhAPP6y3KyUnqElj+U6kY3iOt0A=="], - "@lydell/node-pty-win32-x64": ["@lydell/node-pty-win32-x64@1.2.0-beta.12", "", { "os": "win32", "cpu": "x64" }, "sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw=="], + "@lydell/node-pty-win32-x64": ["@lydell/node-pty-win32-x64@1.2.0-beta.15", "", { "os": "win32", "cpu": "x64" }, "sha512-2f8twEmDVxZ7drchAXjtevpmSPhFok0avAnzXro4t5gmz0xsPNKkoZvymwtuIS3xo7PzQqZOPQ/YzwEMb7oIzQ=="], "@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="], diff --git a/package.json b/package.json index 5e69c93016e0..daa3eeee7e8f 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "solid-js": "1.9.10", "solid-sonner": "0.3.1", "vite-plugin-solid": "2.11.10", - "@lydell/node-pty": "1.2.0-beta.12" + "@lydell/node-pty": "1.2.0-beta.15" } }, "devDependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index deac1fa518d5..84f605e31ed3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -53,12 +53,12 @@ "@tsconfig/bun": "catalog:", "@types/bun": "catalog:", "@typescript/native-preview": "catalog:", - "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", - "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", - "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", - "@lydell/node-pty-linux-x64": "1.2.0-beta.12", - "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", - "@lydell/node-pty-win32-x64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.15", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.15", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.15", + "@lydell/node-pty-linux-x64": "1.2.0-beta.15", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.15", + "@lydell/node-pty-win32-x64": "1.2.0-beta.15", "@ff-labs/fff-bin-darwin-arm64": "0.10.5", "@ff-labs/fff-bin-linux-arm64-gnu": "0.10.5", "@ff-labs/fff-bin-linux-x64-gnu": "0.10.5", diff --git a/packages/desktop/electron.vite.config.test.ts b/packages/desktop/electron.vite.config.test.ts index 0abdff38f496..0ea5bdca6604 100644 --- a/packages/desktop/electron.vite.config.test.ts +++ b/packages/desktop/electron.vite.config.test.ts @@ -1,7 +1,17 @@ -import { expect, test } from "bun:test" +import { afterEach, expect, test } from "bun:test" import { loadConfigFromFile, MainConfigFactory } from "electron-vite" import { build } from "vite" import pkg from "./package.json" +import { getCurrentCli } from "./scripts/target" + +const originalTarget = process.env.OPENCODE_CLI_TARGET +afterEach(() => { + if (originalTarget === undefined) { + delete process.env.OPENCODE_CLI_TARGET + return + } + process.env.OPENCODE_CLI_TARGET = originalTarget +}) test.each(["build", "serve"] as const)("configures minification for %s", async (command) => { const result = await loadConfigFromFile( @@ -43,37 +53,55 @@ test("keeps PTY binaries without stale native packaging", () => { ]) }) -test("bundles one Effect runtime and Drizzle while keeping native dependencies external", async () => { - const result = await loadConfigFromFile( - { command: "build", mode: "production" }, - `${import.meta.dirname}/electron.vite.config.ts`, - ) - if (!result.config.main) throw new Error("Missing main-process build configuration") - const config = await new MainConfigFactory( - result.config.main, - { configFile: false, mode: "production" }, - { root: import.meta.dirname }, - ).build() - config.build = { ...config.build, write: false } - config.logLevel = "silent" - const output = await build(config) - const chunks = (Array.isArray(output) ? output : [output]).flatMap((result) => - "output" in result ? result.output.filter((item) => item.type === "chunk") : [], - ) - expect(chunks.length).toBeGreaterThan(0) - const imports = chunks.flatMap((chunk) => [...chunk.imports, ...chunk.dynamicImports]) - const modules = chunks.flatMap((chunk) => Object.keys(chunk.modules)) - for (const name of ["effect", "@effect/platform-node", "@effect/platform-node-shared", "drizzle-orm"]) { - expect(imports.filter((id) => id === name || id.startsWith(`${name}/`))).toEqual([]) - expect(modules.some((id) => id.includes(`/node_modules/${name}/`))).toBe(true) - } - const effect = modules.filter((id) => id.includes("/node_modules/effect/")) - expect(new Set(effect.map((id) => id.split("/node_modules/effect/")[0])).size).toBe(1) - expect(new Set(effect).size).toBe(effect.length) - expect(imports).toContain("electron") - expect(imports).toContain("node:sqlite") - expect(chunks.some((chunk) => chunk.dynamicImports.includes("@zip.js/zip.js"))).toBe(true) - expect(imports).toContain(`@lydell/node-pty-${process.platform}-${process.arch}`) - expect(modules.some((id) => id.includes("/node_modules/msgpackr-extract/"))).toBe(false) - expect(chunks.some((chunk) => chunk.code.includes("msgpackr-extract"))).toBe(true) -}, 30_000) +test("rejects unsupported packaging targets", () => { + expect(() => getCurrentCli("unsupported-target")).toThrow("CLI configuration not available") +}) + +test.each([ + [undefined, `${process.platform}-${process.arch}`], + ["aarch64-apple-darwin", "darwin-arm64"], + ["x86_64-apple-darwin", "darwin-x64"], + ["aarch64-pc-windows-msvc", "win32-arm64"], + ["x86_64-pc-windows-msvc", "win32-x64"], + ["aarch64-unknown-linux-gnu", "linux-arm64"], + ["x86_64-unknown-linux-gnu", "linux-x64"], +])( + "bundles shared runtimes and external PTY bindings for %s (%s)", + async (target, binding) => { + process.env.OPENCODE_CLI_TARGET = target + if (target === undefined) delete process.env.OPENCODE_CLI_TARGET + const result = await loadConfigFromFile( + { command: "build", mode: "production" }, + `${import.meta.dirname}/electron.vite.config.ts`, + ) + if (!result.config.main) throw new Error("Missing main-process build configuration") + const config = await new MainConfigFactory( + result.config.main, + { configFile: false, mode: "production" }, + { root: import.meta.dirname }, + ).build() + config.build = { ...config.build, write: false } + config.logLevel = "silent" + const output = await build(config) + const chunks = (Array.isArray(output) ? output : [output]).flatMap((result) => + "output" in result ? result.output.filter((item) => item.type === "chunk") : [], + ) + expect(chunks.length).toBeGreaterThan(0) + const imports = chunks.flatMap((chunk) => [...chunk.imports, ...chunk.dynamicImports]) + const modules = chunks.flatMap((chunk) => Object.keys(chunk.modules)) + for (const name of ["effect", "@effect/platform-node", "@effect/platform-node-shared", "drizzle-orm"]) { + expect(imports.filter((id) => id === name || id.startsWith(`${name}/`))).toEqual([]) + expect(modules.some((id) => id.includes(`/node_modules/${name}/`))).toBe(true) + } + const effect = modules.filter((id) => id.includes("/node_modules/effect/")) + expect(new Set(effect.map((id) => id.split("/node_modules/effect/")[0])).size).toBe(1) + expect(new Set(effect).size).toBe(effect.length) + expect(imports).toContain("electron") + expect(imports).toContain("node:sqlite") + expect(chunks.some((chunk) => chunk.dynamicImports.includes("@zip.js/zip.js"))).toBe(true) + expect(imports.filter((id) => id.startsWith("@lydell/node-pty"))).toEqual([`@lydell/node-pty-${binding}`]) + expect(modules.some((id) => id.includes("/node_modules/msgpackr-extract/"))).toBe(false) + expect(chunks.some((chunk) => chunk.code.includes("msgpackr-extract"))).toBe(true) + }, + 30_000, +) diff --git a/packages/desktop/electron.vite.config.ts b/packages/desktop/electron.vite.config.ts index d62fe67e3c52..dcfc82ae20da 100644 --- a/packages/desktop/electron.vite.config.ts +++ b/packages/desktop/electron.vite.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "electron-vite" +import { getCurrentCli } from "./scripts/target" const channel = (() => { const raw = process.env.OPENCODE_CHANNEL @@ -7,7 +8,9 @@ const channel = (() => { return "dev" })() -const nodePtyPkg = `@lydell/node-pty-${process.platform}-${process.arch}` +// Windows ARM64 releases are bundled on x64 hosts. +const target = getCurrentCli() +const nodePtyPkg = `@lydell/node-pty-${target.os}-${target.cpu}` const appPlugin = (await import("@opencode-ai/app/vite")).default const picker = (await import("@brendonovich/vite-plugin-opencode")).default() diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ecb8b11ed4e0..e1feddfa3973 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -54,12 +54,12 @@ "vite": "8.2.2" }, "optionalDependencies": { - "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", - "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", - "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", - "@lydell/node-pty-linux-x64": "1.2.0-beta.12", - "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", - "@lydell/node-pty-win32-x64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.15", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.15", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.15", + "@lydell/node-pty-linux-x64": "1.2.0-beta.15", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.15", + "@lydell/node-pty-win32-x64": "1.2.0-beta.15", "msgpackr-extract": "3.0.4" } } diff --git a/packages/desktop/scripts/target.ts b/packages/desktop/scripts/target.ts new file mode 100644 index 000000000000..9ae4e90cc1b6 --- /dev/null +++ b/packages/desktop/scripts/target.ts @@ -0,0 +1,53 @@ +const CLI_BINARIES: Array<{ target: string; package: string; os: string; cpu: string }> = [ + { + target: "aarch64-apple-darwin", + package: "@opencode-ai/cli-darwin-arm64", + os: "darwin", + cpu: "arm64", + }, + { + target: "x86_64-apple-darwin", + package: "@opencode-ai/cli-darwin-x64-baseline", + os: "darwin", + cpu: "x64", + }, + { + target: "aarch64-pc-windows-msvc", + package: "@opencode-ai/cli-windows-arm64", + os: "win32", + cpu: "arm64", + }, + { + target: "x86_64-pc-windows-msvc", + package: "@opencode-ai/cli-windows-x64-baseline", + os: "win32", + cpu: "x64", + }, + { + target: "x86_64-unknown-linux-gnu", + package: "@opencode-ai/cli-linux-x64-baseline", + os: "linux", + cpu: "x64", + }, + { + target: "aarch64-unknown-linux-gnu", + package: "@opencode-ai/cli-linux-arm64", + os: "linux", + cpu: "arm64", + }, +] + +export function getCurrentCli(target = process.env.OPENCODE_CLI_TARGET ?? nativeTarget()) { + const binaryConfig = CLI_BINARIES.find((item) => item.target === target) + if (!binaryConfig) throw new Error(`CLI configuration not available for target '${target}'`) + + return binaryConfig +} + +function nativeTarget() { + const { platform, arch } = process + if (platform === "darwin") return arch === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin" + if (platform === "win32") return arch === "arm64" ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc" + if (platform === "linux") return arch === "arm64" ? "aarch64-unknown-linux-gnu" : "x86_64-unknown-linux-gnu" + throw new Error(`Unsupported platform: ${platform}/${arch}`) +} diff --git a/packages/desktop/scripts/utils.ts b/packages/desktop/scripts/utils.ts index ad356fce50d5..062598a8ad60 100644 --- a/packages/desktop/scripts/utils.ts +++ b/packages/desktop/scripts/utils.ts @@ -2,6 +2,7 @@ import { $ } from "bun" import { chmod, copyFile, mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" +import { getCurrentCli } from "./target" const CLI_VERSION = "dev" @@ -13,62 +14,8 @@ export function resolveChannel(): Channel { return "dev" } -export const CLI_BINARIES: Array<{ target: string; package: string; os: string; cpu: string }> = [ - { - target: "aarch64-apple-darwin", - package: "@opencode-ai/cli-darwin-arm64", - os: "darwin", - cpu: "arm64", - }, - { - target: "x86_64-apple-darwin", - package: "@opencode-ai/cli-darwin-x64-baseline", - os: "darwin", - cpu: "x64", - }, - { - target: "aarch64-pc-windows-msvc", - package: "@opencode-ai/cli-windows-arm64", - os: "win32", - cpu: "arm64", - }, - { - target: "x86_64-pc-windows-msvc", - package: "@opencode-ai/cli-windows-x64-baseline", - os: "win32", - cpu: "x64", - }, - { - target: "x86_64-unknown-linux-gnu", - package: "@opencode-ai/cli-linux-x64-baseline", - os: "linux", - cpu: "x64", - }, - { - target: "aarch64-unknown-linux-gnu", - package: "@opencode-ai/cli-linux-arm64", - os: "linux", - cpu: "arm64", - }, -] - export const CLI_TARGET = Bun.env.OPENCODE_CLI_TARGET -function nativeTarget() { - const { platform, arch } = process - if (platform === "darwin") return arch === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin" - if (platform === "win32") return arch === "arm64" ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc" - if (platform === "linux") return arch === "arm64" ? "aarch64-unknown-linux-gnu" : "x86_64-unknown-linux-gnu" - throw new Error(`Unsupported platform: ${platform}/${arch}`) -} - -export function getCurrentCli(target = CLI_TARGET ?? nativeTarget()) { - const binaryConfig = CLI_BINARIES.find((item) => item.target === target) - if (!binaryConfig) throw new Error(`CLI configuration not available for target '${target}'`) - - return binaryConfig -} - export async function downloadCliToResources(version = CLI_VERSION, dest = windowsify("resources/opencode-cli")) { const cli = getCurrentCli() const directory = await mkdtemp(join(tmpdir(), "opencode-cli-"))