keithproject commited on
Commit
dfbc738
Β·
1 Parent(s): 040c488

Initial Commit

Browse files
Files changed (9) hide show
  1. .env.example +534 -0
  2. .gitattributes +8 -0
  3. .gitignore +15 -0
  4. Dockerfile +113 -0
  5. docker-compose.yml +16 -0
  6. entrypoint.sh +234 -0
  7. nginx-proxy.conf +10 -0
  8. nginx.conf +33 -0
  9. requirements.txt +1 -0
.env.example ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hermes Agent Environment Configuration
2
+ # Copy this file to .env and fill in your API keys
3
+
4
+ # HERMES_DASHBOARD_PASSWORD=
5
+ # HERMES_DASHBOARD_USERNAME=
6
+
7
+ # =============================================================================
8
+ # LLM PROVIDER (Fireworks AI)
9
+ # =============================================================================
10
+ # Get your key at: https://app.fireworks.ai/settings/users/api-keys
11
+ # Address models directly by catalog ID, e.g.
12
+ # accounts/fireworks/models/kimi-k2p6, accounts/fireworks/models/glm-5p2
13
+ # FIREWORKS_API_KEY=
14
+ # =============================================================================
15
+ # LLM PROVIDER (OpenRouter)
16
+ # =============================================================================
17
+ # OpenRouter provides access to many models through one API
18
+ # All LLM calls go through OpenRouter - no direct provider keys needed
19
+ # Get your key at: https://openrouter.ai/keys
20
+ # OPENROUTER_API_KEY=
21
+
22
+ # Default model is configured in ~/.hermes/config.yaml (model.default).
23
+ # Use 'hermes model' or 'hermes setup' to change it.
24
+ # LLM_MODEL is no longer read from .env β€” this line is kept for reference only.
25
+ # LLM_MODEL=anthropic/claude-opus-4.6
26
+
27
+ # =============================================================================
28
+ # LLM PROVIDER (NovitaAI)
29
+ # =============================================================================
30
+ # NovitaAI β€” 90+ models, pay-per-use
31
+ # Get your key at: https://novita.ai/settings/key-management
32
+ # NOVITA_API_KEY=
33
+ # NOVITA_BASE_URL=https://api.novita.ai/openai/v1 # Override default base URL
34
+
35
+ # =============================================================================
36
+ # LLM PROVIDER (Google AI Studio / Gemini)
37
+ # =============================================================================
38
+ # Native Gemini API via Google's OpenAI-compatible endpoint.
39
+ # Get your key at: https://aistudio.google.com/app/apikey
40
+ # GOOGLE_API_KEY=your_google_ai_studio_key_here
41
+ # GEMINI_API_KEY=your_gemini_key_here # alias for GOOGLE_API_KEY
42
+ # Optional base URL override (default: Google's OpenAI-compatible endpoint)
43
+ # GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
44
+
45
+ # =============================================================================
46
+ # LLM PROVIDER (Ollama Cloud)
47
+ # =============================================================================
48
+ # Cloud-hosted open models via Ollama's OpenAI-compatible endpoint.
49
+ # Get your key at: https://ollama.com/settings
50
+ # OLLAMA_API_KEY=your_ollama_key_here
51
+ # Optional base URL override (default: https://ollama.com/v1)
52
+ # OLLAMA_BASE_URL=https://ollama.com/v1
53
+
54
+ # =============================================================================
55
+ # LLM PROVIDER (z.ai / GLM)
56
+ # =============================================================================
57
+ # z.ai provides access to ZhipuAI GLM models (GLM-4-Plus, etc.)
58
+ # Get your key at: https://z.ai or https://open.bigmodel.cn
59
+ # GLM_API_KEY=
60
+ # GLM_BASE_URL=https://api.z.ai/api/paas/v4 # Override default base URL
61
+
62
+ # =============================================================================
63
+ # LLM PROVIDER (Kimi / Moonshot)
64
+ # =============================================================================
65
+ # Kimi Code provides access to Moonshot AI coding models (kimi-k2.5, etc.)
66
+ # Get your key at: https://platform.kimi.ai (Kimi Code console)
67
+ # Keys prefixed sk-kimi- use the Kimi Code API (api.kimi.com) by default.
68
+ # Legacy keys from platform.moonshot.ai need KIMI_BASE_URL override below.
69
+ # KIMI_API_KEY=
70
+ # KIMI_BASE_URL=https://api.kimi.com/coding/v1 # Default for sk-kimi- keys
71
+ # KIMI_BASE_URL=https://api.moonshot.ai/v1 # For legacy Moonshot keys
72
+ # KIMI_BASE_URL=https://api.moonshot.cn/v1 # For Moonshot China keys
73
+ # KIMI_CN_API_KEY= # Dedicated Moonshot China key
74
+
75
+ # =============================================================================
76
+ # LLM PROVIDER (Arcee AI)
77
+ # =============================================================================
78
+ # Arcee AI provides access to Trinity models (trinity-mini, trinity-large-*)
79
+ # Get an Arcee key at: https://chat.arcee.ai/
80
+ # ARCEEAI_API_KEY=
81
+ # ARCEE_BASE_URL= # Override default base URL
82
+
83
+ # =============================================================================
84
+ # LLM PROVIDER (MiniMax)
85
+ # =============================================================================
86
+ # MiniMax provides access to MiniMax models (global endpoint)
87
+ # Get your key at: https://www.minimax.io
88
+ # MINIMAX_API_KEY=
89
+ # MINIMAX_BASE_URL=https://api.minimax.io/v1 # Override default base URL
90
+
91
+ # MiniMax China endpoint (for users in mainland China)
92
+ # MINIMAX_CN_API_KEY=
93
+ # MINIMAX_CN_BASE_URL=https://api.minimaxi.com/v1 # Override default base URL
94
+
95
+ # =============================================================================
96
+ # LLM PROVIDER (OpenCode Zen)
97
+ # =============================================================================
98
+ # OpenCode Zen provides curated, tested models (GPT, Claude, Gemini, MiniMax, GLM, Kimi)
99
+ # Pay-as-you-go pricing. Get your key at: https://opencode.ai/auth
100
+ # OPENCODE_ZEN_API_KEY=
101
+ # OPENCODE_ZEN_BASE_URL=https://opencode.ai/zen/v1 # Override default base URL
102
+
103
+ # =============================================================================
104
+ # LLM PROVIDER (OpenCode Go)
105
+ # =============================================================================
106
+ # OpenCode Go provides access to open models (GLM-5, Kimi K2.5, MiniMax M2.5)
107
+ # $10/month subscription. Get your key at: https://opencode.ai/auth
108
+ # OPENCODE_GO_API_KEY=
109
+
110
+ # =============================================================================
111
+ # LLM PROVIDER (Hugging Face Inference Providers)
112
+ # =============================================================================
113
+ # Hugging Face routes to 20+ open models via unified OpenAI-compatible endpoint.
114
+ # Free tier included ($0.10/month), no markup on provider rates.
115
+ # Get your token at: https://huggingface.co/settings/tokens
116
+ # Required permission: "Make calls to Inference Providers"
117
+ # HF_TOKEN=
118
+ # HF_BASE_URL=https://router.huggingface.co/v1 # Override default base URL
119
+ # OPENCODE_GO_BASE_URL=https://opencode.ai/zen/go/v1 # Override default base URL
120
+
121
+ # DeepInfra β€” 100+ top open models, pay-per-use.
122
+ # Get your key at: https://deepinfra.com/dash/api_keys
123
+ # DEEPINFRA_API_KEY=
124
+
125
+ # =============================================================================
126
+ # LLM PROVIDER (Qwen OAuth)
127
+ # =============================================================================
128
+ # Qwen OAuth reuses your local Qwen CLI login (qwen auth qwen-oauth).
129
+ # No API key needed β€” credentials come from ~/.qwen/oauth_creds.json.
130
+ # Optional base URL override:
131
+ # HERMES_QWEN_BASE_URL=https://portal.qwen.ai/v1
132
+
133
+ # =============================================================================
134
+ # LLM PROVIDER (Xiaomi MiMo)
135
+ # =============================================================================
136
+ # Xiaomi MiMo models (mimo-v2-pro, mimo-v2-omni, mimo-v2-flash).
137
+ # Get your key at: https://platform.xiaomimimo.com
138
+ # XIAOMI_API_KEY=your_key_here
139
+ # Optional base URL override:
140
+ # XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
141
+
142
+ # =============================================================================
143
+ # LLM PROVIDER (Upstage Solar)
144
+ # =============================================================================
145
+ # Upstage provides access to Upstage Solar models.
146
+ # Get your key at: https://console.upstage.ai/api-keys
147
+ # UPSTAGE_API_KEY=your_key_here
148
+ # Optional base URL override:
149
+ # UPSTAGE_BASE_URL=https://api.upstage.ai/v1
150
+
151
+ # =============================================================================
152
+ # TOOL API KEYS
153
+ # =============================================================================
154
+
155
+ # Exa API Key - AI-native web search and contents
156
+ # Get at: https://exa.ai
157
+ # EXA_API_KEY=
158
+
159
+ # Parallel API Key - AI-native web search and extract
160
+ # Get at: https://parallel.ai
161
+ # PARALLEL_API_KEY=
162
+
163
+ # Firecrawl API Key - Web search, extract, and crawl
164
+ # Get at: https://firecrawl.dev/
165
+ # FIRECRAWL_API_KEY=
166
+
167
+
168
+ # FAL.ai API Key - Image generation
169
+ # Get at: https://fal.ai/
170
+ # FAL_KEY=
171
+
172
+ # Honcho - Cross-session AI-native user modeling (optional)
173
+ # Builds a persistent understanding of the user across sessions and tools.
174
+ # Get at: https://app.honcho.dev
175
+ # Also requires ~/.honcho/config.json with enabled=true (see README).
176
+ # HONCHO_API_KEY=
177
+
178
+ # =============================================================================
179
+ # HYPERLIQUID OPTIONAL SKILL
180
+ # =============================================================================
181
+ # Optional defaults for the Hyperliquid skill in optional-skills/blockchain/hyperliquid
182
+ #
183
+ # Hyperliquid API base URL override
184
+ # Default: https://api.hyperliquid.xyz
185
+ # HYPERLIQUID_API_URL=https://api.hyperliquid-testnet.xyz
186
+ #
187
+ # Default address for account-level commands like state, fills, orders, and review
188
+ # HYPERLIQUID_USER_ADDRESS=0x0000000000000000000000000000000000000000
189
+
190
+ # =============================================================================
191
+ # TERMINAL TOOL CONFIGURATION
192
+ # =============================================================================
193
+ # Backend type: "local", "singularity", "docker", "modal", or "ssh"
194
+ # Terminal backend is configured in ~/.hermes/config.yaml (terminal.backend).
195
+ # Use 'hermes setup' or 'hermes config set terminal.backend docker' to change.
196
+ # Supported: local, docker, singularity, modal, ssh
197
+ #
198
+ # Only override here if you need to force a backend without touching config.yaml:
199
+ # TERMINAL_ENV=local
200
+
201
+ # Override the container runtime binary (e.g. to use Podman instead of Docker).
202
+ # Useful on systems where Docker's storage driver is broken or unavailable.
203
+ # HERMES_DOCKER_BINARY=/usr/local/bin/podman
204
+
205
+ # Container images (for singularity/docker/modal backends)
206
+ # TERMINAL_DOCKER_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
207
+ # TERMINAL_SINGULARITY_IMAGE=docker://nikolaik/python-nodejs:python3.11-nodejs20
208
+ TERMINAL_MODAL_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
209
+
210
+
211
+ # Working directory for terminal commands
212
+ # For local backend: "." means current directory (resolved automatically)
213
+ # For remote backends (ssh/docker/modal/singularity): use an absolute path
214
+ # INSIDE the target environment, or leave unset for the backend's default
215
+ # (/root for modal, / for docker, ~ for ssh). Do NOT use a host-local path.
216
+ # Usually managed by config.yaml (terminal.cwd) β€” uncomment to override
217
+ # TERMINAL_CWD=.
218
+
219
+ # Default command timeout in seconds
220
+ TERMINAL_TIMEOUT=60
221
+
222
+ # Cleanup inactive environments after this many seconds
223
+ TERMINAL_LIFETIME_SECONDS=300
224
+
225
+ # =============================================================================
226
+ # SSH REMOTE EXECUTION (for TERMINAL_ENV=ssh)
227
+ # =============================================================================
228
+ # Run terminal commands on a remote server via SSH.
229
+ # Agent code stays on your machine, commands execute remotely.
230
+ #
231
+ # SECURITY BENEFITS:
232
+ # - Agent cannot read your .env file (API keys protected)
233
+ # - Agent cannot modify its own code
234
+ # - Remote server acts as isolated sandbox
235
+ # - Can safely configure passwordless sudo on remote
236
+ #
237
+ # TERMINAL_SSH_HOST=192.168.1.100
238
+ # TERMINAL_SSH_USER=agent
239
+ # TERMINAL_SSH_PORT=22
240
+ # TERMINAL_SSH_KEY=~/.ssh/id_rsa
241
+
242
+ # =============================================================================
243
+ # SUDO SUPPORT (works with ALL terminal backends)
244
+ # =============================================================================
245
+ # If set, enables sudo commands by piping password via `sudo -S`.
246
+ # Works with: local, docker, singularity, modal, and ssh backends.
247
+ #
248
+ # SECURITY WARNING: Password stored in plaintext. Only use on trusted machines.
249
+ #
250
+ # ALTERNATIVES:
251
+ # - For SSH backend: Configure passwordless sudo on the remote server
252
+ # - For containers: Run as root inside the container (no sudo needed)
253
+ # - For local: Configure /etc/sudoers for specific commands
254
+ # - For CLI: Leave unset - you'll be prompted interactively with 45s timeout
255
+ #
256
+ # SUDO_PASSWORD=your_password_here
257
+
258
+ # =============================================================================
259
+ # MODAL CLOUD BACKEND (Optional - for TERMINAL_ENV=modal)
260
+ # =============================================================================
261
+ # Modal uses CLI authentication, not environment variables.
262
+ # Run: pip install modal && modal setup
263
+ # This will authenticate via browser and store credentials locally.
264
+ # No API key needed in .env - Modal handles auth automatically.
265
+
266
+ # =============================================================================
267
+ # BROWSER TOOL CONFIGURATION (agent-browser + Browserbase)
268
+ # =============================================================================
269
+ # Browser automation requires Browserbase cloud service for remote browser execution.
270
+ # This allows the agent to navigate websites, fill forms, and extract information.
271
+ #
272
+ # STEALTH MODES:
273
+ # - Basic Stealth: ALWAYS active (random fingerprints, auto CAPTCHA solving)
274
+ # - Advanced Stealth: Requires BROWSERBASE_ADVANCED_STEALTH=true (Scale Plan only)
275
+
276
+ # Browserbase API Key - Cloud browser execution
277
+ # Get at: https://browserbase.com/
278
+ # BROWSERBASE_API_KEY=
279
+
280
+ # Browserbase Project ID - From your Browserbase dashboard
281
+ # BROWSERBASE_PROJECT_ID=
282
+
283
+ # Enable residential proxies for better CAPTCHA solving (default: true)
284
+ # Routes traffic through residential IPs, significantly improves success rate
285
+ BROWSERBASE_PROXIES=true
286
+
287
+ # Enable advanced stealth mode (default: false, requires Scale Plan)
288
+ # Uses custom Chromium build to avoid bot detection altogether
289
+ BROWSERBASE_ADVANCED_STEALTH=false
290
+
291
+ # Browser engine for local mode (default: auto = Chrome)
292
+ # "auto" β€” use Chrome (don't pass --engine flag)
293
+ # "lightpanda" β€” use Lightpanda (1.3-5.8x faster navigation, no screenshots)
294
+ # "chrome" β€” explicitly request Chrome
295
+ # Requires agent-browser v0.25.3+. Lightpanda commands that fail or return
296
+ # empty results are automatically retried with Chrome.
297
+ # Also configurable via browser.engine in config.yaml.
298
+ # AGENT_BROWSER_ENGINE=auto
299
+
300
+ # Browser session timeout in seconds (default: 300)
301
+ # Sessions are cleaned up after this duration of inactivity
302
+ BROWSER_SESSION_TIMEOUT=300
303
+
304
+ # Browser inactivity timeout - auto-cleanup inactive sessions (default: 120 = 2 min)
305
+ # Browser sessions are automatically closed after this period of no activity
306
+ BROWSER_INACTIVITY_TIMEOUT=120
307
+
308
+ # Extra Chromium launch flags passed to agent-browser, comma- or newline-separated.
309
+ # Hermes auto-injects "--no-sandbox,--disable-dev-shm-usage" when it detects root
310
+ # or AppArmor-restricted unprivileged user namespaces (Ubuntu 23.10+, DGX Spark,
311
+ # many container images), so leave this unset unless you need extra flags.
312
+ # Setting this disables the auto-injection.
313
+ # AGENT_BROWSER_ARGS=--no-sandbox
314
+
315
+ # Camofox local anti-detection browser (Camoufox-based Firefox).
316
+ # Set CAMOFOX_URL to route the browser tools through a local Camofox server
317
+ # instead of agent-browser/Browserbase. See docs/user-guide/features/browser.md.
318
+ # CAMOFOX_URL=http://localhost:9377
319
+
320
+ # Externally managed Camofox sessions β€” when another app owns the visible
321
+ # Camofox browser, set these so Hermes shares the same userId/profile instead
322
+ # of creating its own isolated session.
323
+ # CAMOFOX_USER_ID=
324
+ # CAMOFOX_SESSION_KEY=
325
+ # Set to true to reuse an already-open Camofox tab for this identity before
326
+ # creating a new one (useful for gateway restarts).
327
+ # CAMOFOX_ADOPT_EXISTING_TAB=false
328
+
329
+ # =============================================================================
330
+ # SESSION LOGGING
331
+ # =============================================================================
332
+ # Session trajectories are automatically saved to logs/ directory
333
+ # Format: logs/session_YYYYMMDD_HHMMSS_UUID.json
334
+ # Contains full conversation history in trajectory format for debugging/replay
335
+
336
+ # =============================================================================
337
+ # VOICE TRANSCRIPTION & OPENAI TTS
338
+ # =============================================================================
339
+ # Required for voice message transcription (Whisper) and OpenAI TTS voices.
340
+ # Uses OpenAI's API directly (not via OpenRouter).
341
+ # Named VOICE_TOOLS_OPENAI_KEY to avoid interference with OpenRouter.
342
+ # Get at: https://platform.openai.com/api-keys
343
+ # VOICE_TOOLS_OPENAI_KEY=
344
+
345
+ # =============================================================================
346
+ # SLACK INTEGRATION
347
+ # =============================================================================
348
+ # Slack Bot Token - From Slack App settings (OAuth & Permissions)
349
+ # Get at: https://api.slack.com/apps
350
+ # SLACK_BOT_TOKEN=xoxb-...
351
+
352
+ # Slack App Token - For Socket Mode (App-Level Tokens in Slack App settings)
353
+ # SLACK_APP_TOKEN=xapp-...
354
+
355
+ # Slack allowed users (comma-separated Slack user IDs)
356
+ # SLACK_ALLOWED_USERS=
357
+
358
+ # =============================================================================
359
+ # TELEGRAM INTEGRATION
360
+ # =============================================================================
361
+ # Telegram Bot Token - From @BotFather (https://t.me/BotFather)
362
+ # TELEGRAM_BOT_TOKEN=
363
+ # TELEGRAM_ALLOWED_USERS= # Comma-separated user IDs
364
+ # TELEGRAM_HOME_CHANNEL= # Default chat for cron delivery
365
+ # TELEGRAM_HOME_CHANNEL_NAME= # Display name for home channel
366
+ # TELEGRAM_CRON_THREAD_ID= # Forum topic ID for cron deliveries; overrides TELEGRAM_HOME_CHANNEL_THREAD_ID for cron so replies work in topic mode
367
+
368
+ # Webhook mode (optional β€” for cloud deployments like Fly.io/Railway)
369
+ # Default is long polling. Setting TELEGRAM_WEBHOOK_URL switches to webhook mode.
370
+ # TELEGRAM_WEBHOOK_URL=https://my-app.fly.dev/telegram
371
+ # TELEGRAM_WEBHOOK_PORT=8443
372
+ # TELEGRAM_WEBHOOK_SECRET= # Recommended for production
373
+
374
+ # WhatsApp (built-in Baileys bridge β€” run `hermes whatsapp` to pair)
375
+ # WHATSAPP_ENABLED=false
376
+ # WHATSAPP_ALLOWED_USERS=15551234567
377
+
378
+ # Email (IMAP/SMTP β€” send and receive emails as Hermes)
379
+ # For Gmail: enable 2FA β†’ create App Password at https://myaccount.google.com/apppasswords
380
+ # EMAIL_ADDRESS=hermes@gmail.com
381
+ # EMAIL_PASSWORD=xxxx xxxx xxxx xxxx
382
+ # EMAIL_IMAP_HOST=imap.gmail.com
383
+ # EMAIL_IMAP_PORT=993
384
+ # EMAIL_SMTP_HOST=smtp.gmail.com
385
+ # EMAIL_SMTP_PORT=587
386
+ # EMAIL_POLL_INTERVAL=15
387
+ # EMAIL_ALLOWED_USERS=your@email.com
388
+ # EMAIL_HOME_ADDRESS=your@email.com
389
+
390
+ # Gateway-wide: allow ALL users without an allowlist (default: false = deny)
391
+ # Only set to true if you intentionally want open access.
392
+ # GATEWAY_ALLOW_ALL_USERS=false
393
+
394
+ # =============================================================================
395
+ # RESPONSE PACING
396
+ # =============================================================================
397
+ # Human-like delays between message chunks on messaging platforms.
398
+ # Makes the bot feel less robotic.
399
+ # HERMES_HUMAN_DELAY_MODE=off # off | natural | custom
400
+ # HERMES_HUMAN_DELAY_MIN_MS=800 # Min delay in ms (custom mode)
401
+ # HERMES_HUMAN_DELAY_MAX_MS=2500 # Max delay in ms (custom mode)
402
+
403
+ # =============================================================================
404
+ # DEBUG OPTIONS
405
+ # =============================================================================
406
+ WEB_TOOLS_DEBUG=false
407
+ VISION_TOOLS_DEBUG=false
408
+ MOA_TOOLS_DEBUG=false
409
+ IMAGE_TOOLS_DEBUG=false
410
+
411
+ # =============================================================================
412
+ # CONTEXT COMPRESSION (Auto-shrinks long conversations)
413
+ # =============================================================================
414
+ # When conversation approaches model's context limit, middle turns are
415
+ # automatically summarized to free up space.
416
+ #
417
+ # Context compression is configured in ~/.hermes/config.yaml under compression:
418
+ # CONTEXT_COMPRESSION_ENABLED=true # Enable auto-compression (default: true)
419
+ # CONTEXT_COMPRESSION_THRESHOLD=0.85 # Compress at 85% of context limit
420
+ # Model is set via compression.summary_model in config.yaml (default: google/gemini-3-flash-preview)
421
+
422
+ # =============================================================================
423
+ # SKILLS HUB (GitHub integration for skill search/install/publish)
424
+ # =============================================================================
425
+
426
+ # GitHub Personal Access Token β€” for higher API rate limits on skill search/install
427
+ # Get at: https://github.com/settings/tokens (Fine-grained recommended)
428
+ # GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
429
+
430
+ # GitHub App credentials (optional β€” for bot identity on PRs)
431
+ # GITHUB_APP_ID=
432
+ # GITHUB_APP_PRIVATE_KEY_PATH=
433
+ # GITHUB_APP_INSTALLATION_ID=
434
+
435
+ # Groq API key (free tier β€” used for Whisper STT in voice mode)
436
+ # GROQ_API_KEY=
437
+
438
+ # ElevenLabs API key (cloud STT/TTS β€” Scribe transcription)
439
+ # ELEVENLABS_API_KEY=
440
+
441
+ # =============================================================================
442
+ # STT PROVIDER SELECTION
443
+ # =============================================================================
444
+ # Default STT provider is "local" (faster-whisper) β€” runs on your machine, no API key needed.
445
+ # Install with: pip install faster-whisper
446
+ # Model downloads automatically on first use (~150 MB for "base").
447
+ # To use cloud providers instead, set GROQ_API_KEY, VOICE_TOOLS_OPENAI_KEY, or ELEVENLABS_API_KEY above.
448
+ # Provider priority: local > groq > openai > mistral > xai > elevenlabs
449
+ # Configure in config.yaml: stt.provider: local | groq | openai | mistral | xai | elevenlabs
450
+
451
+ # =============================================================================
452
+ # STT ADVANCED OVERRIDES (optional)
453
+ # =============================================================================
454
+ # Override default STT models per provider (normally set via stt.model in config.yaml)
455
+ # STT_GROQ_MODEL=whisper-large-v3-turbo
456
+ # STT_OPENAI_MODEL=whisper-1
457
+ # STT_ELEVENLABS_MODEL=scribe_v2
458
+
459
+ # Override STT provider endpoints (for proxies or self-hosted instances)
460
+ # GROQ_BASE_URL=https://api.groq.com/openai/v1
461
+ # STT_OPENAI_BASE_URL=https://api.openai.com/v1
462
+ # ELEVENLABS_STT_BASE_URL=https://api.elevenlabs.io/v1
463
+
464
+ # =============================================================================
465
+ # MICROSOFT TEAMS INTEGRATION
466
+ # =============================================================================
467
+ # Register a Bot in Azure: https://dev.botframework.com/ β†’ "Register a bot"
468
+ # Or use Azure Portal: Azure Active Directory β†’ App registrations β†’ New registration
469
+ # Then add the bot to Teams via the Bot Framework or App Studio.
470
+ #
471
+ # TEAMS_CLIENT_ID= # Azure AD App (client) ID
472
+ # TEAMS_CLIENT_SECRET= # Azure AD client secret value
473
+ # TEAMS_TENANT_ID= # Azure AD tenant ID (or "common" for multi-tenant)
474
+ # TEAMS_ALLOWED_USERS= # Comma-separated AAD object IDs or UPNs
475
+ # TEAMS_ALLOW_ALL_USERS=false # Set true to skip the allowlist
476
+ # TEAMS_HOME_CHANNEL= # Default channel/chat ID for cron delivery
477
+ # TEAMS_HOME_CHANNEL_NAME= # Display name for the home channel
478
+ # TEAMS_PORT=3978 # Webhook listen port (Bot Framework default)
479
+
480
+ # =============================================================================
481
+ # GOOGLE CHAT INTEGRATION
482
+ # =============================================================================
483
+ # Connects via Cloud Pub/Sub pull subscription (no public URL required).
484
+ # Setup walkthrough: website/docs/user-guide/messaging/google_chat.md.
485
+ # 1. Create a GCP project, enable the Google Chat API and Cloud Pub/Sub.
486
+ # 2. Create a Service Account with roles/pubsub.subscriber on the
487
+ # subscription (NOT project-wide); download the JSON key.
488
+ # 3. Configure your Chat app at console.cloud.google.com/apis/credentials
489
+ # β†’ Google Chat API β†’ Configuration β†’ Cloud Pub/Sub topic.
490
+ # 4. (Optional, for native attachment delivery) Each user runs
491
+ # `/setup-files` once in their own DM after Pub/Sub is wired up.
492
+ #
493
+ # GOOGLE_CHAT_PROJECT_ID= # GCP project hosting the topic (or set GOOGLE_CLOUD_PROJECT)
494
+ # GOOGLE_CHAT_SUBSCRIPTION_NAME= # Full path: projects/<id>/subscriptions/<name>
495
+ # GOOGLE_CHAT_SERVICE_ACCOUNT_JSON= # Path to SA JSON (or set GOOGLE_APPLICATION_CREDENTIALS)
496
+ # GOOGLE_CHAT_ALLOWED_USERS= # Comma-separated emails allowed to talk to the bot
497
+ # GOOGLE_CHAT_ALLOW_ALL_USERS=false # Set true to skip the allowlist
498
+ # GOOGLE_CHAT_HOME_CHANNEL= # Default space (spaces/XXXX) for cron delivery
499
+ # GOOGLE_CHAT_HOME_CHANNEL_NAME= # Display name for the home channel
500
+
501
+ # =============================================================================
502
+ # A2A INTEGRATION
503
+ # =============================================================================
504
+
505
+ # A2A_AGENT_NAME=
506
+ # A2A_HOST=
507
+ # A2A_BEARER_TOKEN=
508
+
509
+ # =============================================================================
510
+ # A2A INTEGRATION
511
+ # =============================================================================
512
+
513
+ # API_SERVER_HOST=
514
+ # API_SERVER_ENABLED=
515
+ # API_SERVER_KEY=
516
+
517
+ # =============================================================================
518
+ # BUZZ INTEGRATION
519
+ # =============================================================================
520
+
521
+ # BUZZ_RELAY_URL=
522
+ # BUZZ_PRIVATE_KEY=
523
+ # BUZZ_HOME_CHANNEL_THREAD_ID=
524
+ # BUZZ_ALLOWED_USERS=123123123,1231231234
525
+ # BUZZ_DISPLAY_NAME=
526
+ # BUZZ_TRANSPORT=
527
+
528
+ # =============================================================================
529
+ # DISCORD INTEGRATION
530
+ # =============================================================================
531
+
532
+ # DISCORD_BOT_TOKEN=
533
+ # DISCORD_GUILD_ID=
534
+ # DISCORD_CHANNEL_ID=
.gitattributes CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
@@ -33,3 +37,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
1
+ # Force LF line endings for shell/Python scripts
2
+ *.sh text eol=lf
3
+ *.py text eol=lf
4
+
5
  *.7z filter=lfs diff=lfs merge=lfs -text
6
  *.arrow filter=lfs diff=lfs merge=lfs -text
7
  *.bin filter=lfs diff=lfs merge=lfs -text
 
37
  *.zip filter=lfs diff=lfs merge=lfs -text
38
  *.zst filter=lfs diff=lfs merge=lfs -text
39
  *tfevents* filter=lfs diff=lfs merge=lfs -text
40
+ *.png filter=lfs diff=lfs merge=lfs -text
41
+ *.jpg filter=lfs diff=lfs merge=lfs -text
42
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
43
+ *.webp filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment and secrets
2
+ .env
3
+ .env.local
4
+ *.pem
5
+
6
+ # Dependencies and build
7
+ node_modules/
8
+ __pycache__/
9
+ *.pyc
10
+
11
+ # Logs and temp
12
+ *.log
13
+ .DS_Store
14
+
15
+ graphify-out/
Dockerfile ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ── Stage 1: Build Hermes Agent from source ──────────────────────────────
2
+ FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie AS uv_source
3
+ FROM tianon/gosu:1.19-trixie AS gosu_source
4
+
5
+ FROM debian:13.4
6
+ SHELL ["/bin/bash", "-c"]
7
+
8
+ ENV PYTHONUNBUFFERED=1
9
+ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
10
+
11
+ # ── System dependencies ──────────────────────────────────────────────────
12
+ RUN echo "[build] Installing system deps..." && START=$(date +%s) \
13
+ && apt-get update && apt-get install -y --no-install-recommends \
14
+ build-essential python3 python3-pip python3-venv \
15
+ git curl wget ca-certificates \
16
+ ripgrep ffmpeg gcc python3-dev libffi-dev procps gnupg \
17
+ git git-lfs \
18
+ vim nano htop bash \
19
+ tar gzip xz-utils bzip2 rsync \
20
+ gettext-base openssh-client nginx \
21
+ && rm -rf /var/lib/apt/lists/* \
22
+ && pip3 install --no-cache-dir --break-system-packages huggingface_hub requests pyyaml \
23
+ && echo "[build] System deps: $(($(date +%s) - START))s"
24
+
25
+ # Install Node.js 22 LTS (official binary). Debian 13's apt `nodejs` is v20.x with
26
+ # npm 9.2.0, which fails Hermes' `npm install` (EBADENGINE): @electron/rebuild@4.2.0
27
+ # requires node >=22.12.0 and react-router@8.3.0 requires node >=22.22.0 β€” the repo
28
+ # sets engine-strict=true, so we pin the latest 22.x (22.23.2) that satisfies both.
29
+ # The binary tarball is distro-agnostic and ships its own npm, avoiding apt lag.
30
+ RUN echo "[build] Installing Node.js 22 LTS..." && START=$(date +%s) \
31
+ && curl -fsSL https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz -o /tmp/node.tar.xz \
32
+ && tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \
33
+ && rm -f /tmp/node.tar.xz \
34
+ && node --version && npm --version \
35
+ && echo "[build] Node.js install: $(($(date +%s) - START))s"
36
+
37
+ # ── Install buzz CLI (Rust, build only the buzz-cli crate) ────────────────
38
+ # Provides the `buzz` binary needed by BUZZ_PRIVATE_KEY agent config.
39
+ RUN echo "[build] Installing Rust + building buzz-cli..." && START=$(date +%s) \
40
+ && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal \
41
+ && . "$HOME/.cargo/env" \
42
+ && git clone --depth 1 https://github.com/block/buzz.git /tmp/buzz-src \
43
+ && cd /tmp/buzz-src \
44
+ && cargo build --release -p buzz-cli \
45
+ && cp target/release/buzz /usr/local/bin/buzz \
46
+ && chmod +x /usr/local/bin/buzz \
47
+ && cd / && rm -rf /tmp/buzz-src \
48
+ && echo "[build] buzz-cli: $(buzz --version 2>/dev/null || echo installed)" \
49
+ && echo "[build] Rust+buzz build: $(($(date +%s) - START))s"
50
+
51
+ # ── Non-root user (uid 1000 β€” required for HF Spaces Dev Mode) ───────────
52
+ RUN useradd -u 1000 -m -d /opt/data hermes
53
+
54
+ COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/
55
+ COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/
56
+
57
+ # ── Clone and build Hermes Agent ─────────────────────────────────────────
58
+ RUN echo "[build] Cloning Hermes Agent..." && START=$(date +%s) \
59
+ && git clone --depth 1 https://github.com/NousResearch/hermes-agent.git /opt/hermes \
60
+ && echo "[build] Clone: $(($(date +%s) - START))s"
61
+
62
+ WORKDIR /opt/hermes
63
+
64
+ # ── Node dependencies + Playwright + Web Dashboard build ─────────────────
65
+ RUN echo "[build] Installing Node deps + Playwright..." && START=$(date +%s) \
66
+ && npm install --prefer-offline --no-audit \
67
+ && npx playwright install --with-deps chromium --only-shell \
68
+ && if [ -d /opt/hermes/scripts/whatsapp-bridge ]; then \
69
+ cd /opt/hermes/scripts/whatsapp-bridge && npm install --prefer-offline --no-audit && npm install https-proxy-agent; \
70
+ fi \
71
+ && echo "[build] Building web dashboard..." \
72
+ && cd /opt/hermes/web && npm install --prefer-offline --no-audit && npm run build \
73
+ && cd /opt/hermes && npm cache clean --force \
74
+ && echo "[build] Node deps + web dashboard: $(($(date +%s) - START))s"
75
+
76
+ # ── Python dependencies ──────────────────────────────────────────────────
77
+ RUN chown -R hermes:hermes /opt/hermes
78
+ USER hermes
79
+
80
+ RUN echo "[build] Installing Python deps..." && START=$(date +%s) \
81
+ && cd /opt/hermes \
82
+ && UV_CACHE_DIR=/tmp/uv-cache uv venv \
83
+ && UV_CACHE_DIR=/tmp/uv-cache uv pip install --no-cache-dir -e ".[all]" \
84
+ && UV_CACHE_DIR=/tmp/uv-cache uv pip install --no-cache-dir huggingface_hub requests pyyaml \
85
+ && rm -rf /tmp/uv-cache \
86
+ && echo "[build] Python deps: $(($(date +%s) - START))s"
87
+
88
+ USER root
89
+ RUN chmod +x /opt/hermes/docker/entrypoint.sh
90
+
91
+ # ── Prepare runtime dirs ─────────────────────────────────���──────────────
92
+ RUN mkdir -p /opt/data/cron /opt/data/sessions /opt/data/logs /opt/data/hooks \
93
+ /opt/data/memories /opt/data/skills /opt/data/skins /opt/data/plans \
94
+ /opt/data/workspace /opt/data/home \
95
+ && chown -R 1000:1000 /opt/data
96
+
97
+ USER root
98
+
99
+ ARG CACHE_BUST=2026-07-18-wa-proxy-patch-v3
100
+ RUN echo "Build: ${CACHE_BUST}"
101
+ COPY --chown=root:root entrypoint.sh /opt/scripts/entrypoint.sh
102
+ RUN chmod +x /opt/scripts/entrypoint.sh
103
+
104
+ ENV HERMES_HOME=/opt/data
105
+ ENV PATH="/opt/hermes/.venv/bin:$PATH"
106
+
107
+ WORKDIR /app
108
+ COPY --link ./ /app
109
+
110
+ COPY nginx-proxy.conf /etc/nginx/proxy.conf
111
+ COPY nginx.conf /etc/nginx/nginx.conf
112
+
113
+ CMD ["/opt/scripts/entrypoint.sh"]
docker-compose.yml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ hermes:
3
+ build: .
4
+ container_name: hermes
5
+ ports:
6
+ - "7860:7860"
7
+ env_file:
8
+ - .env
9
+ environment:
10
+ - HERMES_DASHBOARD_PASSWORD=${HERMES_DASHBOARD_PASSWORD:-}
11
+ - HERMES_DASHBOARD_USERNAME=${HERMES_DASHBOARD_USERNAME:-admin}
12
+ volumes:
13
+ - hermes_data:/data/hermes
14
+
15
+ volumes:
16
+ hermes_data:
entrypoint.sh ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ BOOT_START=$(date +%s)
5
+
6
+ echo "[entrypoint] Hermes Agent on HuggingFace Spaces (Safe Sync Mode)"
7
+ echo "==============================================================================="
8
+
9
+ # ── Configuration ───────────────────────────────────────────────────────────
10
+ HERMES_WORK="/opt/data" # Fast local filesystem (working dir)
11
+ HERMES_BACKUP="/data/hermes" # HF mounted bucket (backup only)
12
+ INSTALL_DIR="/opt/hermes"
13
+ HERMES_UID=1000
14
+ HERMES_GID=1000
15
+ SYNC_INTERVAL="${SYNC_INTERVAL:-60}" # Default: sync every 1 minute
16
+
17
+ # ── Singleton guard ──────────────────────────────────────────────────────────
18
+ # Hanya satu instance yang menjalankan steps. Instance duplikat (dari HF rapid-
19
+ # restart) cukup sleep infinity β€” tidak exit (exit = HF restart lagi).
20
+ # HERMES_ENTRYPOINT_OWNER di-export sebelum exec gosu sehingga instance setelah
21
+ # privilege-drop tahu dia adalah kelanjutan sah, bukan duplikat.
22
+ ENTRYPOINT_PID_FILE="/tmp/hermes-entrypoint.pid"
23
+ ENTRYPOINT_LOG="/tmp/hermes-entrypoint.log"
24
+
25
+ if [ -z "${HERMES_ENTRYPOINT_OWNER:-}" ]; then
26
+ # Instance baru tanpa token β†’ cek apakah ada instance lain yang sudah running
27
+ if [ -f "$ENTRYPOINT_PID_FILE" ]; then
28
+ OLD_PID=$(cat "$ENTRYPOINT_PID_FILE" 2>/dev/null || echo "")
29
+ if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then
30
+ echo "[entrypoint] Instance $OLD_PID already running β€” standing by silently."
31
+ sleep infinity
32
+ exit 0
33
+ else
34
+ echo "[entrypoint] Stale PID $OLD_PID (dead) β€” taking over..."
35
+ rm -f "$ENTRYPOINT_PID_FILE" "$ENTRYPOINT_LOG" 2>/dev/null || true
36
+ fi
37
+ fi
38
+ echo $$ > "$ENTRYPOINT_PID_FILE"
39
+ export HERMES_ENTRYPOINT_OWNER=$$
40
+ # Buat log world-writable sekarang (sebagai root) agar hermes bisa write setelah gosu
41
+ touch "$ENTRYPOINT_LOG" && chmod 666 "$ENTRYPOINT_LOG"
42
+ fi
43
+
44
+ exec > >(tee -a "$ENTRYPOINT_LOG") 2>&1
45
+
46
+ # ── Helper: Safe Sync (EXCLUDE large/temp files) ───────────────────────────
47
+ safe_sync() {
48
+ local SRC="$1"
49
+ local DST="$2"
50
+
51
+ if [ ! -d "$SRC" ]; then
52
+ echo " Source $SRC does not exist, skipping sync"
53
+ return 0
54
+ fi
55
+
56
+ mkdir -p "$DST"
57
+
58
+ echo " Syncing: $SRC -> $DST"
59
+ echo " Excluding: node_modules, .cache, logs, *.tmp, *.lock, *.pid, .local/share/uv"
60
+
61
+ rsync -a --checksum --delete \
62
+ --exclude='.local/share/uv' \
63
+ --exclude='node_modules' \
64
+ --exclude='.cache' \
65
+ --exclude='.playwright' \
66
+ --exclude='logs' \
67
+ --exclude='*.log' \
68
+ --exclude='*.log.*' \
69
+ --exclude='*.tmp' \
70
+ --exclude='*.lock' \
71
+ --exclude='*.pid' \
72
+ --exclude='__pycache__' \
73
+ --exclude='state.db-wal' \
74
+ --exclude='state.db-shm' \
75
+ "$SRC/" "$DST/"
76
+
77
+ echo " βœ“ Sync completed"
78
+ }
79
+
80
+ # ── Stage 1: Restore from backup (as root, before privilege drop) ──────────
81
+ if [ "$(id -u)" = "0" ]; then
82
+ echo ""
83
+ echo "=== 1. Restoring Hermes data from persistent storage ==="
84
+
85
+ # Create working directory on fast local filesystem
86
+ mkdir -p "$HERMES_WORK"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
87
+ mkdir -p "$HERMES_BACKUP"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
88
+
89
+ # Restore from backup if exists
90
+ if [ -d "$HERMES_BACKUP" ] && [ "$(ls -A $HERMES_BACKUP 2>/dev/null | wc -l)" -gt 0 ]; then
91
+ echo "Found existing data in $HERMES_BACKUP"
92
+ RESTORE_START=$(date +%s)
93
+ safe_sync "$HERMES_BACKUP" "$HERMES_WORK"
94
+ RESTORE_END=$(date +%s)
95
+ echo "[TIMER] Restore from backup: $((RESTORE_END - RESTORE_START))s"
96
+ echo "Restore completed (sessions, memories, workspace restored)"
97
+ else
98
+ echo "No existing backup found, starting fresh"
99
+ fi
100
+
101
+ # Fix ownership on working directory (fast - only local files)
102
+ chown -R ${HERMES_UID}:${HERMES_GID} "$HERMES_WORK"
103
+ echo "βœ“ Working directory ready: $HERMES_WORK (owned by hermes)"
104
+
105
+ # Ensure /tmp is world-writable
106
+ chmod 1777 /tmp 2>/dev/null || true
107
+
108
+ echo ""
109
+ echo "=== 2. Starting Scheduled Backup (Every ${SYNC_INTERVAL}s) ==="
110
+ # Background backup loop β€” uses safe_sync which already excludes .local/share/uv
111
+ (while true; do
112
+ sleep ${SYNC_INTERVAL}
113
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up $HERMES_WORK -> $HERMES_BACKUP"
114
+ safe_sync "$HERMES_WORK" "$HERMES_BACKUP"
115
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed"
116
+ done) &
117
+ BACKUP_PID=$!
118
+ echo "Backup loop started with PID $BACKUP_PID"
119
+
120
+ echo ""
121
+ echo "=== 3. Starting nginx reverse proxy (port 7860 β†’ 7861) ==="
122
+ nginx
123
+ echo " βœ“ nginx started (PID $(cat /run/nginx.pid 2>/dev/null || echo unknown))"
124
+
125
+ echo ""
126
+ echo "=== 4. Dropping privileges to hermes (uid ${HERMES_UID}) ==="
127
+ exec gosu ${HERMES_UID} "$0" "$@"
128
+ fi
129
+
130
+ # ── Stage 2: Run as hermes user ─────────────────────────────────────────────
131
+ echo ""
132
+ echo "=== 4. Activating Python virtual environment ==="
133
+ if [ -f "${INSTALL_DIR}/.venv/bin/activate" ]; then
134
+ source "${INSTALL_DIR}/.venv/bin/activate"
135
+ echo "βœ“ Activated venv: $(which python3)"
136
+ fi
137
+
138
+ # ── Bootstrap config files (in working dir, not mounted bucket) ─────────────
139
+ echo ""
140
+ echo "=== 5. Bootstrapping configuration files ==="
141
+
142
+ if [ ! -f "$HERMES_WORK/.env" ] && [ -f "$INSTALL_DIR/.env.example" ]; then
143
+ cp "$INSTALL_DIR/.env.example" "$HERMES_WORK/.env"
144
+ echo "βœ“ Created .env from example"
145
+ fi
146
+
147
+ if [ ! -f "$HERMES_WORK/config.yaml" ] && [ -f "$INSTALL_DIR/cli-config.yaml.example" ]; then
148
+ cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_WORK/config.yaml"
149
+ echo "βœ“ Created config.yaml from example"
150
+ fi
151
+
152
+ if [ ! -f "$HERMES_WORK/SOUL.md" ] && [ -f "$INSTALL_DIR/docker/SOUL.md" ]; then
153
+ cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_WORK/SOUL.md"
154
+ echo "βœ“ Created SOUL.md from template"
155
+ fi
156
+
157
+ # ── Dashboard basic auth ────────────────────────────────────────────────────
158
+ DASH_USER="${HERMES_DASHBOARD_USERNAME:-admin}"
159
+ DASH_PASS="${HERMES_DASHBOARD_PASSWORD:-}"
160
+ if [ -n "$DASH_PASS" ] && [ -f "$HERMES_WORK/config.yaml" ]; then
161
+ if ! grep -q 'basic_auth' "$HERMES_WORK/config.yaml"; then
162
+ cat >> "$HERMES_WORK/config.yaml" <<EOF
163
+
164
+ dashboard:
165
+ basic_auth:
166
+ username: ${DASH_USER}
167
+ password: ${DASH_PASS}
168
+ EOF
169
+ echo "βœ“ Injected dashboard.basic_auth into config.yaml"
170
+ fi
171
+ fi
172
+
173
+ if [ -n "$DASH_PASS" ]; then
174
+ export HERMES_DASHBOARD_BASIC_AUTH_USERNAME="$DASH_USER"
175
+ export HERMES_DASHBOARD_BASIC_AUTH_PASSWORD="$DASH_PASS"
176
+ fi
177
+
178
+ # ── Sync bundled skills ─────────────────────────────────────────────────────
179
+ if [ -d "$INSTALL_DIR/skills" ] && [ -f "$INSTALL_DIR/tools/skills_sync.py" ]; then
180
+ python3 "$INSTALL_DIR/tools/skills_sync.py" 2>&1 || echo "[entrypoint] Skills sync skipped"
181
+ fi
182
+
183
+ # ── Build artifacts check ───────────────────────────────────────────────────
184
+ echo ""
185
+ echo "=== 6. Build artifacts check ==="
186
+ test -f "$INSTALL_DIR/run_agent.py" && echo " βœ“ run_agent.py" || echo " ⚠ run_agent.py not found"
187
+ test -f "$INSTALL_DIR/gateway/run.py" && echo " βœ“ gateway/run.py" || echo " ⚠ gateway/run.py not found"
188
+ test -d "$INSTALL_DIR/web" && echo " βœ“ web/ dashboard" || echo " ⚠ web/ not found"
189
+ command -v hermes >/dev/null 2>&1 && echo " βœ“ hermes CLI: $(which hermes)" || echo " ⚠ hermes CLI not in PATH"
190
+
191
+ # ── Register Buzz profile ───────────────────────────────────────────────────
192
+ if [ -n "${BUZZ_RELAY_URL:-}" ] && [ -n "${BUZZ_PRIVATE_KEY:-}" ]; then
193
+ _BUZZ_NAME="${BUZZ_DISPLAY_NAME:-${AGENT_NAME:-$(hostname)}}"
194
+ echo ""
195
+ echo "=== 7. Setting Buzz profile name: ${_BUZZ_NAME} ==="
196
+ buzz users set-profile --name "${_BUZZ_NAME}" 2>&1 || \
197
+ echo "⚠ buzz users set-profile failed (non-fatal)"
198
+ fi
199
+
200
+ # ── Export working directory as HERMES_HOME ─────────────────────────────────
201
+ export HERMES_HOME="$HERMES_WORK"
202
+ echo ""
203
+ echo "=== 8. Environment Setup ==="
204
+ echo " Working directory (FAST): $HERMES_WORK"
205
+ echo " Backup directory (HF Bucket): $HERMES_BACKUP"
206
+ echo " Sync interval: ${SYNC_INTERVAL}s"
207
+ echo " HERMES_HOME: $HERMES_HOME"
208
+
209
+ ENTRYPOINT_END=$(date +%s)
210
+ echo ""
211
+ echo "[TIMER] Entrypoint setup: $((ENTRYPOINT_END - BOOT_START))s"
212
+
213
+ # ── Start Hermes Gateway ────────────────────────────────────────────────────
214
+ echo ""
215
+ echo "=== 9. Starting Hermes Agent ==="
216
+ echo " Hermes running on local filesystem: $HERMES_WORK (FAST)"
217
+ echo " Persistent storage: $HERMES_BACKUP (synced every ${SYNC_INTERVAL}s)"
218
+ echo ""
219
+
220
+ cd "$HERMES_WORK"
221
+
222
+ # Trap SIGTERM untuk final backup sebelum shutdown (dipasang di Stage 2 karena exec gosu menggantikan Stage 1)
223
+ trap "echo ''; echo 'Shutting down... performing final backup'; safe_sync '$HERMES_WORK' '$HERMES_BACKUP'; echo 'Final backup completed'; exit 0" SIGTERM SIGINT
224
+
225
+ # # Initial backup segera setelah start (jangan tunggu interval pertama)
226
+ # (sleep 30 && echo "[$(date '+%Y-%m-%d %H:%M:%S')] Initial post-start backup" && safe_sync "$HERMES_WORK" "$HERMES_BACKUP" && echo "[$(date '+%Y-%m-%d %H:%M:%S')] Initial backup done") &
227
+
228
+ # Start dashboard di background (port 7861 untuk HF Spaces)
229
+ "${INSTALL_DIR}/.venv/bin/hermes" dashboard --host 0.0.0.0 --port 7861 --no-open --insecure &
230
+ DASHBOARD_PID=$!
231
+ echo " Dashboard started (PID $DASHBOARD_PID) on :7860"
232
+ sleep 2
233
+
234
+ exec "${INSTALL_DIR}/.venv/bin/python3" -u "${INSTALL_DIR}/gateway/run.py"
nginx-proxy.conf ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ proxy_set_header Host $host;
2
+ proxy_set_header X-Real-IP $remote_addr;
3
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
4
+ proxy_set_header X-Forwarded-Port $server_port;
5
+ proxy_set_header X-Forwarded-Host $host/a2a;
6
+ proxy_set_header Upgrade $http_upgrade;
7
+ proxy_set_header Connection "upgrade";
8
+ proxy_redirect off;
9
+ proxy_read_timeout 300s;
10
+ proxy_connect_timeout 10s;
nginx.conf ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ worker_processes auto;
2
+ events { worker_connections 1024; }
3
+
4
+ http {
5
+ include /etc/nginx/mime.types;
6
+ default_type application/octet-stream;
7
+ sendfile on;
8
+
9
+ server {
10
+ listen 7860;
11
+ server_name _;
12
+
13
+ location / {
14
+ proxy_pass http://127.0.0.1:7861;
15
+ include /etc/nginx/proxy.conf;
16
+ }
17
+
18
+ location /8642/ {
19
+ proxy_pass http://127.0.0.1:8642/;
20
+ include /etc/nginx/proxy.conf;
21
+ }
22
+
23
+ location /.well-known/agent-card.json {
24
+ proxy_pass http://127.0.0.1:9900/.well-known/agent-card.json;
25
+ include /etc/nginx/proxy.conf;
26
+ }
27
+
28
+ location /a2a/ {
29
+ proxy_pass http://127.0.0.1:9900/;
30
+ include /etc/nginx/proxy.conf;
31
+ }
32
+ }
33
+ }
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ huggingface_hub>=0.24.5