{ pkgs, lib, stdenvNoCC, bun, fetchFromGitHub, fzf, makeBinaryWrapper, models-dev, nix-update-script, ripgrep, testers, writableTmpDirAsHomeHook, }: let customCaBundle = pkgs.runCommand "ca-bundle-with-foyer.crt" { } '' cat ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt \ ${../../modules/services/certs/Foyer-Group-Root-CA.crt} \ ${../../modules/services/certs/Foyer-Sub-CA.crt} > $out ''; pname = "mia"; version = "1.1.2.1"; src = fetchFromGitHub { githubBase = "github.foyer.lu"; owner = "rdo"; repo = "mia"; rev = "6e6193ceb325a0077f7a49051276cbdd6be60736"; # tag = "v${version}-mia"; hash = "sha256-qrYbdhII6jOPdztk7JxNlOz8vxef7G8WmMGOQr2vb5A="; curlOptsList = [ "--location" "--cacert" "${customCaBundle}" ]; }; 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-pOGHE02eoZzSkQwfUmG2EVaN80mBxKhIW+BFEufTh3w="; 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/mia \ --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"; }; })