mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-14 09:11:54 +00:00
nix: package apps/desktop as .#desktop (#28964)
Adds nix/desktop.nix building the Electron renderer with buildNpmPackage and wrapping nixpkgs' electron binary. Reuses .#default by setting HERMES_DESKTOP_HERMES to its hermes binary, so the desktop's resolver picks up the fully-wired nix hermes (venv, bundled skills/plugins, runtime PATH) without reimplementing agent resolution. - nix/desktop.nix: renderer + electron wrapper - nix/hermes-agent.nix: finalAttrs form, exposes hermesDesktop in passthru - nix/packages.nix: exposes .#desktop + adds to fix-lockfiles - apps/desktop/package-lock.json: standalone hermetic lockfile nix build .#desktop && nix run .#desktop both clean.
This commit is contained in:
parent
7f8b0dd1e0
commit
6079d7dd9d
4 changed files with 18496 additions and 3 deletions
18363
apps/desktop/package-lock.json
generated
Normal file
18363
apps/desktop/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
116
nix/desktop.nix
Normal file
116
nix/desktop.nix
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# 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
|
||||
src = ../apps;
|
||||
npmDeps = pkgs.fetchNpmDeps {
|
||||
src = ../apps/desktop;
|
||||
# buildNpmPackage uses `npm ci` which is strict — peer deps not in the
|
||||
# lockfile cause network fetch attempts. Fetcher v2 stages the full
|
||||
# cache (including peer-only deps) so `npm ci` can resolve them offline.
|
||||
fetcherVersion = 2;
|
||||
hash = "sha256-7W9ObYz08yDMtybY8+RkUXkKVsJXINLl0qBUB91hpao=";
|
||||
};
|
||||
|
||||
npm = hermesNpmLib.mkNpmPassthru { folder = "apps/desktop"; attr = "desktop"; pname = "hermes-desktop"; };
|
||||
|
||||
packageJson = builtins.fromJSON (builtins.readFile (src + "/desktop/package.json"));
|
||||
version = packageJson.version;
|
||||
|
||||
# Build the renderer (dist/ + electron/ + package.json).
|
||||
renderer = pkgs.buildNpmPackage (npm // {
|
||||
pname = "hermes-desktop-renderer";
|
||||
inherit src npmDeps version;
|
||||
sourceRoot = "apps/desktop";
|
||||
|
||||
doCheck = false;
|
||||
# buildNpmPackage uses `npm ci` which fails on peer deps not in the
|
||||
# lockfile. npmDepsFetcherVersion=2 stages the full cache (peer deps
|
||||
# included) so the offline `npm ci` resolves them.
|
||||
npmDepsFetcherVersion = 2;
|
||||
# `--ignore-scripts` skips the electron prebuild download (we use nixpkgs
|
||||
# electron instead). `--legacy-peer-deps` matches the dev workflow —
|
||||
# apps/desktop has conflicting peer deps (zod, @testing-library) that
|
||||
# the package.json relies on npm 7+ to relax.
|
||||
npmFlags = [ "--ignore-scripts" "--legacy-peer-deps" ];
|
||||
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 build
|
||||
echo '{"schemaVersion":1,"commit":"nix","branch":"nix","dirty":false,"source":"nix"}' > build/install-stamp.json
|
||||
|
||||
# The vite config aliases react/react-dom to ../../node_modules/react
|
||||
# (workspace root, where npm dedups them in dev). In the standalone
|
||||
# nix build there is no workspace root, so the deps are installed
|
||||
# locally — rewrite the aliases to point at the local copy.
|
||||
substituteInPlace vite.config.ts \
|
||||
--replace-quiet '../../node_modules/' './node_modules/'
|
||||
|
||||
# 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).
|
||||
npx vite build --outDir dist
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
cp -r dist electron build $out/
|
||||
cp 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
|
||||
'';
|
||||
|
||||
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";
|
||||
};
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
callPackage,
|
||||
python312,
|
||||
nodejs_22,
|
||||
electron,
|
||||
ripgrep,
|
||||
git,
|
||||
openssh,
|
||||
|
|
@ -127,7 +128,7 @@ let
|
|||
print('No collisions found.')
|
||||
'';
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hermes-agent";
|
||||
version = (fromTOML (builtins.readFile ../pyproject.toml)).project.version;
|
||||
|
||||
|
|
@ -183,6 +184,18 @@ stdenv.mkDerivation {
|
|||
hermesVenv
|
||||
;
|
||||
|
||||
# `hermesDesktop` references `finalAttrs.finalPackage` (this whole
|
||||
# derivation, after all overrides are applied) so the desktop wrapper
|
||||
# can prepend its `/bin` to PATH. The desktop's resolver step 4
|
||||
# ("existing hermes on PATH") then picks up the fully wrapped
|
||||
# `hermes` binary — venv with all deps, bundled skills/plugins,
|
||||
# runtime PATH (ripgrep/git/ffmpeg/etc). No re-implementation
|
||||
# of the agent resolution in the desktop wrapper.
|
||||
hermesDesktop = callPackage ./desktop.nix {
|
||||
inherit hermesNpmLib electron;
|
||||
hermesAgent = finalAttrs.finalPackage;
|
||||
};
|
||||
|
||||
devShellHook = ''
|
||||
STAMP=".nix-stamps/hermes-agent"
|
||||
STAMP_VALUE="${pyprojectHash}:${uvLockHash}"
|
||||
|
|
@ -208,4 +221,4 @@ stdenv.mkDerivation {
|
|||
license = licenses.mit;
|
||||
platforms = platforms.unix;
|
||||
};
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
default = hermesAgent;
|
||||
tui = hermesAgent.hermesTui;
|
||||
web = hermesAgent.hermesWeb;
|
||||
desktop = hermesAgent.hermesDesktop;
|
||||
|
||||
fix-lockfiles = hermesAgent.hermesNpmLib.mkFixLockfiles {
|
||||
packages = [ hermesAgent.hermesTui hermesAgent.hermesWeb ];
|
||||
packages = [ hermesAgent.hermesTui hermesAgent.hermesWeb hermesAgent.hermesDesktop ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue