diff --git a/build.sh b/build.sh index 480e90d..976d563 100755 --- a/build.sh +++ b/build.sh @@ -17,6 +17,7 @@ cleanup_action='yes' perform_cleanup=false test_flags_mode=false local_exe_path='' +node_pty_dir='' original_user='' original_home='' project_root='' @@ -192,7 +193,7 @@ parse_arguments() { while (( $# > 0 )); do case "$1" in - -b|--build|-c|--clean|-e|--exe|-r|--release-tag|-s|--source-dir) + -b|--build|-c|--clean|-e|--exe|-r|--release-tag|-s|--source-dir|--node-pty-dir) if [[ -z ${2:-} || $2 == -* ]]; then echo "Error: Argument for $1 is missing" >&2 exit 1 @@ -203,6 +204,7 @@ parse_arguments() { -e|--exe) local_exe_path="$2" ;; -r|--release-tag) release_tag="$2" ;; -s|--source-dir) source_dir="$2" ;; + --node-pty-dir) node_pty_dir="$2" ;; esac shift 2 ;; @@ -217,6 +219,7 @@ parse_arguments() { echo ' --clean: Specify whether to clean intermediate build files (yes or no). Default: yes' echo ' --exe: Use a local Claude installer exe instead of downloading' echo ' --source-dir: Path to repo root for scripts/ and assets (default: project root)' + echo ' --node-pty-dir: Path to pre-built node-pty package (skips npm install)' echo ' --release-tag: Release tag (e.g., v1.3.2+claude1.1.799) to append wrapper version to package' echo ' --test-flags: Parse flags, print results, and exit without building.' exit 0 diff --git a/flake.nix b/flake.nix index 0688816..4e16503 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,10 @@ systems = [ "x86_64-linux" "aarch64-linux" ]; perSystem = { pkgs, ... }: let - claude-desktop = pkgs.callPackage ./nix/claude-desktop.nix { }; + node-pty = pkgs.callPackage ./nix/node-pty.nix { }; + claude-desktop = pkgs.callPackage ./nix/claude-desktop.nix { + inherit node-pty; + }; in { packages = { inherit claude-desktop; @@ -23,8 +26,12 @@ }; flake = { - overlays.default = final: prev: { - claude-desktop = final.callPackage ./nix/claude-desktop.nix { }; + overlays.default = final: prev: let + node-pty = final.callPackage ./nix/node-pty.nix { }; + in { + claude-desktop = final.callPackage ./nix/claude-desktop.nix { + inherit node-pty; + }; claude-desktop-fhs = final.callPackage ./nix/fhs.nix { claude-desktop = final.claude-desktop; }; diff --git a/nix/claude-desktop.nix b/nix/claude-desktop.nix index dc5419b..5d680e5 100644 --- a/nix/claude-desktop.nix +++ b/nix/claude-desktop.nix @@ -13,6 +13,7 @@ python3, bash, getent, + node-pty, }: let pname = "claude-desktop"; @@ -78,6 +79,7 @@ stdenvNoCC.mkDerivation { bash ${sourceRoot}/build.sh \ --exe "$(pwd)/Claude-Setup.exe" \ --source-dir "${sourceRoot}" \ + --node-pty-dir "${node-pty}/lib/node_modules/node-pty" \ --build nix \ --clean no @@ -92,9 +94,6 @@ stdenvNoCC.mkDerivation { cp build/electron-app/app.asar $out/lib/claude-desktop/resources/ cp -r build/electron-app/app.asar.unpacked $out/lib/claude-desktop/resources/ - # TODO: node-pty is not in nixpkgs; terminal features (Claude Code) - # require it. A future improvement could build it with buildNpmPackage. - # Install icons for size in 16 24 32 48 64 256; do icon_dir=$out/share/icons/hicolor/"$size"x"$size"/apps diff --git a/nix/node-pty.nix b/nix/node-pty.nix new file mode 100644 index 0000000..19fd45d --- /dev/null +++ b/nix/node-pty.nix @@ -0,0 +1,51 @@ +{ + lib, + buildNpmPackage, + fetchFromGitHub, + python3, + node-gyp, +}: +buildNpmPackage rec { + pname = "node-pty"; + version = "1.1.0"; + + src = fetchFromGitHub { + owner = "microsoft"; + repo = "node-pty"; + rev = "v${version}"; + hash = "sha256-R0QxTw3tNJvW4aEi+GOF0iZhGgI42HTYJih90CdF18I="; + }; + + npmDepsHash = "sha256-HRv/4NO7CHkPs7ld8lx61n2cty0EhmWVrpH/1Vqh+Nk="; + + # node-gyp needs python3 for native compilation + nativeBuildInputs = [ python3 node-gyp ]; + + # chokidar (dev dep) has an optional dep on fsevents (macOS-only). + # The Nix npm deps fetcher excludes it, so npm ci sees a lock/json + # mismatch. Strip the reference from the lock file to fix the sync. + postPatch = '' + sed -i '/"fsevents"/d' package-lock.json + ''; + + # Default npmBuildHook only runs "npm run build" (tsc), but we also + # need the native addon. Run both explicitly. + buildPhase = '' + runHook preBuild + npm run build + node-gyp rebuild + runHook postBuild + ''; + + # npmInstallHook doesn't copy the native addon — do it ourselves + postInstall = '' + cp -r build $out/lib/node_modules/node-pty/ + ''; + + meta = with lib; { + description = "Fork pseudoterminals in Node.JS"; + homepage = "https://github.com/microsoft/node-pty"; + license = licenses.mit; + platforms = platforms.linux; + }; +}