Merge remote-tracking branch 'origin/main' into fix/bundle-size

This commit is contained in:
ethernet 2026-05-11 16:01:00 -04:00
commit 3197b4de6d
1437 changed files with 219762 additions and 11968 deletions

View file

@ -239,6 +239,27 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2)
echo "ok" > $out/result
'';
# Verify extraDependencyGroups passes through to python.nix
extra-dependency-groups = let
hermesWithGroups = hermes-agent.override {
extraDependencyGroups = [ "honcho" ];
};
in pkgs.runCommand "hermes-extra-dependency-groups" { } ''
set -e
echo "=== Checking extraDependencyGroups override evaluates ==="
# Eval-only: verify the override produces valid derivation paths
# without building the full venv (which is expensive and redundant
# since the mechanism is just list concatenation into python.nix).
echo "derivation: ${hermesWithGroups}"
echo "venv: ${hermesWithGroups.hermesVenv}"
echo "PASS: extraDependencyGroups override evaluates cleanly"
echo "=== All extraDependencyGroups checks passed ==="
mkdir -p $out
echo "ok" > $out/result
'';
# ── Config merge + round-trip test ────────────────────────────────
# Tests the merge script (Nix activation behavior) across 7
# scenarios, then verifies Python's load_config() reads correctly.

View file

@ -1,7 +1,9 @@
# nix/hermes-agent.nix — Overridable Hermes Agent package
#
# callPackage auto-wires nixpkgs args; flake inputs are passed explicitly.
# Users override via: pkgs.hermes-agent.override { extraPythonPackages = [...]; }
# Users override via:
# pkgs.hermes-agent.override { extraPythonPackages = [...]; }
# pkgs.hermes-agent.override { extraDependencyGroups = [ "hindsight" ]; }
{
lib,
stdenv,
@ -25,11 +27,13 @@
rev ? null,
# Overridable parameters
extraPythonPackages ? [ ],
extraDependencyGroups ? [ ],
}:
let
nodejs = nodejs_22;
hermesVenv = callPackage ./python.nix {
inherit uv2nix pyproject-nix pyproject-build-systems;
dependency-groups = [ "all" ] ++ extraDependencyGroups;
};
hermesNpmLib = callPackage ./lib.nix {

View file

@ -163,35 +163,42 @@
for entry in "''${ENTRIES[@]}"; do
IFS=":" read -r ATTR FOLDER NIX_FILE <<< "$entry"
echo "==> .#$ATTR ($FOLDER -> $NIX_FILE)"
OUTPUT=$(nix build ".#$ATTR.npmDeps" --no-link --rebuild --print-build-logs 2>&1)
STATUS=$?
if [ "$STATUS" -eq 0 ]; then
# Compute the actual hash from the lockfile directly using
# prefetch-npm-deps. This avoids false "ok" from nix build when
# an old derivation is cached in a substituter (cachix/cache.nixos.org).
LOCK_FILE="$FOLDER/package-lock.json"
NEW_HASH=$(${pkgs.lib.getExe pkgs.prefetch-npm-deps} "$LOCK_FILE" 2>/dev/null)
if [ -z "$NEW_HASH" ]; then
echo " prefetch-npm-deps failed, falling back to nix build" >&2
OUTPUT=$(nix build ".#$ATTR.npmDeps" --no-link --print-build-logs 2>&1)
STATUS=$?
if [ "$STATUS" -eq 0 ]; then
echo " ok (via nix build)"
continue
fi
NEW_HASH=$(echo "$OUTPUT" | awk '/got:/ {print $2; exit}')
if [ -z "$NEW_HASH" ]; then
if echo "$OUTPUT" | grep -qE "throttled|HTTP error 418|substituter .* is disabled|some outputs of .* are not valid"; then
echo " skipped (transient cache failure see primary nix build for real status)" >&2
echo "$OUTPUT" | tail -8 >&2
continue
fi
echo " build failed with no hash mismatch:" >&2
echo "$OUTPUT" | tail -40 >&2
exit 1
fi
fi
OLD_HASH=$(grep -oE 'hash = "sha256-[^"]+"' "$NIX_FILE" | head -1 \
| sed -E 's/hash = "(.*)"/\1/')
if [ "$NEW_HASH" = "$OLD_HASH" ]; then
echo " ok"
continue
fi
NEW_HASH=$(echo "$OUTPUT" | awk '/got:/ {print $2; exit}')
if [ -z "$NEW_HASH" ]; then
# Magic-Nix-Cache occasionally returns HTTP 418 / cache-throttled
# mid-run; nix then prints "outputs … not valid, so checking is
# not possible" without a `got:` line. That's an infrastructure
# blip, not a stale lockfile — warn + skip rather than failing
# the lint. A real hash mismatch would still surface in the
# primary `.#$ATTR` build, which is a separate CI job.
if echo "$OUTPUT" | grep -qE "throttled|HTTP error 418|substituter .* is disabled|some outputs of .* are not valid"; then
echo " skipped (transient cache failure see primary nix build for real status)" >&2
echo "$OUTPUT" | tail -8 >&2
continue
fi
echo " build failed with no hash mismatch:" >&2
echo "$OUTPUT" | tail -40 >&2
exit 1
fi
HASH_LINE=$(grep -n 'hash = "sha256-' "$NIX_FILE" | head -1 | cut -d: -f1)
OLD_HASH=$(grep -oE 'hash = "sha256-[^"]+"' "$NIX_FILE" | head -1 \
| sed -E 's/hash = "(.*)"/\1/')
LOCK_FILE="$FOLDER/package-lock.json"
echo " stale: $NIX_FILE:$HASH_LINE $OLD_HASH -> $NEW_HASH"
STALE=1

View file

@ -28,8 +28,10 @@
let
cfg = config.services.hermes-agent;
effectivePackage = if cfg.extraPythonPackages == [ ] then cfg.package
else cfg.package.override { inherit (cfg) extraPythonPackages; };
effectivePackage =
if cfg.extraPythonPackages == [ ] && cfg.extraDependencyGroups == [ ]
then cfg.package
else cfg.package.override { inherit (cfg) extraPythonPackages extraDependencyGroups; };
hermes-agent = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default;
# Deep-merge config type (from 0xrsydn/nix-hermes-agent)
@ -115,9 +117,13 @@
chown "$HERMES_UID:$HERMES_GID" "$TARGET_HOME"
chmod 0750 "$TARGET_HOME"
# Ensure HERMES_HOME is owned by the target user
# Ensure HERMES_HOME is owned by the target user.
# Use find instead of chown -R: chown strips the setgid bit (kernel
# behavior), destroying the 2770 permissions the NixOS activation
# script sets for group access by hostUsers. Only touch files with
# wrong ownership so correctly-owned dirs keep their permission bits.
if [ -n "''${HERMES_HOME:-}" ] && [ -d "$HERMES_HOME" ]; then
chown -R "$HERMES_UID:$HERMES_GID" "$HERMES_HOME"
find "$HERMES_HOME" \! -user "$HERMES_UID" -exec chown "$HERMES_UID:$HERMES_GID" {} +
fi
# ── Provision apt packages (first boot only, cached in writable layer) ──
@ -512,6 +518,21 @@
'';
};
extraDependencyGroups = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Additional pyproject.toml optional-dependency groups to include in
the sealed Python venv. These are resolved by uv alongside core
dependencies no PYTHONPATH patching or collision risk.
Use this for optional extras already declared in hermes-agent's
pyproject.toml (e.g. "hindsight", "honcho", "voice").
Use extraPythonPackages for external packages not in pyproject.toml.
'';
example = [ "hindsight" ];
};
restart = mkOption {
type = types.str;
default = "always";

View file

@ -4,7 +4,7 @@ let
src = ../ui-tui;
npmDeps = pkgs.fetchNpmDeps {
inherit src;
hash = "sha256-hxBD2zsPwdSoUL57feFFGqZ2Z1xIHxERwmQa/jIqNZw=";
hash = "sha256-9r1EYQ600gNXOnNXwakorpEk7hS/FPxZVbB2JksrhYs=";
};
npm = hermesNpmLib.mkNpmPassthru { folder = "ui-tui"; attr = "tui"; pname = "hermes-tui"; };