Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Tests

on:
push:
branches:
- "main"
paths-ignore:
- "README.md"
pull_request:
branches:
- "main"
paths-ignore:
- "README.md"
workflow_dispatch:

jobs:
test:
name: Run Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python dependencies
run: pip install pyyaml
- name: Run Node.js Tests
run: npm run test:node
- name: Run Python Tests
run: npm run test:python
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ build_*/
# Node
node_modules/

# Python
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/

# OS files
.DS_Store
Thumbs.db
Expand Down
38 changes: 32 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,43 @@ DAppNode package for [Hermes Agent](https://hermes-agent.nousresearch.com/) by [

## Features

- **Multi-LLM Support**: OpenRouter (200+ models), OpenAI, Anthropic, Google Gemini, Ollama (local), Groq, DeepSeek, and more
- **Messaging Gateway**: Telegram, Discord, Slack, WhatsApp, Signal — all from a single process
- **Multi-LLM Support**: DAppNode Nexus (private AI), OpenRouter (200+ models), OpenAI, Anthropic, Google Gemini, Ollama (local), Groq, Mistral AI, DeepSeek, Hugging Face, GitHub Copilot, and custom endpoints
- **Messaging Gateway**: Telegram, WhatsApp, Discord, Slack, Signal — all from a single process
- **Self-Improving Skills**: Agent creates and refines skills from experience
- **Persistent Memory**: Cross-session recall with user modeling
- **Cron Scheduling**: Automated tasks delivered to any platform
- **Setup Wizard**: Web-based configuration for providers, models, and integrations
- **Setup Wizard & Dashboard**: Web-based configuration with live model discovery, auto-login proxy, and built-in system diagnostics
- **Web Terminal**: Built-in browser terminal (`ttyd`) for running `hermes` CLI commands

## Network Services & Ports

| Service | Container Port | URL | Description |
|---|---|---|---|
| **Setup Wizard** | `8080` | `http://hermes-agent.dappnode:8080` | Web UI for provider configuration & overview |
| **Hermes Dashboard** | `8081` | `http://hermes-agent.dappnode:8080/dashboard` | Web dashboard for sessions, memory, & skills |
| **Gateway API** | `3000` | `http://hermes-agent.dappnode:3000` | OpenAI-compatible HTTP API & web interface |
| **Web Terminal** | `7681` | `http://hermes-agent.dappnode:7681` | Browser terminal for `hermes` CLI commands |

## Getting Started

1. Install the package from the DAppNode Package Store
2. Open the **Setup Wizard** at `http://hermes-agent.dappnode:8080` to configure your AI provider and API key
3. Open the **Gateway Web UI** at `http://hermes-agent.dappnode:3000` to start chatting
1. Install the package from the DAppNode Package Store.
2. Open the **Setup Wizard** at `http://hermes-agent.dappnode:8080` to configure your AI provider and integrations.
3. Open the **Hermes Dashboard** at `http://hermes-agent.dappnode:8080/dashboard` or run `hermes chat` in the **Web Terminal**.

## Development & Testing

Run unit tests for both Node.js and Python hooks:

```bash
# Run all tests
npm test

# Run Node.js tests
npm run test:node

# Run Python bootstrapping & config tests
npm run test:python
```

## Building

Expand All @@ -32,6 +57,7 @@ npx @dappnode/dappnodesdk build
- [Nous Research](https://nousresearch.com/)
- [Upstream Repository](https://github.com/NousResearch/hermes-agent)
- [DAppNode SDK](https://docs.dappnode.io/docs/dev/sdk/overview)
- [DAppNode Nexus](https://nexus.dappnode.com/)

## License

Expand Down
19 changes: 13 additions & 6 deletions dappnode/bootstrap-env.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ def write_env(path: Path, lines: list[str], updates: dict[str, str]) -> None:
out.append(f"{key}={value}")

path.write_text("\n".join(out).rstrip() + "\n", encoding="utf-8")
path.chmod(0o600)
try:
path.chmod(0o600)
except OSError:
pass


def has_usable_secret(value: str, min_length: int = 16) -> bool:
Expand Down Expand Up @@ -123,18 +126,22 @@ def repair_profile_env(profile_home: Path, *, is_default: bool) -> dict[str, str
return env


def iter_profile_homes() -> list[Path]:
profiles_root = HERMES_HOME / "profiles"
def iter_profile_homes(hermes_home: Path = HERMES_HOME) -> list[Path]:
profiles_root = hermes_home / "profiles"
if not profiles_root.is_dir():
return []
return sorted(path for path in profiles_root.iterdir() if path.is_dir())


def main() -> None:
repair_profile_env(HERMES_HOME, is_default=True)
for profile_home in iter_profile_homes():
def bootstrap_all(hermes_home: Path = HERMES_HOME) -> None:
repair_profile_env(hermes_home, is_default=True)
for profile_home in iter_profile_homes(hermes_home):
repair_profile_env(profile_home, is_default=False)


def main() -> None:
bootstrap_all(HERMES_HOME)


if __name__ == "__main__":
main()
Loading