Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
12 changes: 6 additions & 6 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
98 changes: 63 additions & 35 deletions packages/desktop/electron.vite.config.test.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand Down Expand Up @@ -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,
)
5 changes: 4 additions & 1 deletion packages/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from "electron-vite"
import { getCurrentCli } from "./scripts/target"

const channel = (() => {
const raw = process.env.OPENCODE_CHANNEL
Expand All @@ -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()
Expand Down
12 changes: 6 additions & 6 deletions packages/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
53 changes: 53 additions & 0 deletions packages/desktop/scripts/target.ts
Original file line number Diff line number Diff line change
@@ -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}`)
}
55 changes: 1 addition & 54 deletions packages/desktop/scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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-"))
Expand Down
Loading