{
  lib,
  stdenvNoCC,
  bun,
  rev ? "dirty",
  hash ?
    (lib.pipe ./hashes.json [
      builtins.readFile
      builtins.fromJSON
    ]).nodeModules.${stdenvNoCC.hostPlatform.system},
}:
let
  packageJson = lib.pipe ../packages/opencode/package.json [
    builtins.readFile
    builtins.fromJSON
  ];
  platform = stdenvNoCC.hostPlatform;
  bunCpu = if platform.isAarch64 then "arm64" else "x64";
  bunOs = if platform.isLinux then "linux" else "darwin";
in
stdenvNoCC.mkDerivation {
  pname = "opencode-node_modules";
  version = "${packageJson.version}-${rev}";

  src = lib.fileset.toSource {
    root = ../.;
    fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
      lib.fileset.unions [
        ../packages
        ../bun.lock
        ../package.json
        ../patches
        ../install # required by desktop build (cli.rs include_str!)
        ../.github/TEAM_MEMBERS # required by @opencode-ai/script
      ]
    );
  };

  impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
    "GIT_PROXY_COMMAND"
    "SOCKS_SERVER"
  ];

  nativeBuildInputs = [ bun ];

  dontConfigure = true;

  buildPhase = ''
    runHook preBuild
    export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
    bun install \
      --cpu="${bunCpu}" \
      --os="${bunOs}" \
      --filter '!./' \
      --filter './packages/opencode' \
      --filter './packages/desktop' \
      --frozen-lockfile \
      --ignore-scripts \
      --no-progress
    bun --bun ${./scripts/canonicalize-node-modules.ts}
    bun --bun ${./scripts/normalize-bun-binaries.ts}
    runHook postBuild
  '';

  installPhase = ''
    runHook preInstall
    mkdir -p $out
    find . -type d -name node_modules -exec cp -R --parents {} $out \;
    runHook postInstall
  '';

  dontFixup = true;

  outputHashAlgo = "sha256";
  outputHashMode = "recursive";
  outputHash = hash;

  meta.platforms = [
    "aarch64-linux"
    "x86_64-linux"
    "aarch64-darwin"
    "x86_64-darwin"
  ];
}
