mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-14 13:46:22 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
662d68d6a4 |
@@ -711,12 +711,12 @@
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
@@ -914,7 +914,6 @@
|
||||
"@opencode-ai/merman": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -934,6 +933,7 @@
|
||||
"uqr": "0.1.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
"solid-js": "catalog:",
|
||||
"tree-sitter-bash": "0.25.0",
|
||||
"tree-sitter-powershell": "0.25.10",
|
||||
"uqr": "0.1.3",
|
||||
"web-tree-sitter": "0.25.10",
|
||||
"uqr": "0.1.3",
|
||||
"ws": "8.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
export const simulationExternals = ["@opencode-ai/simulation", "@opencode-ai/simulation/*"]
|
||||
|
||||
export const buildTargets: ReadonlyArray<{
|
||||
readonly os: string
|
||||
readonly arch: "arm64" | "x64"
|
||||
readonly abi?: "musl"
|
||||
readonly avx2?: false
|
||||
}> = [
|
||||
{ os: "linux", arch: "arm64" },
|
||||
{ os: "linux", arch: "x64" },
|
||||
{ os: "linux", arch: "x64", avx2: false },
|
||||
{ os: "linux", arch: "arm64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", avx2: false },
|
||||
{ os: "darwin", arch: "arm64" },
|
||||
{ os: "darwin", arch: "x64" },
|
||||
{ os: "darwin", arch: "x64", avx2: false },
|
||||
{ os: "win32", arch: "arm64" },
|
||||
{ os: "win32", arch: "x64" },
|
||||
{ os: "win32", arch: "x64", avx2: false },
|
||||
]
|
||||
|
||||
export function targetName(item: (typeof buildTargets)[number], binary = "opencode2") {
|
||||
return [
|
||||
binary,
|
||||
item.os === "win32" ? "windows" : item.os,
|
||||
item.arch,
|
||||
item.avx2 === false ? "baseline" : undefined,
|
||||
item.abi,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("-")
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { createSolidTransformPlugin } from "@opentui/solid/bun-plugin"
|
||||
import type { BunPlugin } from "bun"
|
||||
import pkg from "../package.json"
|
||||
import { buildAppArchive } from "./app-assets"
|
||||
import { buildTargets, simulationExternals, targetName } from "./build-config"
|
||||
|
||||
const dir = path.resolve(import.meta.dirname, "..")
|
||||
const binary = "opencode2"
|
||||
@@ -27,36 +28,16 @@ const skipInstall = process.argv.includes("--skip-install")
|
||||
const skipWebUi = process.argv.includes("--skip-web-ui")
|
||||
const solidPlugin = createSolidTransformPlugin()
|
||||
|
||||
const allTargets: {
|
||||
os: string
|
||||
arch: "arm64" | "x64"
|
||||
abi?: "musl"
|
||||
avx2?: false
|
||||
}[] = [
|
||||
{ os: "linux", arch: "arm64" },
|
||||
{ os: "linux", arch: "x64" },
|
||||
{ os: "linux", arch: "x64", avx2: false },
|
||||
{ os: "linux", arch: "arm64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", avx2: false },
|
||||
{ os: "darwin", arch: "arm64" },
|
||||
{ os: "darwin", arch: "x64" },
|
||||
{ os: "darwin", arch: "x64", avx2: false },
|
||||
{ os: "win32", arch: "arm64" },
|
||||
{ os: "win32", arch: "x64" },
|
||||
{ os: "win32", arch: "x64", avx2: false },
|
||||
]
|
||||
|
||||
const targets =
|
||||
requestedTarget !== undefined
|
||||
? allTargets.filter((item) => targetName(item) === requestedTarget)
|
||||
? buildTargets.filter((item) => targetName(item) === requestedTarget)
|
||||
: singleFlag
|
||||
? allTargets.filter((item) => {
|
||||
? buildTargets.filter((item) => {
|
||||
if (item.os !== process.platform || item.arch !== process.arch) return false
|
||||
if (item.avx2 === false) return baselineFlag
|
||||
return item.abi === undefined
|
||||
})
|
||||
: allTargets
|
||||
: buildTargets
|
||||
if (!targets.length) throw new Error(`Unknown build target: ${requestedTarget}`)
|
||||
|
||||
if (!skipInstall) await $`bun install --os="*" --cpu="*" @opentui/core@${pkg.dependencies["@opentui/core"]}`
|
||||
@@ -93,7 +74,7 @@ for (const item of targets) {
|
||||
entrypoints: ["./src/index.ts"],
|
||||
tsconfig: "./tsconfig.json",
|
||||
plugins: [appAssetsPlugin, solidPlugin, parcelWatcherPlugin],
|
||||
external: ["node-gyp"],
|
||||
external: ["node-gyp", ...simulationExternals],
|
||||
format: "esm",
|
||||
minify: true,
|
||||
sourcemap: "inline",
|
||||
@@ -112,6 +93,7 @@ for (const item of targets) {
|
||||
OPENCODE_VERSION: `'${Script.version}'`,
|
||||
OPENCODE_CLI_NAME: `'${binary}'`,
|
||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||
OPENCODE_SIMULATION: "false",
|
||||
OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "undefined",
|
||||
// FFF_LIBC selects the fff native lib variant: "musl" or "gnu".
|
||||
FFF_LIBC: item.os === "linux" ? `'${item.abi ?? "gnu"}'` : "undefined",
|
||||
@@ -140,15 +122,3 @@ for (const item of targets) {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function targetName(item: (typeof allTargets)[number]) {
|
||||
return [
|
||||
binary,
|
||||
item.os === "win32" ? "windows" : item.os,
|
||||
item.arch,
|
||||
item.avx2 === false ? "baseline" : undefined,
|
||||
item.abi,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("-")
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import { Schema } from "effect"
|
||||
import fs from "node:fs/promises"
|
||||
import os from "node:os"
|
||||
import path from "node:path"
|
||||
import { verifyArtifact } from "./verify-artifact"
|
||||
|
||||
const nodeBuild = process.argv.includes("--node")
|
||||
const target = `cli${nodeBuild ? "-node" : ""}-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}`
|
||||
const directory = path.join(import.meta.dir, "..", "dist", ...(nodeBuild ? ["node"] : []), target, "bin")
|
||||
const binary = path.join(directory, `opencode2${nodeBuild ? "-node" : ""}${process.platform === "win32" ? ".exe" : ""}`)
|
||||
if (!(await Bun.file(binary).exists())) throw new Error(`Missing compiled CLI in ${directory}`)
|
||||
await verifyArtifact(binary)
|
||||
|
||||
const root = await fs.realpath(await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-smoke-")))
|
||||
const env = {
|
||||
@@ -29,6 +31,7 @@ const processes: Array<ReturnType<typeof Bun.spawn>> = []
|
||||
const errors: Array<Promise<string>> = []
|
||||
let failure: unknown
|
||||
try {
|
||||
await verifySimulationDiagnostic()
|
||||
await fs.mkdir(path.join(root, ".opencode"))
|
||||
spawnService()
|
||||
spawnService()
|
||||
@@ -104,6 +107,25 @@ if (failure)
|
||||
cause: failure,
|
||||
})
|
||||
|
||||
async function verifySimulationDiagnostic() {
|
||||
const process = Bun.spawn([binary, "--version"], {
|
||||
env: { ...env, OPENCODE_DRIVE: "artifact-smoke" },
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
})
|
||||
const [exit, stdout, stderr] = await Promise.all([
|
||||
process.exited,
|
||||
new Response(process.stdout).text(),
|
||||
new Response(process.stderr).text(),
|
||||
])
|
||||
if (exit === 0) throw new Error("Compiled CLI accepted a simulation request")
|
||||
const output = stdout + stderr
|
||||
if (!output.includes("Simulation is not included in production OpenCode builds"))
|
||||
throw new Error(`Compiled CLI returned an opaque simulation error: ${output}`)
|
||||
if (!output.includes("opencode-drive start --dev <checkout>"))
|
||||
throw new Error(`Compiled CLI omitted the simulation migration command: ${output}`)
|
||||
}
|
||||
|
||||
function spawnService() {
|
||||
const process = Bun.spawn([binary, "serve", "--service"], { env, stdout: "ignore", stderr: "pipe" })
|
||||
processes.push(process)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import path from "node:path"
|
||||
|
||||
export async function verifyArtifact(binary: string) {
|
||||
const contents = Buffer.from(await Bun.file(binary).arrayBuffer())
|
||||
const target = path.basename(path.dirname(path.dirname(binary)))
|
||||
const platform = target.includes("darwin") ? "darwin" : target.includes("linux") ? "linux" : "win32"
|
||||
const markers = [
|
||||
"SimulationPng",
|
||||
"Drive.create",
|
||||
"@napi-rs/canvas",
|
||||
"commit-mono-latin-400-normal",
|
||||
"noto-sans-symbols-symbols-400-normal",
|
||||
"noto-sans-math-math-400-normal",
|
||||
"../simulation/src/",
|
||||
`skia.${platform}-`,
|
||||
]
|
||||
const found = markers.filter((marker) => contents.includes(marker))
|
||||
if (found.length > 0) throw new Error(`Compiled CLI contains simulation artifacts: ${found.join(", ")}`)
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const binary = process.argv[2]
|
||||
if (!binary) throw new Error("Usage: bun run script/verify-artifact.ts <binary>")
|
||||
await verifyArtifact(binary)
|
||||
}
|
||||
@@ -12,6 +12,12 @@ import { Global } from "@opencode-ai/util/global"
|
||||
import { AppProcess } from "@opencode-ai/util/process"
|
||||
import { Config } from "./config"
|
||||
import { Npm } from "@opencode-ai/util/npm"
|
||||
import { SimulationBuild } from "./simulation"
|
||||
|
||||
if (!SimulationBuild.enabled && (process.env.OPENCODE_DRIVE || process.env.OPENCODE_SIMULATE)) {
|
||||
console.error(SimulationBuild.unavailable)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const Handlers = Runtime.handlers(Commands, {
|
||||
$: () => import("./commands/handlers/default"),
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
declare const OPENCODE_SIMULATION: boolean
|
||||
|
||||
export const SimulationBuild = {
|
||||
enabled: typeof OPENCODE_SIMULATION === "boolean" ? OPENCODE_SIMULATION : true,
|
||||
unavailable: "Simulation is not included in production OpenCode builds. Use opencode-drive start --dev <checkout>.",
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { buildTargets, simulationExternals, targetName } from "../script/build-config"
|
||||
|
||||
describe("published Bun build configuration", () => {
|
||||
test("externalizes the simulation package and every subpath", () => {
|
||||
expect(simulationExternals).toEqual(["@opencode-ai/simulation", "@opencode-ai/simulation/*"])
|
||||
})
|
||||
|
||||
test("covers every configured target with the same production build", () => {
|
||||
expect(buildTargets.map((target) => targetName(target))).toEqual([
|
||||
"opencode2-linux-arm64",
|
||||
"opencode2-linux-x64",
|
||||
"opencode2-linux-x64-baseline",
|
||||
"opencode2-linux-arm64-musl",
|
||||
"opencode2-linux-x64-musl",
|
||||
"opencode2-linux-x64-baseline-musl",
|
||||
"opencode2-darwin-arm64",
|
||||
"opencode2-darwin-x64",
|
||||
"opencode2-darwin-x64-baseline",
|
||||
"opencode2-windows-arm64",
|
||||
"opencode2-windows-x64",
|
||||
"opencode2-windows-x64-baseline",
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -253,6 +253,7 @@ export function mainConfig(input: NodeBuildInput): UserConfig {
|
||||
OPENCODE_VERSION: JSON.stringify(input.version),
|
||||
OPENCODE_CLI_NAME: JSON.stringify("opencode2-node"),
|
||||
OPENCODE_CHANNEL: JSON.stringify(input.channel),
|
||||
OPENCODE_SIMULATION: "false",
|
||||
OPENCODE_LIBC: input.target.platform === "linux" ? JSON.stringify("glibc") : "undefined",
|
||||
FFF_LIBC: input.target.platform === "linux" ? JSON.stringify("gnu") : "undefined",
|
||||
},
|
||||
|
||||
@@ -168,6 +168,10 @@ Use `opencode-drive check ./drive.ts` and `start --script` for the Effect-native
|
||||
|
||||
## OpenCode development
|
||||
|
||||
Simulation is available only from an OpenCode development checkout after its
|
||||
normal workspace install. Published `opencode2` and `opencode2-node` binaries do
|
||||
not include the simulation runtime.
|
||||
|
||||
Run this:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -100,7 +100,10 @@ const startCommand = Command.make(
|
||||
record: Flag.boolean("record").pipe(
|
||||
Flag.withDescription("Record the complete headless session and export it on stop"),
|
||||
),
|
||||
dev: Flag.string("dev").pipe(Flag.optional, Flag.withDescription("Path to an OpenCode development checkout")),
|
||||
dev: Flag.string("dev").pipe(
|
||||
Flag.optional,
|
||||
Flag.withDescription("Path to the required OpenCode development checkout"),
|
||||
),
|
||||
},
|
||||
(config) =>
|
||||
executeEffect(
|
||||
@@ -113,16 +116,16 @@ const startCommand = Command.make(
|
||||
Command.withDescription("Launch a local simulated OpenCode instance"),
|
||||
Command.withExamples([
|
||||
{
|
||||
command: "opencode-drive start --name demo",
|
||||
description: "Launch headless OpenCode on the default ports",
|
||||
command: "opencode-drive start --name demo --dev <checkout>",
|
||||
description: "Launch a headless development checkout on the default ports",
|
||||
},
|
||||
{
|
||||
command: "opencode-drive start --visible",
|
||||
description: "Launch visible OpenCode on the default ports",
|
||||
command: "opencode-drive start --visible --dev <checkout>",
|
||||
description: "Launch a visible development checkout on the default ports",
|
||||
},
|
||||
{
|
||||
command: "opencode-drive start --name demo --script ./drive.ts",
|
||||
description: "Launch headless OpenCode and run a script",
|
||||
command: "opencode-drive start --name demo --script ./drive.ts --dev <checkout>",
|
||||
description: "Launch a headless development checkout and run a script",
|
||||
},
|
||||
]),
|
||||
)
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
|
||||
@@ -45,6 +45,8 @@ import { sessionLocationLayer } from "./middleware/session-location"
|
||||
import { ServerInfo } from "./server-info"
|
||||
import type { ServerOptions } from "./options"
|
||||
|
||||
declare const OPENCODE_SIMULATION: boolean
|
||||
|
||||
const applicationServiceNodes = [
|
||||
Global.node,
|
||||
Database.node,
|
||||
@@ -134,6 +136,12 @@ function makeRoutes<AuthError, AuthServices>(
|
||||
const serviceLayer = options.simulation
|
||||
? Layer.unwrap(
|
||||
Effect.gen(function* () {
|
||||
if (typeof OPENCODE_SIMULATION === "boolean" && !OPENCODE_SIMULATION)
|
||||
return yield* Effect.fail(
|
||||
new Error(
|
||||
"Simulation is not included in production OpenCode builds. Use opencode-drive start --dev <checkout>.",
|
||||
),
|
||||
)
|
||||
const { simulationReplacements } = yield* Effect.promise(() => import("@opencode-ai/simulation/backend"))
|
||||
const simulation = yield* simulationReplacements({ version: App.make(options.app).version })
|
||||
return AppNodeBuilder.build(embedded ? embeddedApplicationServices : applicationServices, [
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
"@opencode-ai/merman": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -105,6 +104,7 @@
|
||||
"uqr": "0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/simulation": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
|
||||
@@ -101,6 +101,8 @@ import { AttentionProvider } from "./context/attention"
|
||||
import { StorageProvider } from "./context/storage"
|
||||
import { createTuiClipboard } from "./clipboard"
|
||||
|
||||
declare const OPENCODE_SIMULATION: boolean
|
||||
|
||||
registerOpencodeSpinner()
|
||||
|
||||
const appGlobalBindingCommands = ["session.list", "session.new", "open.menu"] as const
|
||||
@@ -245,8 +247,14 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
||||
)
|
||||
}
|
||||
if (process.env.OPENCODE_DRIVE) {
|
||||
const { Drive } = yield* Effect.promise(() => import("@opencode-ai/simulation/frontend"))
|
||||
return yield* Drive.create(options, input.app.version)
|
||||
if (typeof OPENCODE_SIMULATION === "boolean" && !OPENCODE_SIMULATION)
|
||||
return yield* Effect.fail(
|
||||
new Error(
|
||||
"Simulation is not included in production OpenCode builds. Use opencode-drive start --dev <checkout>.",
|
||||
),
|
||||
)
|
||||
const { create } = yield* Effect.promise(() => import("@opencode-ai/simulation/frontend"))
|
||||
return yield* create(options, input.app.version)
|
||||
}
|
||||
return yield* Effect.acquireRelease(
|
||||
Effect.tryPromise({
|
||||
|
||||
Reference in New Issue
Block a user