{ lib , stdenvNoCC , bun , fetchFromGitHub , fzf , makeBinaryWrapper , models-dev , nix-update-script , ripgrep , testers , writableTmpDirAsHomeHook , }: let pname = "opencode"; version = "1.0.170"; src = fetchFromGitHub { owner = "sst"; repo = "opencode"; tag = "v${version}"; hash = "sha256-Y0thIZ20p0FSBAH0mJfFn8e+OEUvlZyTuk+/yEt8Sy8="; }; node_modules = stdenvNoCC.mkDerivation { pname = "${pname}-node_modules"; inherit version src; impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [ "GIT_PROXY_COMMAND" "SOCKS_SERVER" ]; nativeBuildInputs = [ bun writableTmpDirAsHomeHook ]; dontConfigure = true; buildPhase = '' runHook preBuild export BUN_INSTALL_CACHE_DIR=$(mktemp -d) bun install \ --cpu="*" \ --filter=./packages/opencode \ --force \ --frozen-lockfile \ --ignore-scripts \ --no-progress \ --os="*" \ --production bun run ./nix/scripts/canonicalize-node-modules.ts bun run ./nix/scripts/normalize-bun-binaries.ts runHook postBuild ''; installPhase = '' runHook preInstall mkdir -p $out while IFS= read -r dir; do rel="''${dir#./}" dest="$out/$rel" mkdir -p "$(dirname "$dest")" cp -R "$dir" "$dest" done < <(find . -type d -name node_modules -prune | sort) runHook postInstall ''; # NOTE: Required else we get errors that our fixed-output derivation references store paths dontFixup = true; outputHash = "sha256-Aq774bgU12HkrF2oAtfu9kyQFlxUeDbmwlS9lz4Z4ZI="; outputHashAlgo = "sha256"; outputHashMode = "recursive"; }; in stdenvNoCC.mkDerivation (finalAttrs: { inherit pname version src node_modules ; nativeBuildInputs = [ bun makeBinaryWrapper models-dev ]; patches = [ # NOTE: Relax Bun version check to be a warning instead of an error ./relax-bun-version-check.patch ]; configurePhase = '' runHook preConfigure cp -R ${node_modules}/. . runHook postConfigure ''; env.MODELS_DEV_API_JSON = "${models-dev}/dist/_api.json"; env.OPENCODE_VERSION = finalAttrs.version; env.OPENCODE_CHANNEL = "stable"; preBuild = '' chmod -R u+w ./packages/opencode/node_modules pushd ./packages/opencode/node_modules/@parcel/ for pkg in ../../../../node_modules/.bun/@parcel+watcher-*; do linkName=$(basename "$pkg" | sed 's/@.*+\(.*\)@.*/\1/') ln -sf "$pkg/node_modules/@parcel/$linkName" "$linkName" done popd pushd ./packages/opencode/node_modules/@opentui/ for pkg in ../../../../node_modules/.bun/@opentui+core-*; do linkName=$(basename "$pkg" | sed 's/@.*+\(.*\)@.*/\1/') ln -sf "$pkg/node_modules/@opentui/$linkName" "$linkName" done popd ''; buildPhase = '' runHook preBuild cd ./packages/opencode cp ${./bundle.ts} ./bundle.ts bun run ./bundle.ts runHook postBuild ''; dontStrip = true; installPhase = '' runHook preInstall mkdir -p $out/lib/opencode # Copy the bundled dist directory cp -r dist $out/lib/opencode/ # Fix WASM paths in worker.ts - use absolute paths to the installed location # Main wasm is tree-sitter-.wasm, language wasms are tree-sitter--.wasm main_wasm=$(find "$out/lib/opencode/dist" -maxdepth 1 -name 'tree-sitter-[a-z0-9]*.wasm' -print -quit) substituteInPlace $out/lib/opencode/dist/worker.ts \ --replace-fail 'module2.exports = "../../../tree-sitter-' 'module2.exports = "'"$out"'/lib/opencode/dist/tree-sitter-' \ --replace-fail 'new URL("tree-sitter.wasm", import.meta.url).href' "\"$main_wasm\"" # Copy only the native modules we need (marked as external in bundle.ts) mkdir -p $out/lib/opencode/node_modules/.bun mkdir -p $out/lib/opencode/node_modules/@opentui # Copy @opentui/core platform-specific packages for pkg in ../../node_modules/.bun/@opentui+core-*; do if [ -d "$pkg" ]; then cp -r "$pkg" $out/lib/opencode/node_modules/.bun/$(basename "$pkg") fi done mkdir -p $out/bin makeWrapper ${lib.getExe bun} $out/bin/opencode \ --add-flags "run" \ --add-flags "$out/lib/opencode/dist/index.js" \ --prefix PATH : ${ lib.makeBinPath [ fzf ripgrep ] } \ --argv0 opencode runHook postInstall ''; postInstall = '' # Add symlinks for platform-specific native modules for pkg in $out/lib/opencode/node_modules/.bun/@opentui+core-*; do if [ -d "$pkg" ]; then pkgName=$(basename "$pkg" | sed 's/@opentui+\(core-[^@]*\)@.*/\1/') ln -sf ../.bun/$(basename "$pkg")/node_modules/@opentui/$pkgName \ $out/lib/opencode/node_modules/@opentui/$pkgName fi done ''; passthru = { tests.version = testers.testVersion { package = finalAttrs.finalPackage; command = "HOME=$(mktemp -d) opencode --version"; inherit (finalAttrs) version; }; updateScript = nix-update-script { extraArgs = [ "--subpackage" "node_modules" ]; }; }; meta = { description = "AI coding agent built for the terminal"; longDescription = '' OpenCode is a terminal-based agent that can build anything. It combines a TypeScript/JavaScript core with a Go-based TUI to provide an interactive AI coding experience. ''; homepage = "https://github.com/sst/opencode"; license = lib.licenses.mit; platforms = [ "aarch64-linux" "x86_64-linux" "aarch64-darwin" "x86_64-darwin" ]; mainProgram = "opencode"; }; })