diff --git a/nix/desktop-package.nix b/nix/desktop-package.nix index 633915fca..a646ef253 100644 --- a/nix/desktop-package.nix +++ b/nix/desktop-package.nix @@ -16,57 +16,16 @@ # the upstream hash via `paseo.override { npmDepsHash = "..."; }`. paseo, }: - -let - version = (builtins.fromJSON (builtins.readFile ../package.json)).version; - darwinInfoPlist = builtins.toFile "Paseo-Info.plist" ( - lib.generators.toPlist { escape = true; } { - CFBundleDisplayName = "Paseo"; - CFBundleExecutable = "Paseo"; - CFBundleIconFile = "Paseo.icns"; - CFBundleIdentifier = "sh.paseo.desktop"; - CFBundleInfoDictionaryVersion = "6.0"; - CFBundleName = "Paseo"; - CFBundlePackageType = "APPL"; - CFBundleShortVersionString = version; - CFBundleURLTypes = [ - { - CFBundleURLName = "Paseo agent link"; - CFBundleURLSchemes = [ "paseo" ]; - } - ]; - CFBundleVersion = version; - LSApplicationCategoryType = "public.app-category.developer-tools"; - LSEnvironment.MallocNanoZone = "0"; - LSMinimumSystemVersion = "12.0"; - NSAppTransportSecurity.NSAllowsArbitraryLoads = true; - NSAudioCaptureUsageDescription = "Paseo needs access to audio capture for voice input."; - NSBluetoothAlwaysUsageDescription = "Paseo needs access to Bluetooth audio devices."; - NSBluetoothPeripheralUsageDescription = "Paseo needs access to Bluetooth audio devices."; - NSCameraUsageDescription = "Paseo needs access to the camera for image attachments."; - NSHighResolutionCapable = true; - NSMainNibFile = "MainMenu"; - NSMicrophoneUsageDescription = "Paseo needs access to the microphone for voice input."; - NSPrefersDisplaySafeAreaCompatibilityMode = false; - NSPrincipalClass = "AtomApplication"; - NSQuitAlwaysKeepsWindows = false; - NSRequiresAquaSystemAppearance = false; - NSSupportsAutomaticGraphicsSwitching = true; - } - ); -in buildNpmPackage { pname = "paseo-desktop"; - inherit version; + version = (builtins.fromJSON (builtins.readFile ../package.json)).version; src = lib.cleanSourceWith { src = ./..; - filter = - path: type: - let - baseName = builtins.baseNameOf path; - relPath = lib.removePrefix (toString ./..) path; - in + filter = path: type: let + baseName = builtins.baseNameOf path; + relPath = lib.removePrefix (toString ./..) path; + in # Exclude mobile-only platform code (we only need the web/electron build) !(lib.hasPrefix "/packages/app/android" relPath) && !(lib.hasPrefix "/packages/app/ios" relPath) @@ -102,15 +61,15 @@ buildNpmPackage { # Prevent onnxruntime-node's install script from running during automatic # npm rebuild. We manually rebuild only node-pty in buildPhase. - npmRebuildFlags = [ "--ignore-scripts" ]; + npmRebuildFlags = ["--ignore-scripts"]; nativeBuildInputs = [ python3 # for node-gyp (node-pty) - makeWrapper ] ++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook + makeWrapper copyDesktopItems ]; @@ -143,56 +102,75 @@ buildNpmPackage { # Expo web export for the Electron renderer ( cd packages/app && PASEO_WEB_PLATFORM=electron npx expo export --platform web ) - # Desktop main process (tsc only — NOT electron-builder) + # Desktop main process npm run build:main --workspace=@getpaseo/desktop + ${lib.optionalString stdenv.hostPlatform.isDarwin '' + # Let electron-builder create the native bundle layout (including helper + # app names and bundle identifiers), but source Electron from nixpkgs + # instead of downloading a release at build time. + ( + cd packages/desktop + CSC_IDENTITY_AUTO_DISCOVERY=false \ + ../../node_modules/.bin/electron-builder \ + --config electron-builder.yml \ + --dir \ + --mac \ + --publish never \ + --config.electronDist=${electron}/Applications \ + --config.mac.notarize=false + ) + ''} + runHook postBuild ''; installPhase = '' runHook preInstall - mkdir -p $out/share/paseo-desktop $out/bin - - # Materialize only the desktop and daemon runtime graphs. Copying the - # complete monorepo used to ship every build-time dependency (including - # Electron, Expo tooling, and cross-platform builder binaries), making the - # desktop output larger than 2 GiB. - PASEO_TRACE_DESKTOP=1 node scripts/trace-daemon.mjs > desktop-files.txt - - while IFS= read -r path; do - [ -z "$path" ] && continue - mkdir -p "$out/share/paseo-desktop/$(dirname "$path")" - cp -a "$path" "$out/share/paseo-desktop/$path" - done < desktop-files.txt - - # Keep the same unpackaged monorepo layout expected by main.js. - cp package.json $out/share/paseo-desktop/ - mkdir -p $out/share/paseo-desktop/packages/app - cp -a packages/app/dist $out/share/paseo-desktop/packages/app/ - - for runtime_path in \ - packages/desktop/dist/main.js \ - packages/desktop/dist/preload.js \ - packages/desktop/dist/features/browser-keyboard/guest-preload.js \ - packages/desktop/package.json; do - if [ ! -e "$out/share/paseo-desktop/$runtime_path" ]; then - echo "desktop runtime trace omitted $runtime_path" >&2 - exit 1 - fi - done - - if [ -e $out/share/paseo-desktop/node_modules/electron ]; then - echo "desktop runtime trace included npm Electron" >&2 - exit 1 - fi - - # Skills directory referenced at runtime by some agents - if [ -d skills ]; then - cp -a skills $out/share/paseo-desktop/ - fi + mkdir -p $out/bin ${lib.optionalString stdenv.hostPlatform.isLinux '' + mkdir -p $out/share/paseo-desktop + + # Materialize only the desktop and daemon runtime graphs. Copying the + # complete monorepo used to ship every build-time dependency (including + # Electron, Expo tooling, and cross-platform builder binaries), making the + # desktop output larger than 2 GiB. + PASEO_TRACE_DESKTOP=1 node scripts/trace-daemon.mjs > desktop-files.txt + + while IFS= read -r path; do + [ -z "$path" ] && continue + mkdir -p "$out/share/paseo-desktop/$(dirname "$path")" + cp -a "$path" "$out/share/paseo-desktop/$path" + done < desktop-files.txt + + # Keep the same unpackaged monorepo layout expected by main.js. + cp package.json $out/share/paseo-desktop/ + mkdir -p $out/share/paseo-desktop/packages/app + cp -a packages/app/dist $out/share/paseo-desktop/packages/app/ + + for runtime_path in \ + packages/desktop/dist/main.js \ + packages/desktop/dist/preload.js \ + packages/desktop/dist/features/browser-keyboard/guest-preload.js \ + packages/desktop/package.json; do + if [ ! -e "$out/share/paseo-desktop/$runtime_path" ]; then + echo "desktop runtime trace omitted $runtime_path" >&2 + exit 1 + fi + done + + if [ -e $out/share/paseo-desktop/node_modules/electron ]; then + echo "desktop runtime trace included npm Electron" >&2 + exit 1 + fi + + # Skills directory referenced at runtime by some agents + if [ -d skills ]; then + cp -a skills $out/share/paseo-desktop/ + fi + # Hicolor icon for desktop environments install -Dm644 packages/desktop/assets/icon.png \ $out/share/icons/hicolor/512x512/apps/paseo-desktop.png @@ -207,30 +185,13 @@ buildNpmPackage { ''} ${lib.optionalString stdenv.hostPlatform.isDarwin '' - app="$out/Applications/Paseo.app" - mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources" - - # Reuse nixpkgs' Electron framework through store symlinks so the Paseo - # output stays small while still presenting a normal application bundle - # to LaunchServices. - ln -s ${electron}/Applications/Electron.app/Contents/Frameworks \ - "$app/Contents/Frameworks" - ln -s ${electron}/Applications/Electron.app/Contents/MacOS/Electron \ - "$app/Contents/MacOS/Electron" - for resource in ${electron}/Applications/Electron.app/Contents/Resources/*; do - ln -s "$resource" "$app/Contents/Resources/$(basename "$resource")" - done - - cp ${darwinInfoPlist} "$app/Contents/Info.plist" - cp ${electron}/Applications/Electron.app/Contents/PkgInfo "$app/Contents/PkgInfo" - cp packages/desktop/assets/icon.icns "$app/Contents/Resources/Paseo.icns" - - # Keep the same unpackaged runtime layout as the Linux Nix package. The - # outer .app supplies native macOS identity, icon, and URL handling while - # the wrapper points Electron at the immutable built main process. - makeWrapper "$app/Contents/MacOS/Electron" "$app/Contents/MacOS/Paseo" \ - --add-flags "$out/share/paseo-desktop/packages/desktop/dist/main.js" \ - --set EXPO_DEV_URL "paseo://app/" + app="$(find packages/desktop/release -maxdepth 3 -type d -name Paseo.app -print -quit)" + if [ -z "$app" ]; then + echo "electron-builder did not produce Paseo.app" >&2 + exit 1 + fi + mkdir -p "$out/Applications" + cp -R "$app" "$out/Applications/Paseo.app" ln -s ../Applications/Paseo.app/Contents/MacOS/Paseo "$out/bin/paseo-desktop" ''} @@ -245,7 +206,7 @@ buildNpmPackage { comment = "Self-hosted daemon for AI coding agents"; exec = "paseo-desktop"; icon = "paseo-desktop"; - categories = [ "Development" ]; + categories = ["Development"]; startupWMClass = "Paseo"; }) ];