Consolidate per-package package-lock.json files into a single root-level workspace lockfile. Update all consumers: - Nix: shared src/npmDeps/npmDepsHash in lib.nix; devshell hook stamps package.json paths then runs npm ci from root; individual .nix files use mkNpmPassthru attrs instead of per-package fetchNpmDeps. - Python CLI: new _workspace_root() helper so _tui_need_npm_install, _make_tui_argv, _build_web_ui resolve lockfile/node_modules from the workspace root. - Desktop: replace --force-build/mtime heuristic with content-hash build stamp (_compute_desktop_content_hash via pathspec). Remove --force-build flag. - Dockerfile: single root npm install; no per-directory lockfile copies. - CI: nix-lockfile-fix and osv-scanner reference root package-lock.json; apps/dashboard → apps/desktop. - Tests: new test_tui_npm_install.py; desktop stamp tests in test_gui_command.py; updated assertions in test_cmd_update.py, test_web_ui_build.py, test_dockerfile_pid1_reaping.py. - Docs: remove --force-build from desktop flag table. Deleted: apps/desktop/package-lock.json, ui-tui/package-lock.json, ui-tui/packages/hermes-ink/package-lock.json, web/package-lock.json.
110 lines
4.0 KiB
Nix
110 lines
4.0 KiB
Nix
# nix/desktop.nix — Hermes Desktop (Electron) app build + wrapper
|
|
#
|
|
# `hermesAgent` is the fully-built `.#default` package — it ships the
|
|
# `hermes` binary with the venv, runtime PATH, bundled skills/plugins, etc.
|
|
# already wired up. We point the desktop at it via the existing
|
|
# `HERMES_DESKTOP_HERMES` override env var, so the desktop's resolver
|
|
# uses our fully wrapped binary at step 4 ("existing Hermes CLI").
|
|
# No reimplementation of the agent resolution in this wrapper.
|
|
{ pkgs, lib, stdenv, makeWrapper, hermesNpmLib, electron, hermesAgent, ... }:
|
|
let
|
|
npm = hermesNpmLib.mkNpmPassthru { folder = "apps/desktop"; attr = "desktop"; pname = "hermes-desktop"; };
|
|
|
|
packageJson = builtins.fromJSON (builtins.readFile (npm.src + "/apps/desktop/package.json"));
|
|
version = packageJson.version;
|
|
|
|
# Build the renderer (dist/ + electron/ + package.json).
|
|
renderer = pkgs.buildNpmPackage (npm // {
|
|
pname = "hermes-desktop-renderer";
|
|
inherit version;
|
|
|
|
doCheck = false;
|
|
# The workspace lockfile resolves all peer deps
|
|
# correctly so --legacy-peer-deps is not needed.
|
|
# --ignore-scripts comes from mkNpmPassthru (shared).
|
|
makeCacheWritable = true;
|
|
|
|
buildPhase = ''
|
|
runHook preBuild
|
|
|
|
# write-build-stamp.cjs replacement. Packaged Electron reads this
|
|
# at first-launch to pin the install.ps1 git ref; informational in
|
|
# nix builds (the backend comes from the derivation directly).
|
|
mkdir -p apps/desktop/build
|
|
echo '{"schemaVersion":1,"commit":"nix","branch":"nix","dirty":false,"source":"nix"}' > apps/desktop/build/install-stamp.json
|
|
|
|
# Build from apps/desktop/ so vite.config.ts resolves correctly.
|
|
# The workspace root's node_modules/ is accessible as ../../node_modules/.
|
|
cd apps/desktop
|
|
|
|
# vite handles TS transpilation via esbuild — no type-checking.
|
|
# We skip `tsc -b` to avoid type errors in test files that don't
|
|
# ship in the bundle (real upstream peer-dep version mismatches
|
|
# in @testing-library/react v16 — not blocking the build).
|
|
# Call vite directly from root node_modules to avoid npx resolving
|
|
# through unpatched workspace symlinks.
|
|
node ../../node_modules/vite/bin/vite.js build --outDir dist
|
|
|
|
# Return to source root so installPhase paths are correct.
|
|
cd ../..
|
|
|
|
runHook postBuild
|
|
'';
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
mkdir -p $out
|
|
# vite writes to apps/desktop/dist/ (we cd'd there in buildPhase).
|
|
# apps/desktop/build was created before the cd. electron/ is source.
|
|
cp -r apps/desktop/dist $out/
|
|
cp -r apps/desktop/electron $out/
|
|
cp -r apps/desktop/build $out/
|
|
cp apps/desktop/package.json $out/
|
|
runHook postInstall
|
|
'';
|
|
});
|
|
in
|
|
|
|
# Electron wrapper: nixpkgs' electron binary pointed at the renderer dir.
|
|
stdenv.mkDerivation {
|
|
pname = "hermes-desktop";
|
|
inherit version;
|
|
|
|
dontUnpack = true;
|
|
dontBuild = true;
|
|
|
|
nativeBuildInputs = [ makeWrapper ];
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
mkdir -p $out/share/hermes-desktop $out/bin
|
|
cp -r ${renderer}/* $out/share/hermes-desktop/
|
|
|
|
# Wrap the nixpkgs electron binary to launch our app. Set
|
|
# HERMES_DESKTOP_HERMES to the absolute path of the nix-built `hermes`
|
|
# binary so the desktop's resolver step 4 ("existing Hermes CLI on
|
|
# PATH") uses our fully wrapped binary — venv with all deps,
|
|
# bundled skills/plugins, runtime PATH (ripgrep/git/ffmpeg/etc).
|
|
# No reimplementation of the agent resolver in the wrapper.
|
|
makeWrapper ${lib.getExe electron} $out/bin/hermes-desktop \
|
|
--add-flags "$out/share/hermes-desktop" \
|
|
--set HERMES_DESKTOP_HERMES "${lib.getExe hermesAgent}" \
|
|
--set ELECTRON_IS_DEV 0
|
|
|
|
runHook postInstall
|
|
'';
|
|
|
|
passthru = {
|
|
inherit (renderer.passthru) packageJsonPath;
|
|
};
|
|
|
|
meta = with lib; {
|
|
description = "Native Electron desktop shell for Hermes Agent";
|
|
homepage = "https://github.com/NousResearch/hermes-agent";
|
|
license = licenses.mit;
|
|
platforms = platforms.unix;
|
|
mainProgram = "hermes-desktop";
|
|
};
|
|
}
|