From 761c113427c084ca6cb3e394adb6f469c0d61118 Mon Sep 17 00:00:00 2001 From: ethernet Date: Mon, 20 Apr 2026 16:12:28 -0400 Subject: [PATCH] nix: automatic lockfile fixing to keep main building with nix (#13136) * ci(nix): automatic lockfile fixing to keep main building This reverts commit 688c9f5b7c3cb19aebb6843973ac57ed570ebc4a. * update lockfiles --- .github/workflows/nix-lockfile-check.yml | 76 +++++++++ .github/workflows/nix-lockfile-fix.yml | 149 +++++++++++++++++ nix/devShell.nix | 3 +- nix/lib.nix | 193 +++++++++++++++++++++++ nix/packages.nix | 12 +- nix/tui.nix | 49 +----- nix/web.nix | 44 +----- ui-tui/package-lock.json | 63 ++++++-- web/package-lock.json | 26 +-- 9 files changed, 491 insertions(+), 124 deletions(-) create mode 100644 .github/workflows/nix-lockfile-check.yml create mode 100644 .github/workflows/nix-lockfile-fix.yml create mode 100644 nix/lib.nix diff --git a/.github/workflows/nix-lockfile-check.yml b/.github/workflows/nix-lockfile-check.yml new file mode 100644 index 000000000..61c428021 --- /dev/null +++ b/.github/workflows/nix-lockfile-check.yml @@ -0,0 +1,76 @@ +name: Nix Lockfile Check + +on: + pull_request: + paths: + - 'ui-tui/package.json' + - 'ui-tui/package-lock.json' + - 'web/package.json' + - 'web/package-lock.json' + - 'nix/tui.nix' + - 'nix/web.nix' + - 'nix/lib.nix' + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: nix-lockfile-check-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v30 + + - name: Resolve head SHA + id: sha + shell: bash + run: | + FULL="${{ github.event.pull_request.head.sha || github.sha }}" + echo "full=$FULL" >> "$GITHUB_OUTPUT" + echo "short=${FULL:0:7}" >> "$GITHUB_OUTPUT" + + - name: Check lockfile hashes + id: check + continue-on-error: true + env: + LINK_SHA: ${{ steps.sha.outputs.full }} + run: nix run .#fix-lockfiles -- --check + + - name: Post sticky PR comment (stale) + if: steps.check.outputs.stale == 'true' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + message: | + ### ⚠️ npm lockfile hash out of date + + Checked against commit [`${{ steps.sha.outputs.short }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ steps.sha.outputs.full }}) (PR head at check time). + + The `hash = "sha256-..."` line in these nix files no longer matches the committed `package-lock.json`: + + ${{ steps.check.outputs.report }} + + #### Apply the fix + + - [ ] **Apply lockfile fix** — tick to push a commit with the correct hashes to this PR branch + - Or [run the Nix Lockfile Fix workflow](${{ github.server_url }}/${{ github.repository }}/actions/workflows/nix-lockfile-fix.yml) manually (pass PR `#${{ github.event.pull_request.number }}`) + - Or locally: `nix run .#fix-lockfiles -- --apply` and commit the diff + + - name: Clear sticky PR comment (resolved) + if: steps.check.outputs.stale == 'false' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + delete: true + + - name: Fail if stale + if: steps.check.outputs.stale == 'true' + run: exit 1 diff --git a/.github/workflows/nix-lockfile-fix.yml b/.github/workflows/nix-lockfile-fix.yml new file mode 100644 index 000000000..5021e318f --- /dev/null +++ b/.github/workflows/nix-lockfile-fix.yml @@ -0,0 +1,149 @@ +name: Nix Lockfile Fix + +on: + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to fix (leave empty to run on the selected branch)' + required: false + type: string + issue_comment: + types: [edited] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: nix-lockfile-fix-${{ github.event.issue.number || github.event.inputs.pr_number || github.ref }} + cancel-in-progress: false + +jobs: + fix: + # Run on manual dispatch OR when a task-list checkbox in the sticky + # lockfile-check comment flips from `[ ]` to `[x]`. + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'issue_comment' + && github.event.issue.pull_request != null + && contains(github.event.comment.body, '[x] **Apply lockfile fix**') + && !contains(github.event.changes.body.from, '[x] **Apply lockfile fix**')) + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - name: Authorize & resolve PR + id: resolve + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + // 1. Verify the actor has write access — applies to both checkbox + // clicks and manual dispatch. + const { data: perm } = + await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.actor, + }); + if (!['admin', 'write', 'maintain'].includes(perm.permission)) { + core.setFailed( + `${context.actor} lacks write access (has: ${perm.permission})` + ); + return; + } + + // 2. Resolve which ref to check out. + let prNumber = ''; + if (context.eventName === 'issue_comment') { + prNumber = String(context.payload.issue.number); + } else if (context.eventName === 'workflow_dispatch') { + prNumber = context.payload.inputs.pr_number || ''; + } + + if (!prNumber) { + core.setOutput('ref', context.ref.replace(/^refs\/heads\//, '')); + core.setOutput('repo', context.repo.repo); + core.setOutput('owner', context.repo.owner); + core.setOutput('pr', ''); + return; + } + + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: Number(prNumber), + }); + core.setOutput('ref', pr.head.ref); + core.setOutput('repo', pr.head.repo.name); + core.setOutput('owner', pr.head.repo.owner.login); + core.setOutput('pr', String(pr.number)); + + # Wipe the sticky lockfile-check comment to a "running" state as soon + # as the job is authorized, so the user sees their click was picked up + # before the ~minute of nix build work. + - name: Mark sticky as running + if: steps.resolve.outputs.pr != '' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + number: ${{ steps.resolve.outputs.pr }} + message: | + ### 🔄 Applying lockfile fix… + + Triggered by @${{ github.actor }} — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + repository: ${{ steps.resolve.outputs.owner }}/${{ steps.resolve.outputs.repo }} + ref: ${{ steps.resolve.outputs.ref }} + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v30 + + - name: Apply lockfile hashes + id: apply + run: nix run .#fix-lockfiles -- --apply + + - name: Commit & push + if: steps.apply.outputs.changed == 'true' + shell: bash + run: | + set -euo pipefail + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add nix/tui.nix nix/web.nix + git commit -m "fix(nix): refresh npm lockfile hashes" + git push + + - name: Update sticky (applied) + if: steps.apply.outputs.changed == 'true' && steps.resolve.outputs.pr != '' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + number: ${{ steps.resolve.outputs.pr }} + message: | + ### ✅ Lockfile fix applied + + Pushed a commit refreshing the npm lockfile hashes — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + - name: Update sticky (already current) + if: steps.apply.outputs.changed == 'false' && steps.resolve.outputs.pr != '' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + number: ${{ steps.resolve.outputs.pr }} + message: | + ### ✅ Lockfile hashes already current + + Nothing to commit — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + - name: Update sticky (failed) + if: failure() && steps.resolve.outputs.pr != '' + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + header: nix-lockfile-check + number: ${{ steps.resolve.outputs.pr }} + message: | + ### ❌ Lockfile fix failed + + See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for logs. diff --git a/nix/devShell.nix b/nix/devShell.nix index 63edc59cf..d0d56e40b 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -7,7 +7,8 @@ let hermes-agent = inputs.self.packages.${system}.default; hermes-tui = inputs.self.packages.${system}.tui; - packages = [ hermes-agent hermes-tui ]; + hermes-web = inputs.self.packages.${system}.web; + packages = [ hermes-agent hermes-tui hermes-web ]; in { devShells.default = pkgs.mkShell { inputsFrom = packages; diff --git a/nix/lib.nix b/nix/lib.nix new file mode 100644 index 000000000..f97f1539f --- /dev/null +++ b/nix/lib.nix @@ -0,0 +1,193 @@ +# nix/lib.nix — Shared helpers for nix stuff +{ pkgs, npm-lockfile-fix }: +{ + # Returns a buildNpmPackage-compatible attrs set that provides: + # patchPhase — strips trailing NUL newline from lockfile + # nativeBuildInputs — [ updateLockfileScript ] (list, prepend with ++ for more) + # passthru.devShellHook — stamp-checked npm install + hash auto-update + # passthru.npmLockfile — metadata for mkFixLockfiles + # + # Usage: + # npm = hermesNpmLib.mkNpmPassthru { folder = "ui-tui"; attr = "tui"; pname = "hermes-tui"; }; + # pkgs.buildNpmPackage (npm // { ... } # or: + # pkgs.buildNpmPackage ({ ... } // npm) + mkNpmPassthru = + { folder, # repo-relative folder with package.json, e.g. "ui-tui" + attr, # flake package attr, e.g. "tui" + pname, # e.g. "hermes-tui" + nixFile ? "nix/${attr}.nix", # defaults to nix/.nix + }: + { + patchPhase = '' + runHook prePatch + sed -i -z 's/\n$//' package-lock.json + runHook postPatch + ''; + + nativeBuildInputs = [ + (pkgs.writeShellScriptBin "update_${attr}_lockfile" '' + set -euox pipefail + + REPO_ROOT=$(git rev-parse --show-toplevel) + + cd "$REPO_ROOT/${folder}" + rm -rf node_modules/ + npm cache clean --force + CI=true npm install + ${pkgs.lib.getExe npm-lockfile-fix} ./package-lock.json + + NIX_FILE="$REPO_ROOT/${nixFile}" + sed -i "s/hash = \"[^\"]*\";/hash = \"\";/" $NIX_FILE + NIX_OUTPUT=$(nix build .#${attr} 2>&1 || true) + NEW_HASH=$(echo "$NIX_OUTPUT" | grep 'got:' | awk '{print $2}') + echo got new hash $NEW_HASH + sed -i "s|hash = \"[^\"]*\";|hash = \"$NEW_HASH\";|" $NIX_FILE + nix build .#${attr} + echo "Updated npm hash in $NIX_FILE to $NEW_HASH" + '') + ]; + + passthru = { + devShellHook = pkgs.writeShellScript "npm-dev-hook-${pname}" '' + REPO_ROOT=$(git rev-parse --show-toplevel) + + _hermes_npm_stamp() { + sha256sum "${folder}/package.json" "${folder}/package-lock.json" \ + 2>/dev/null | sha256sum | awk '{print $1}' + } + STAMP=".nix-stamps/${pname}" + STAMP_VALUE="$(_hermes_npm_stamp)" + if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$STAMP_VALUE" ]; then + echo "${pname}: installing npm dependencies..." + ( cd ${folder} && CI=true npm install --silent --no-fund --no-audit 2>/dev/null ) + + # Auto-update the nix hash so it stays in sync with the lockfile + echo "${pname}: prefetching npm deps..." + NIX_FILE="$REPO_ROOT/${nixFile}" + if NEW_HASH=$(${pkgs.lib.getExe pkgs.prefetch-npm-deps} "${folder}/package-lock.json" 2>/dev/null); then + sed -i "s|hash = \"sha256-[A-Za-z0-9+/=]+\"|hash = \"$NEW_HASH\";|" "$NIX_FILE" + echo "${pname}: updated hash to $NEW_HASH" + else + echo "${pname}: warning: prefetch failed, run 'nix run .#fix-lockfiles -- --apply' manually" >&2 + fi + + mkdir -p .nix-stamps + _hermes_npm_stamp > "$STAMP" + fi + unset -f _hermes_npm_stamp + ''; + + npmLockfile = { + inherit attr folder nixFile; + }; + }; + }; + + # Aggregate `fix-lockfiles` bin from a list of packages carrying + # passthru.npmLockfile = { attr; folder; nixFile; }; + # Invocations: + # fix-lockfiles --check # exit 1 if any hash is stale + # fix-lockfiles --apply # rewrite stale hashes in place + # Writes machine-readable fields (stale, changed, report) to $GITHUB_OUTPUT + # when set, so CI workflows can post a sticky PR comment directly. + mkFixLockfiles = + { + packages, # list of packages with passthru.npmLockfile + }: + let + entries = map (p: p.passthru.npmLockfile) packages; + entryArgs = pkgs.lib.concatMapStringsSep " " (e: "\"${e.attr}:${e.folder}:${e.nixFile}\"") entries; + in + pkgs.writeShellScriptBin "fix-lockfiles" '' + set -uox pipefail + MODE="''${1:---check}" + case "$MODE" in + --check|--apply) ;; + -h|--help) + echo "usage: fix-lockfiles [--check|--apply]" + exit 0 ;; + *) + echo "usage: fix-lockfiles [--check|--apply]" >&2 + exit 2 ;; + esac + + ENTRIES=(${entryArgs}) + + REPO_ROOT="$(git rev-parse --show-toplevel)" + cd "$REPO_ROOT" + + # When running in GH Actions, emit Markdown links in the report pointing + # at the offending line of the nix file (and the lockfile) at the exact + # commit that was checked. LINK_SHA should be set by the workflow to the + # PR head SHA; falls back to GITHUB_SHA (which on pull_request is the + # test-merge commit, still browseable). + LINK_SERVER="''${GITHUB_SERVER_URL:-https://github.com}" + LINK_REPO="''${GITHUB_REPOSITORY:-}" + LINK_SHA="''${LINK_SHA:-''${GITHUB_SHA:-}}" + + STALE=0 + FIXED=0 + REPORT="" + + 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 --print-build-logs 2>&1) + STATUS=$? + if [ "$STATUS" -eq 0 ]; then + echo " ok" + continue + fi + + NEW_HASH=$(echo "$OUTPUT" | awk '/got:/ {print $2; exit}') + if [ -z "$NEW_HASH" ]; then + 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 + + if [ -n "$LINK_REPO" ] && [ -n "$LINK_SHA" ]; then + NIX_URL="$LINK_SERVER/$LINK_REPO/blob/$LINK_SHA/$NIX_FILE#L$HASH_LINE" + LOCK_URL="$LINK_SERVER/$LINK_REPO/blob/$LINK_SHA/$LOCK_FILE" + REPORT+="- [\`$NIX_FILE:$HASH_LINE\`]($NIX_URL) (\`.#$ATTR\`): \`$OLD_HASH\` → \`$NEW_HASH\` — lockfile: [\`$LOCK_FILE\`]($LOCK_URL)"$'\n' + else + REPORT+="- \`$NIX_FILE:$HASH_LINE\` (\`.#$ATTR\`): \`$OLD_HASH\` → \`$NEW_HASH\`"$'\n' + fi + + if [ "$MODE" = "--apply" ]; then + sed -i "s|hash = \"sha256-[^\"]*\";|hash = \"$NEW_HASH\";|" "$NIX_FILE" + nix build ".#$ATTR.npmDeps" --no-link --print-build-logs + FIXED=1 + echo " fixed" + fi + done + + if [ -n "''${GITHUB_OUTPUT:-}" ]; then + { + [ "$STALE" -eq 1 ] && echo "stale=true" || echo "stale=false" + [ "$FIXED" -eq 1 ] && echo "changed=true" || echo "changed=false" + if [ -n "$REPORT" ]; then + echo "report<> "$GITHUB_OUTPUT" + fi + + if [ "$STALE" -eq 1 ] && [ "$MODE" = "--check" ]; then + echo + echo "Stale lockfile hashes detected. Run:" + echo " nix run .#fix-lockfiles -- --apply" + exit 1 + fi + + exit 0 + ''; +} diff --git a/nix/packages.nix b/nix/packages.nix index 912be7843..721546851 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -8,10 +8,14 @@ inherit (inputs) uv2nix pyproject-nix pyproject-build-systems; }; - hermesTui = pkgs.callPackage ./tui.nix { + hermesNpmLib = pkgs.callPackage ./lib.nix { npm-lockfile-fix = inputs'.npm-lockfile-fix.packages.default; }; + hermesTui = pkgs.callPackage ./tui.nix { + inherit hermesNpmLib; + }; + # Import bundled skills, excluding runtime caches bundledSkills = pkgs.lib.cleanSourceWith { src = ../skills; @@ -19,7 +23,7 @@ }; hermesWeb = pkgs.callPackage ./web.nix { - npm-lockfile-fix = inputs'.npm-lockfile-fix.packages.default; + inherit hermesNpmLib; }; runtimeDeps = with pkgs; [ @@ -111,6 +115,10 @@ tui = hermesTui; web = hermesWeb; + + fix-lockfiles = hermesNpmLib.mkFixLockfiles { + packages = [ hermesTui hermesWeb ]; + }; }; }; } diff --git a/nix/tui.nix b/nix/tui.nix index 7303edecb..04bbfa034 100644 --- a/nix/tui.nix +++ b/nix/tui.nix @@ -1,18 +1,18 @@ # nix/tui.nix — Hermes TUI (Ink/React) compiled with tsc and bundled -{ pkgs, npm-lockfile-fix, ... }: +{ pkgs, hermesNpmLib, ... }: let src = ../ui-tui; npmDeps = pkgs.fetchNpmDeps { inherit src; - hash = "sha256-mG3vpgGi4ljt4X3XIf3I/5mIcm+rVTUAmx2DQ6YVA90="; + hash = "sha256-RU4qSHgJPMyfRSEJDzkG4+MReDZDc6QbTD2wisa5QE0="; }; + npm = hermesNpmLib.mkNpmPassthru { folder = "ui-tui"; attr = "tui"; pname = "hermes-tui"; }; + packageJson = builtins.fromJSON (builtins.readFile (src + "/package.json")); version = packageJson.version; - - npmLockHash = builtins.hashString "sha256" (builtins.readFile ../ui-tui/package-lock.json); in -pkgs.buildNpmPackage { +pkgs.buildNpmPackage (npm // { pname = "hermes-tui"; inherit src npmDeps version; @@ -37,41 +37,4 @@ pkgs.buildNpmPackage { runHook postInstall ''; - - nativeBuildInputs = [ - (pkgs.writeShellScriptBin "update_tui_lockfile" '' - set -euox pipefail - - # get root of repo - REPO_ROOT=$(git rev-parse --show-toplevel) - - # cd into ui-tui and reinstall - cd "$REPO_ROOT/ui-tui" - rm -rf node_modules/ - npm cache clean --force - CI=true npm install # ci env var to suppress annoying unicode install banner lag - ${pkgs.lib.getExe npm-lockfile-fix} ./package-lock.json - - NIX_FILE="$REPO_ROOT/nix/tui.nix" - # compute the new hash - sed -i "s/hash = \"[^\"]*\";/hash = \"\";/" $NIX_FILE - NIX_OUTPUT=$(nix build .#tui 2>&1 || true) - NEW_HASH=$(echo "$NIX_OUTPUT" | grep 'got:' | awk '{print $2}') - echo got new hash $NEW_HASH - sed -i "s|hash = \"[^\"]*\";|hash = \"$NEW_HASH\";|" $NIX_FILE - nix build .#tui - echo "Updated npm hash in $NIX_FILE to $NEW_HASH" - '') - ]; - - passthru.devShellHook = '' - STAMP=".nix-stamps/hermes-tui" - STAMP_VALUE="${npmLockHash}" - if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$STAMP_VALUE" ]; then - echo "hermes-tui: installing npm dependencies..." - cd ui-tui && CI=true npm install --silent --no-fund --no-audit 2>/dev/null && cd .. - mkdir -p .nix-stamps - echo "$STAMP_VALUE" > "$STAMP" - fi - ''; -} +}) diff --git a/nix/web.nix b/nix/web.nix index 247889753..fc7772896 100644 --- a/nix/web.nix +++ b/nix/web.nix @@ -1,15 +1,15 @@ # nix/web.nix — Hermes Web Dashboard (Vite/React) frontend build -{ pkgs, npm-lockfile-fix, ... }: +{ pkgs, hermesNpmLib, ... }: let src = ../web; npmDeps = pkgs.fetchNpmDeps { inherit src; - hash = "sha256-Y0pOzdFG8BLjfvCLmsvqYpjxFjAQabXp1i7X9W/cCU4="; + hash = "sha256-TS/vrCHbdvXkPcAPxImKzAd2pdDCrKlgYZkXBMQ+TEg="; }; - npmLockHash = builtins.hashString "sha256" (builtins.readFile ../web/package-lock.json); + npm = hermesNpmLib.mkNpmPassthru { folder = "web"; attr = "web"; pname = "hermes-web"; }; in -pkgs.buildNpmPackage { +pkgs.buildNpmPackage (npm // { pname = "hermes-web"; version = "0.0.0"; inherit src npmDeps; @@ -26,38 +26,4 @@ pkgs.buildNpmPackage { cp -r dist $out runHook postInstall ''; - - nativeBuildInputs = [ - (pkgs.writeShellScriptBin "update_web_lockfile" '' - set -euox pipefail - - REPO_ROOT=$(git rev-parse --show-toplevel) - - cd "$REPO_ROOT/web" - rm -rf node_modules/ - npm cache clean --force - CI=true npm install - ${pkgs.lib.getExe npm-lockfile-fix} ./package-lock.json - - NIX_FILE="$REPO_ROOT/nix/web.nix" - sed -i "s/hash = \"[^\"]*\";/hash = \"\";/" $NIX_FILE - NIX_OUTPUT=$(nix build .#web 2>&1 || true) - NEW_HASH=$(echo "$NIX_OUTPUT" | grep 'got:' | awk '{print $2}') - echo got new hash $NEW_HASH - sed -i "s|hash = \"[^\"]*\";|hash = \"$NEW_HASH\";|" $NIX_FILE - nix build .#web - echo "Updated npm hash in $NIX_FILE to $NEW_HASH" - '') - ]; - - passthru.devShellHook = '' - STAMP=".nix-stamps/hermes-web" - STAMP_VALUE="${npmLockHash}" - if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$STAMP_VALUE" ]; then - echo "hermes-web: installing npm dependencies..." - cd web && CI=true npm install --silent --no-fund --no-audit 2>/dev/null && cd .. - mkdir -p .nix-stamps - echo "$STAMP_VALUE" > "$STAMP" - fi - ''; -} +}) diff --git a/ui-tui/package-lock.json b/ui-tui/package-lock.json index 1e8e5cfa4..46c83d195 100644 --- a/ui-tui/package-lock.json +++ b/ui-tui/package-lock.json @@ -89,7 +89,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -319,6 +318,31 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1365,6 +1389,29 @@ "node": ">=14.0.0" } }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.0.0-rc.15", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", @@ -1462,7 +1509,6 @@ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.19.0" } @@ -1473,7 +1519,6 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1484,7 +1529,6 @@ "integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.1", @@ -1514,7 +1558,6 @@ "integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", @@ -1832,7 +1875,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2168,7 +2210,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2854,7 +2895,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3750,7 +3790,6 @@ "resolved": "https://registry.npmjs.org/ink-text-input/-/ink-text-input-6.0.0.tgz", "integrity": "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw==", "license": "MIT", - "peer": true, "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" @@ -5107,7 +5146,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5207,7 +5245,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5980,7 +6017,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -6107,7 +6143,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6217,7 +6252,6 @@ "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -6626,7 +6660,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/web/package-lock.json b/web/package-lock.json index 474fd2f4e..c522d8ba0 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -70,7 +70,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1104,7 +1103,6 @@ "resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.17.tgz", "integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==", "license": "ISC", - "peer": true, "dependencies": { "d3": "^7.9.0", "interval-tree-1d": "^1.0.0", @@ -1757,7 +1755,6 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.0.tgz", "integrity": "sha512-90abYK2q5/qDM+GACs9zRvc5KhEEpEWqWlHSd64zTPNxg+9wCJvTfyD9x2so7hlQhjRYO1Fa6flR3BC/kpTFkA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", @@ -2492,7 +2489,6 @@ "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2502,7 +2498,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2513,7 +2508,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2578,7 +2572,6 @@ "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", @@ -2874,7 +2867,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3027,7 +3019,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3535,7 +3526,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3849,7 +3839,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4228,8 +4217,7 @@ "version": "3.15.0", "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", - "license": "Standard 'no charge' license: https://gsap.com/standard-license.", - "peer": true + "license": "Standard 'no charge' license: https://gsap.com/standard-license." }, "node_modules/has-flag": { "version": "4.0.0", @@ -4544,7 +4532,6 @@ "resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz", "integrity": "sha512-BcjnfUX8jpmwZUz2L7AfBtF9vn4ggTH33hmeufDULbP3YgNZ/C+ss/oO3stbrqRQyaOmRwy70y7BGTGO81S3rA==", "license": "MIT", - "peer": true, "dependencies": { "@radix-ui/react-portal": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.8", @@ -4966,7 +4953,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": "^20.0.0 || >=22.0.0" } @@ -5094,7 +5080,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5166,7 +5151,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5186,7 +5170,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5549,8 +5532,7 @@ "version": "0.180.0", "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -5615,7 +5597,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5701,7 +5682,6 @@ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -5717,7 +5697,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -5839,7 +5818,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }