mirror of
https://github.com/0xrsydn/nix-hermes-agent.git
synced 2026-08-07 00:53:52 +00:00
feat: bump hermes-agent 0.2.0 → 0.3.0 + auto-update CI
- Update package.nix to v0.3.0 (NousResearch/hermes-agent v2026.3.17) - Add scripts/update-pins.sh — checks latest stable release, prefetches, validates build - Add .github/workflows/update-pins.yml — runs every 6h + manual dispatch - Pattern follows nix-openclaw's yolo-update approach
This commit is contained in:
parent
e7b70a7b4d
commit
010a1b6644
4 changed files with 526 additions and 3 deletions
30
.github/workflows/update-pins.yml
vendored
Normal file
30
.github/workflows/update-pins.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: Update Hermes Agent Pins
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Check every 6 hours — hermes-agent doesn't release as often as openclaw
|
||||
- cron: "15 */6 * * *"
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@v13
|
||||
|
||||
- name: Run updater
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git config user.name "nix-hermes-agent-bot"
|
||||
git config user.email "bot@nix-hermes-agent.local"
|
||||
scripts/update-pins.sh
|
||||
335
docs/CONFIG-MAP.md
Normal file
335
docs/CONFIG-MAP.md
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
# Hermes Agent Configuration Map
|
||||
|
||||
**Source:** `hermes_cli/config.py` + `gateway/config.py` (v0.2.0)
|
||||
|
||||
Everything below can be set declaratively via the NixOS module's `services.hermes-agent.config` option (rendered as `cli-config.yaml`), `environmentFiles` (secrets), or `environment` (non-secret env vars).
|
||||
|
||||
---
|
||||
|
||||
## 1. Model & Provider
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `model` | string | `"anthropic/claude-opus-4.6"` | Default model (`provider/model` format) |
|
||||
| `toolsets` | list | `["hermes-cli"]` | Enabled toolsets (e.g. `["all"]`, `["hermes-cli", "browser"]`) |
|
||||
|
||||
**Auth providers** (resolved automatically based on env vars / OAuth):
|
||||
- Nous Portal (OAuth) → `hermes login --provider nous`
|
||||
- OpenAI Codex (OAuth) → `hermes login --provider openai-codex`
|
||||
- OpenRouter → `OPENROUTER_API_KEY`
|
||||
- Z.AI / GLM → `GLM_API_KEY` / `ZAI_API_KEY`
|
||||
- Kimi → `KIMI_API_KEY`
|
||||
- MiniMax → `MINIMAX_API_KEY`
|
||||
- Anthropic → `ANTHROPIC_API_KEY` or Claude Code OAuth
|
||||
|
||||
---
|
||||
|
||||
## 2. Agent Behavior
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `agent.max_turns` | int | `90` | Max tool-calling iterations per conversation |
|
||||
|
||||
---
|
||||
|
||||
## 3. Terminal / Sandbox
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `terminal.backend` | string | `"local"` | `local` / `docker` / `singularity` / `modal` / `daytona` / `ssh` |
|
||||
| `terminal.cwd` | string | `"."` | Working directory for commands |
|
||||
| `terminal.timeout` | int | `180` | Command timeout (seconds) |
|
||||
| `terminal.docker_image` | string | `"nikolaik/python-nodejs:..."` | Docker image |
|
||||
| `terminal.singularity_image` | string | ... | Singularity image |
|
||||
| `terminal.modal_image` | string | ... | Modal image |
|
||||
| `terminal.daytona_image` | string | ... | Daytona image |
|
||||
| `terminal.container_cpu` | int | `1` | Container CPU limit |
|
||||
| `terminal.container_memory` | int | `5120` | Container memory (MB) |
|
||||
| `terminal.container_disk` | int | `51200` | Container disk (MB) |
|
||||
| `terminal.container_persistent` | bool | `true` | Persist filesystem across sessions |
|
||||
| `terminal.docker_volumes` | list | `[]` | Docker volume mounts (`host:container`) |
|
||||
|
||||
**SSH backend env vars:**
|
||||
- `TERMINAL_SSH_HOST`, `TERMINAL_SSH_USER`, `TERMINAL_SSH_KEY`
|
||||
|
||||
---
|
||||
|
||||
## 4. Context Compression
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `compression.enabled` | bool | `true` | Auto-compress long conversations |
|
||||
| `compression.threshold` | float | `0.50` | Context usage % to trigger compression |
|
||||
| `compression.summary_model` | string | `"google/gemini-3-flash-preview"` | Model for summaries |
|
||||
| `compression.summary_provider` | string | `"auto"` | Provider for compression model |
|
||||
|
||||
---
|
||||
|
||||
## 5. Auxiliary Models
|
||||
|
||||
Side-task models (vision, web extraction, etc). Each has the same schema:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `auxiliary.<task>.provider` | string | `"auto"` | Provider override |
|
||||
| `auxiliary.<task>.model` | string | `""` | Model override |
|
||||
| `auxiliary.<task>.base_url` | string | `""` | Direct endpoint URL |
|
||||
| `auxiliary.<task>.api_key` | string | `""` | API key for endpoint |
|
||||
|
||||
**Tasks:** `vision`, `web_extract`, `compression`, `session_search`, `skills_hub`, `mcp`, `flush_memories`
|
||||
|
||||
---
|
||||
|
||||
## 6. Delegation (Subagents)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `delegation.model` | string | `""` | Model for subagents (empty = inherit parent) |
|
||||
| `delegation.provider` | string | `""` | Provider for subagents |
|
||||
| `delegation.base_url` | string | `""` | Direct endpoint for subagents |
|
||||
| `delegation.api_key` | string | `""` | API key for delegation endpoint |
|
||||
|
||||
---
|
||||
|
||||
## 7. Display
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `display.compact` | bool | `false` | Compact output mode |
|
||||
| `display.personality` | string | `"kawaii"` | UI personality |
|
||||
| `display.resume_display` | string | `"full"` | Session resume display |
|
||||
| `display.bell_on_complete` | bool | `false` | Bell sound on completion |
|
||||
| `display.show_reasoning` | bool | `false` | Show model reasoning |
|
||||
| `display.skin` | string | `"default"` | UI skin |
|
||||
|
||||
---
|
||||
|
||||
## 8. Memory
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `memory.memory_enabled` | bool | `true` | Persistent curated memory |
|
||||
| `memory.user_profile_enabled` | bool | `true` | User profile memory |
|
||||
| `memory.memory_char_limit` | int | `2200` | Max chars for memory context |
|
||||
| `memory.user_char_limit` | int | `1375` | Max chars for user profile |
|
||||
|
||||
---
|
||||
|
||||
## 9. Browser
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `browser.inactivity_timeout` | int | `120` | Browser auto-close (seconds) |
|
||||
| `browser.record_sessions` | bool | `false` | Record browser sessions as WebM |
|
||||
|
||||
---
|
||||
|
||||
## 10. Checkpoints (Filesystem Snapshots)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `checkpoints.enabled` | bool | `false` | Auto-snapshot before destructive ops |
|
||||
| `checkpoints.max_snapshots` | int | `50` | Max checkpoints per directory |
|
||||
|
||||
---
|
||||
|
||||
## 11. TTS (Text-to-Speech)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `tts.provider` | string | `"edge"` | `edge` (free) / `elevenlabs` (premium) / `openai` |
|
||||
| `tts.edge.voice` | string | `"en-US-AriaNeural"` | Edge TTS voice |
|
||||
| `tts.elevenlabs.voice_id` | string | `"pNInz6obpgDQGcFmaJgB"` | ElevenLabs voice ID |
|
||||
| `tts.elevenlabs.model_id` | string | `"eleven_multilingual_v2"` | ElevenLabs model |
|
||||
| `tts.openai.model` | string | `"gpt-4o-mini-tts"` | OpenAI TTS model |
|
||||
| `tts.openai.voice` | string | `"alloy"` | OpenAI TTS voice |
|
||||
|
||||
---
|
||||
|
||||
## 12. STT (Speech-to-Text)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `stt.enabled` | bool | `true` | Enable voice transcription |
|
||||
| `stt.provider` | string | `"local"` | `local` (faster-whisper) / `groq` / `openai` |
|
||||
| `stt.local.model` | string | `"base"` | Local model size: tiny/base/small/medium/large-v3 |
|
||||
| `stt.openai.model` | string | `"whisper-1"` | OpenAI STT model |
|
||||
|
||||
---
|
||||
|
||||
## 13. Voice (Interactive)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `voice.record_key` | string | `"ctrl+b"` | Push-to-talk key |
|
||||
| `voice.max_recording_seconds` | int | `120` | Max recording length |
|
||||
| `voice.auto_tts` | bool | `false` | Auto-speak responses |
|
||||
| `voice.silence_threshold` | int | `200` | RMS silence threshold |
|
||||
| `voice.silence_duration` | float | `3.0` | Seconds of silence → auto-stop |
|
||||
|
||||
---
|
||||
|
||||
## 14. Human Delay (Anti-Detection)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `human_delay.mode` | string | `"off"` | `off` / `on` / `adaptive` |
|
||||
| `human_delay.min_ms` | int | `800` | Min delay (ms) |
|
||||
| `human_delay.max_ms` | int | `2500` | Max delay (ms) |
|
||||
|
||||
---
|
||||
|
||||
## 15. Security
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `security.redact_secrets` | bool | `true` | Redact API keys from tool output |
|
||||
| `security.tirith_enabled` | bool | `true` | Pre-exec scanning via tirith |
|
||||
| `security.tirith_path` | string | `"tirith"` | Path to tirith binary |
|
||||
| `security.tirith_timeout` | int | `5` | Tirith scan timeout (seconds) |
|
||||
| `security.tirith_fail_open` | bool | `true` | Allow exec if tirith fails |
|
||||
|
||||
---
|
||||
|
||||
## 16. Discord
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `discord.require_mention` | bool | `true` | Require @mention to respond in channels |
|
||||
| `discord.free_response_channels` | string | `""` | Comma-separated channel IDs for free response |
|
||||
| `discord.auto_thread` | bool | `true` | Auto-create threads on @mention |
|
||||
|
||||
---
|
||||
|
||||
## 17. Session Reset Policy
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `session_reset.mode` | string | `"both"` | `daily` / `idle` / `both` / `none` |
|
||||
| `session_reset.at_hour` | int | `4` | Daily reset hour (0-23, local time) |
|
||||
| `session_reset.idle_minutes` | int | `1440` | Idle timeout before reset (minutes) |
|
||||
|
||||
---
|
||||
|
||||
## 18. Miscellaneous
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `timezone` | string | `""` | IANA timezone (empty = server-local) |
|
||||
| `command_allowlist` | list | `[]` | Permanently allowed dangerous commands |
|
||||
| `quick_commands` | dict | `{}` | User-defined slash commands (exec type) |
|
||||
| `personalities` | dict | `{}` | Custom personality prompts |
|
||||
| `prefill_messages_file` | string | `""` | Path to JSON prefill messages |
|
||||
| `honcho` | dict | `{}` | Honcho AI-native memory overrides |
|
||||
|
||||
---
|
||||
|
||||
## 19. MCP Servers
|
||||
|
||||
Defined via `services.hermes-agent.mcpServers` in the NixOS module:
|
||||
|
||||
```nix
|
||||
mcpServers = {
|
||||
my-server = {
|
||||
command = "npx";
|
||||
args = [ "-y" "my-mcp-server" ];
|
||||
env = { API_KEY = "..."; };
|
||||
timeout = 30;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Merged into `config.mcp_servers` in the rendered YAML.
|
||||
|
||||
---
|
||||
|
||||
## 20. Fallback Model (Failover)
|
||||
|
||||
Not in defaults — must be explicitly configured:
|
||||
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: openrouter # openrouter | nous | openai-codex | zai | kimi-coding | minimax
|
||||
model: anthropic/claude-sonnet-4
|
||||
# Optional for custom endpoints:
|
||||
# base_url: "http://..."
|
||||
# api_key_env: "MY_CUSTOM_KEY"
|
||||
```
|
||||
|
||||
Triggers on: 429 (rate limit), 529 (overload), 503 (service error), connection failures.
|
||||
|
||||
---
|
||||
|
||||
## 21. Environment Variables (Secrets via `environmentFiles`)
|
||||
|
||||
### Provider Keys
|
||||
| Variable | Provider |
|
||||
|----------|----------|
|
||||
| `OPENROUTER_API_KEY` | OpenRouter (default fallback) |
|
||||
| `GLM_API_KEY` / `ZAI_API_KEY` | Z.AI / GLM |
|
||||
| `KIMI_API_KEY` | Kimi / Moonshot |
|
||||
| `MINIMAX_API_KEY` | MiniMax (international) |
|
||||
| `MINIMAX_CN_API_KEY` | MiniMax (China) |
|
||||
| `ANTHROPIC_API_KEY` | Anthropic direct |
|
||||
| `ANTHROPIC_TOKEN` | Anthropic OAuth/setup token |
|
||||
|
||||
### Tool Keys
|
||||
| Variable | Tool |
|
||||
|----------|------|
|
||||
| `FIRECRAWL_API_KEY` | Web search/scraping |
|
||||
| `BROWSERBASE_API_KEY` | Cloud browser |
|
||||
| `FAL_KEY` | Image generation |
|
||||
| `ELEVENLABS_API_KEY` | Premium TTS |
|
||||
| `VOICE_TOOLS_OPENAI_KEY` | Whisper STT + OpenAI TTS |
|
||||
| `GITHUB_TOKEN` | Skills Hub |
|
||||
| `HONCHO_API_KEY` | AI-native memory |
|
||||
| `TINKER_API_KEY` | RL training |
|
||||
| `WANDB_API_KEY` | Experiment tracking |
|
||||
|
||||
### Messaging Tokens
|
||||
| Variable | Platform |
|
||||
|----------|----------|
|
||||
| `TELEGRAM_BOT_TOKEN` | Telegram |
|
||||
| `TELEGRAM_ALLOWED_USERS` | Telegram user whitelist |
|
||||
| `DISCORD_BOT_TOKEN` | Discord |
|
||||
| `DISCORD_ALLOWED_USERS` | Discord user whitelist |
|
||||
| `SLACK_BOT_TOKEN` | Slack |
|
||||
| `SLACK_APP_TOKEN` | Slack (Socket Mode) |
|
||||
| `SIGNAL_HTTP_URL` | Signal |
|
||||
| `SIGNAL_ACCOUNT` | Signal |
|
||||
| `HASS_TOKEN` | Home Assistant |
|
||||
| `EMAIL_ADDRESS` / `EMAIL_PASSWORD` / `EMAIL_IMAP_HOST` / `EMAIL_SMTP_HOST` | Email |
|
||||
| `GATEWAY_ALLOW_ALL_USERS` | Allow all users (global) |
|
||||
|
||||
### Agent Settings
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `MESSAGING_CWD` | Working directory for messaging |
|
||||
| `SUDO_PASSWORD` | Sudo password for terminal |
|
||||
| `HERMES_MAX_ITERATIONS` | Max iterations override |
|
||||
|
||||
---
|
||||
|
||||
## 22. Custom Providers (config.yaml)
|
||||
|
||||
```yaml
|
||||
custom_providers:
|
||||
- name: "local-vllm"
|
||||
base_url: "http://localhost:8000/v1"
|
||||
api_key: "" # optional
|
||||
```
|
||||
|
||||
Use with: `hermes chat --provider custom:local-vllm`
|
||||
|
||||
---
|
||||
|
||||
## Summary: What Goes Where
|
||||
|
||||
| What | Where | NixOS Module Option |
|
||||
|------|-------|---------------------|
|
||||
| Model, toolsets, display, terminal, etc. | `cli-config.yaml` | `config = { ... }` |
|
||||
| API keys, tokens, passwords | `.env` / env vars | `environmentFiles = [ "/run/secrets/hermes.env" ]` |
|
||||
| OAuth tokens (Nous, Codex) | `auth.json` | Runtime via `hermes login` (persistent) |
|
||||
| MCP servers | `cli-config.yaml` | `mcpServers = { ... }` |
|
||||
| SOUL.md, AGENTS.md, USER.md | Workspace files | `documents = { ... }` |
|
||||
| Gateway platform config | env vars or `gateway.json` | `environmentFiles` |
|
||||
|
|
@ -72,14 +72,14 @@ let
|
|||
pythonImportsCheck = [ "acp" ];
|
||||
};
|
||||
|
||||
version = "0.2.0";
|
||||
rev = "64d333204bb2e32cc90a58b5ec5a4db127396dfc";
|
||||
version = "0.3.0";
|
||||
rev = "6ebb816e5611aaf1f3f7187ba8b10e985e899c75";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "NousResearch";
|
||||
repo = "hermes-agent";
|
||||
inherit rev;
|
||||
hash = "sha256-Li8jPEFDthj/AKmlwJhLWxItc34qcTrmJUDQ4kaSxVg=";
|
||||
hash = "sha256-JGjusff/jGjvCCdUtl9IErBTGmpIq6BVA5Gj8mwqVYg=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
|
|
|
|||
158
scripts/update-pins.sh
Executable file
158
scripts/update-pins.sh
Executable file
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Auto-update nix-hermes-agent to track latest stable release from NousResearch/hermes-agent.
|
||||
# Designed to run in GitHub Actions (see .github/workflows/update-pins.yml).
|
||||
# Similar to nix-openclaw's update-pins.sh but tracks releases instead of HEAD.
|
||||
|
||||
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
package_file="$repo_root/package.nix"
|
||||
|
||||
log() {
|
||||
printf '>> %s\n' "$*"
|
||||
}
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq is required but not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Resolve latest stable release ---
|
||||
log "Fetching latest release from NousResearch/hermes-agent"
|
||||
release_json=$(gh api /repos/NousResearch/hermes-agent/releases/latest 2>/dev/null || true)
|
||||
if [[ -z "$release_json" ]]; then
|
||||
echo "Failed to fetch latest release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
release_tag=$(printf '%s' "$release_json" | jq -r '.tag_name // empty')
|
||||
release_name=$(printf '%s' "$release_json" | jq -r '.name // empty')
|
||||
if [[ -z "$release_tag" ]]; then
|
||||
echo "No release tag found" >&2
|
||||
exit 1
|
||||
fi
|
||||
log "Latest release: $release_tag ($release_name)"
|
||||
|
||||
# Extract version from release name or tag (e.g. "Hermes Agent v0.3.0 (v2026.3.17)" → "0.3.0")
|
||||
upstream_version=$(printf '%s' "$release_name" | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
|
||||
if [[ -z "$upstream_version" ]]; then
|
||||
# Fallback: try tag itself
|
||||
upstream_version=$(printf '%s' "$release_tag" | grep -oP 'v?\K[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)
|
||||
fi
|
||||
if [[ -z "$upstream_version" ]]; then
|
||||
echo "Could not parse version from release tag=$release_tag name=$release_name" >&2
|
||||
exit 1
|
||||
fi
|
||||
log "Parsed version: $upstream_version"
|
||||
|
||||
# --- Compare with current ---
|
||||
current_version=$(awk -F'"' '/^ version = /{print $2}' "$package_file" | head -1)
|
||||
log "Current pinned version: $current_version"
|
||||
|
||||
if [[ "$current_version" == "$upstream_version" ]]; then
|
||||
log "Already up to date ($current_version). Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
log "Update available: $current_version → $upstream_version"
|
||||
|
||||
# --- Resolve the commit SHA for the release tag ---
|
||||
tag_sha=$(gh api "/repos/NousResearch/hermes-agent/git/ref/tags/${release_tag}" --jq '.object.sha' 2>/dev/null || true)
|
||||
if [[ -z "$tag_sha" ]]; then
|
||||
# Maybe it's an annotated tag — dereference
|
||||
tag_sha=$(gh api "/repos/NousResearch/hermes-agent/git/ref/tags/${release_tag}" --jq '.object.sha' 2>/dev/null || true)
|
||||
if [[ -n "$tag_sha" ]]; then
|
||||
# Check if it's an annotated tag (type=tag) and dereference
|
||||
tag_type=$(gh api "/repos/NousResearch/hermes-agent/git/tags/${tag_sha}" --jq '.object.type // empty' 2>/dev/null || true)
|
||||
if [[ "$tag_type" == "commit" ]]; then
|
||||
tag_sha=$(gh api "/repos/NousResearch/hermes-agent/git/tags/${tag_sha}" --jq '.object.sha' 2>/dev/null || true)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$tag_sha" ]]; then
|
||||
# Last resort: ls-remote
|
||||
tag_sha=$(git ls-remote https://github.com/NousResearch/hermes-agent.git "refs/tags/${release_tag}" | awk '{print $1}' || true)
|
||||
fi
|
||||
|
||||
if [[ -z "$tag_sha" ]]; then
|
||||
echo "Failed to resolve commit SHA for tag $release_tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
log "Release commit SHA: $tag_sha"
|
||||
|
||||
# --- Prefetch source ---
|
||||
source_url="https://github.com/NousResearch/hermes-agent/archive/${tag_sha}.tar.gz"
|
||||
log "Prefetching source tarball (with submodules via fetchFromGitHub)..."
|
||||
|
||||
# Use nix-prefetch-url for the base archive, but we need fetchFromGitHub hash (includes submodules).
|
||||
# Best approach: temporarily update package.nix with empty hash and let nix build tell us the right one.
|
||||
# Or use nix store prefetch-file for the tarball (no submodules).
|
||||
# Since the package uses fetchSubmodules = true, we need the fetchFromGitHub hash.
|
||||
|
||||
# Strategy: use nix to evaluate the hash by building with a fake hash
|
||||
log "Computing fetchFromGitHub hash (with submodules)..."
|
||||
|
||||
# Save original
|
||||
cp "$package_file" "$package_file.bak"
|
||||
|
||||
# Update version, rev, and set hash to empty
|
||||
perl -0pi -e "s|version = \"[^\"]+\";|version = \"${upstream_version}\";|" "$package_file"
|
||||
perl -0pi -e "s|rev = \"[^\"]+\";|rev = \"${tag_sha}\";|" "$package_file"
|
||||
perl -0pi -e 's|hash = "sha256-[^"]+";|hash = "";|' "$package_file"
|
||||
|
||||
# Build and capture the correct hash from the error
|
||||
build_log=$(mktemp)
|
||||
log "Running nix build to get correct hash..."
|
||||
if nix build .#hermes-agent --accept-flake-config >"$build_log" 2>&1; then
|
||||
log "Build succeeded with empty hash?! Unexpected, but OK."
|
||||
source_hash=""
|
||||
else
|
||||
source_hash=$(grep -oP 'got: *\Ksha256-[A-Za-z0-9+/=]+' "$build_log" | head -1 || true)
|
||||
if [[ -z "$source_hash" ]]; then
|
||||
log "Build failed but couldn't extract hash. Build log:"
|
||||
tail -50 "$build_log" >&2
|
||||
cp "$package_file.bak" "$package_file"
|
||||
rm -f "$build_log" "$package_file.bak"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
rm -f "$build_log"
|
||||
log "Source hash: $source_hash"
|
||||
|
||||
# Update with the correct hash
|
||||
if [[ -n "$source_hash" ]]; then
|
||||
perl -0pi -e "s|hash = \"[^\"]*\";|hash = \"${source_hash}\";|" "$package_file"
|
||||
fi
|
||||
|
||||
# --- Validate build ---
|
||||
build_log=$(mktemp)
|
||||
log "Validating full build..."
|
||||
if ! nix build .#hermes-agent --accept-flake-config >"$build_log" 2>&1; then
|
||||
log "Build validation FAILED. This likely means dependencies changed upstream."
|
||||
log "Build log (last 100 lines):"
|
||||
tail -100 "$build_log" >&2
|
||||
cp "$package_file.bak" "$package_file"
|
||||
rm -f "$build_log" "$package_file.bak"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$build_log" "$package_file.bak"
|
||||
log "Build validation PASSED ✅"
|
||||
|
||||
# --- Commit and push ---
|
||||
if git diff --quiet "$package_file"; then
|
||||
log "No changes to commit (shouldn't happen)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Committing update"
|
||||
git add "$package_file"
|
||||
git commit -m "🤖 bump hermes-agent ${current_version} → ${upstream_version} (${release_tag})" \
|
||||
-m "Upstream: https://github.com/NousResearch/hermes-agent/releases/tag/${release_tag}" \
|
||||
-m "Tests: nix build .#hermes-agent (passed)"
|
||||
|
||||
log "Pushing to main"
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
git push origin HEAD:main
|
||||
|
||||
log "Done! Updated hermes-agent to ${upstream_version} (${release_tag})"
|
||||
Loading…
Add table
Add a link
Reference in a new issue