|
36 | 36 | } |
37 | 37 | ); |
38 | 38 |
|
| 39 | + packages = forEachSystem ( |
| 40 | + system: |
| 41 | + let |
| 42 | + pkgs = import nixpkgs { inherit system; }; |
| 43 | + bun-target = { |
| 44 | + "aarch64-linux" = "bun-linux-arm64"; |
| 45 | + "x86_64-linux" = "bun-linux-x64"; |
| 46 | + }; |
| 47 | + |
| 48 | + models-dev = pkgs.stdenvNoCC.mkDerivation { |
| 49 | + pname = "models-dev"; |
| 50 | + version = "unstable"; |
| 51 | + |
| 52 | + src = pkgs.fetchurl { |
| 53 | + url = "https://models.dev/api.json"; |
| 54 | + hash = "sha256-xQ1FjLTz8g4YbgZZ97j8FrYeuZd9aDUtLB67I23RQDQ="; |
| 55 | + }; |
| 56 | + |
| 57 | + dontUnpack = true; |
| 58 | + dontBuild = true; |
| 59 | + |
| 60 | + installPhase = '' |
| 61 | + mkdir -p $out/dist |
| 62 | + cp $src $out/dist/_api.json |
| 63 | + ''; |
| 64 | + }; |
| 65 | + in |
| 66 | + { |
| 67 | + default = pkgs.callPackage ( |
| 68 | + { |
| 69 | + lib, |
| 70 | + stdenv, |
| 71 | + stdenvNoCC, |
| 72 | + bun, |
| 73 | + makeBinaryWrapper, |
| 74 | + }: |
| 75 | + stdenvNoCC.mkDerivation (finalAttrs: { |
| 76 | + pname = "opencode"; |
| 77 | + version = "1.0.23"; |
| 78 | + |
| 79 | + src = ./.; |
| 80 | + |
| 81 | + node_modules = stdenvNoCC.mkDerivation { |
| 82 | + pname = "opencode-node_modules"; |
| 83 | + inherit (finalAttrs) version src; |
| 84 | + |
| 85 | + impureEnvVars = |
| 86 | + lib.fetchers.proxyImpureEnvVars |
| 87 | + ++ [ |
| 88 | + "GIT_PROXY_COMMAND" |
| 89 | + "SOCKS_SERVER" |
| 90 | + ]; |
| 91 | + |
| 92 | + nativeBuildInputs = [ bun ]; |
| 93 | + |
| 94 | + dontConfigure = true; |
| 95 | + |
| 96 | + buildPhase = '' |
| 97 | + runHook preBuild |
| 98 | + export HOME=$(mktemp -d) |
| 99 | + export BUN_INSTALL_CACHE_DIR=$(mktemp -d) |
| 100 | + bun install \ |
| 101 | + --frozen-lockfile \ |
| 102 | + --ignore-scripts \ |
| 103 | + --no-progress |
| 104 | + runHook postBuild |
| 105 | + ''; |
| 106 | + |
| 107 | + installPhase = '' |
| 108 | + runHook preInstall |
| 109 | + mkdir -p $out |
| 110 | + while IFS= read -r dir; do |
| 111 | + rel="''${dir#./}" |
| 112 | + dest="$out/$rel" |
| 113 | + mkdir -p "$(dirname "$dest")" |
| 114 | + cp -R "$dir" "$dest" |
| 115 | + done < <(find . -type d -name node_modules -prune) |
| 116 | + runHook postInstall |
| 117 | + ''; |
| 118 | + |
| 119 | + dontFixup = true; |
| 120 | + |
| 121 | + outputHashAlgo = "sha256"; |
| 122 | + outputHashMode = "recursive"; |
| 123 | + outputHash = "sha256-S77NbdzNuHALDapU3Qr/lGPwvHCvyGxr+nyVEO9zeBg="; |
| 124 | + }; |
| 125 | + |
| 126 | + nativeBuildInputs = [ |
| 127 | + bun |
| 128 | + makeBinaryWrapper |
| 129 | + ]; |
| 130 | + |
| 131 | + configurePhase = '' |
| 132 | + runHook preConfigure |
| 133 | + cp -R ${finalAttrs.node_modules}/. . |
| 134 | + runHook postConfigure |
| 135 | + ''; |
| 136 | + |
| 137 | + env.MODELS_DEV_API_JSON = "${models-dev}/dist/_api.json"; |
| 138 | + |
| 139 | + buildPhase = '' |
| 140 | + runHook preBuild |
| 141 | +
|
| 142 | + cat > tsconfig.build.json <<'EOF' |
| 143 | + { |
| 144 | + "compilerOptions": { |
| 145 | + "jsx": "preserve", |
| 146 | + "jsxImportSource": "@opentui/solid", |
| 147 | + "allowImportingTsExtensions": true, |
| 148 | + "baseUrl": ".", |
| 149 | + "paths": { |
| 150 | + "@/*": ["./packages/opencode/src/*"], |
| 151 | + "@tui/*": ["./packages/opencode/src/cli/cmd/tui/*"] |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + EOF |
| 156 | +
|
| 157 | + cat > bun-build.ts <<'EOF' |
| 158 | + import solidPlugin from "./packages/opencode/node_modules/@opentui/solid/scripts/solid-plugin" |
| 159 | + import path from "path" |
| 160 | + import fs from "fs" |
| 161 | +
|
| 162 | + const version = "@VERSION@" |
| 163 | + const channel = "@CHANNEL@" |
| 164 | + const repoRoot = process.cwd() |
| 165 | + const packageDir = path.join(repoRoot, "packages/opencode") |
| 166 | +
|
| 167 | + const parserWorker = fs.realpathSync( |
| 168 | + path.join(packageDir, "./node_modules/@opentui/core/parser.worker.js"), |
| 169 | + ) |
| 170 | + const dir = packageDir |
| 171 | + const workerPath = "./src/cli/cmd/tui/worker.ts" |
| 172 | + const target = process.env["BUN_COMPILE_TARGET"] |
| 173 | +
|
| 174 | + if (!target) { |
| 175 | + throw new Error("BUN_COMPILE_TARGET not set") |
| 176 | + } |
| 177 | +
|
| 178 | + // Change to package directory like the original build script does |
| 179 | + process.chdir(packageDir) |
| 180 | +
|
| 181 | + const result = await Bun.build({ |
| 182 | + conditions: ["browser"], |
| 183 | + tsconfig: "./tsconfig.json", |
| 184 | + plugins: [solidPlugin], |
| 185 | + sourcemap: "external", |
| 186 | + entrypoints: ["./src/index.ts", parserWorker, workerPath], |
| 187 | + define: { |
| 188 | + OPENCODE_VERSION: `'@VERSION@'`, |
| 189 | + OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker).replace(/\\/g, "/"), |
| 190 | + OPENCODE_CHANNEL: `'@CHANNEL@'`, |
| 191 | + }, |
| 192 | + compile: { |
| 193 | + target, |
| 194 | + outfile: "opencode", |
| 195 | + execArgv: ["--user-agent=opencode/" + version, "--env-file=\"\"", "--"], |
| 196 | + windows: {}, |
| 197 | + }, |
| 198 | + }) |
| 199 | +
|
| 200 | + if (!result.success) { |
| 201 | + console.error("Build failed!") |
| 202 | + for (const log of result.logs) { |
| 203 | + console.error(log) |
| 204 | + } |
| 205 | + throw new Error("Compilation failed") |
| 206 | + } |
| 207 | +
|
| 208 | + // Nix packaging needs a real file for Worker() lookups, so emit a JS bundle alongside the binary. |
| 209 | + const workerBundle = await Bun.build({ |
| 210 | + entrypoints: [workerPath], |
| 211 | + tsconfig: "./tsconfig.json", |
| 212 | + plugins: [solidPlugin], |
| 213 | + target: "bun", |
| 214 | + outdir: "./.opencode-worker", |
| 215 | + sourcemap: "none", |
| 216 | + }) |
| 217 | +
|
| 218 | + if (!workerBundle.success) { |
| 219 | + console.error("Worker build failed!") |
| 220 | + for (const log of workerBundle.logs) { |
| 221 | + console.error(log) |
| 222 | + } |
| 223 | + throw new Error("Worker compilation failed") |
| 224 | + } |
| 225 | +
|
| 226 | + const workerOutput = workerBundle.outputs.find((output) => output.kind === "entry-point") |
| 227 | + if (!workerOutput) { |
| 228 | + throw new Error("Worker build produced no entry-point output") |
| 229 | + } |
| 230 | +
|
| 231 | + const workerTarget = path.join(packageDir, "opencode-worker.js") |
| 232 | + const workerSource = workerOutput.path |
| 233 | + await Bun.write(workerTarget, Bun.file(workerSource)) |
| 234 | + fs.rmSync(path.dirname(workerSource), { recursive: true, force: true }) |
| 235 | +
|
| 236 | + console.log("Build successful!") |
| 237 | + EOF |
| 238 | +
|
| 239 | + substituteInPlace bun-build.ts \ |
| 240 | + --replace '@VERSION@' "${finalAttrs.version}" \ |
| 241 | + --replace '@CHANNEL@' "latest" |
| 242 | +
|
| 243 | + export BUN_COMPILE_TARGET=${bun-target.${stdenvNoCC.hostPlatform.system}} |
| 244 | + bun --bun bun-build.ts |
| 245 | +
|
| 246 | + runHook postBuild |
| 247 | + ''; |
| 248 | + |
| 249 | + dontStrip = true; |
| 250 | + |
| 251 | + installPhase = '' |
| 252 | + runHook preInstall |
| 253 | +
|
| 254 | + # The binary is created in the package directory after chdir |
| 255 | + cd packages/opencode |
| 256 | + if [ ! -f opencode ]; then |
| 257 | + echo "ERROR: opencode binary not found in $(pwd)" |
| 258 | + ls -la |
| 259 | + exit 1 |
| 260 | + fi |
| 261 | + if [ ! -f opencode-worker.js ]; then |
| 262 | + echo "ERROR: opencode worker bundle not found in $(pwd)" |
| 263 | + ls -la |
| 264 | + exit 1 |
| 265 | + fi |
| 266 | +
|
| 267 | + install -Dm755 opencode $out/bin/opencode |
| 268 | + install -Dm644 opencode-worker.js $out/bin/opencode-worker.js |
| 269 | + runHook postInstall |
| 270 | + ''; |
| 271 | + |
| 272 | + postFixup = lib.optionalString stdenvNoCC.hostPlatform.isLinux '' |
| 273 | + wrapProgram $out/bin/opencode \ |
| 274 | + --set LD_LIBRARY_PATH "${lib.makeLibraryPath [ stdenv.cc.cc.lib ]}" |
| 275 | + ''; |
| 276 | + |
| 277 | + meta = { |
| 278 | + description = "AI coding agent built for the terminal"; |
| 279 | + longDescription = '' |
| 280 | + OpenCode is a terminal-based agent that can build anything. |
| 281 | + It combines a TypeScript/JavaScript core with a Go-based TUI |
| 282 | + to provide an interactive AI coding experience. |
| 283 | + ''; |
| 284 | + homepage = "https://github.com/sst/opencode"; |
| 285 | + license = lib.licenses.mit; |
| 286 | + platforms = [ |
| 287 | + "aarch64-linux" |
| 288 | + "x86_64-linux" |
| 289 | + ]; |
| 290 | + mainProgram = "opencode"; |
| 291 | + }; |
| 292 | + }) |
| 293 | + ) { }; |
| 294 | + } |
| 295 | + ); |
| 296 | + |
39 | 297 | apps = forEachSystem ( |
40 | 298 | system: |
41 | 299 | let |
|
0 commit comments