import os import subprocess import sys import time import threading def run_cmd(cmd, cwd=None, check=True, env=None): print(f"Running: {cmd}", flush=True) result = subprocess.run(cmd, shell=True, cwd=cwd, env=env) if check and result.returncode != 0: print(f"WARNING: Command exited with code {result.returncode}", flush=True) def launch_dummy_gradio(): try: import spaces import gradio as gr @spaces.GPU def dummy_gpu_task(): return "OK" with gr.Blocks() as demo: gr.Markdown("ZeroGPU Dummy") btn = gr.Button("Run") btn.click(dummy_gpu_task) print("Launching dummy Gradio app on port 7861 to satisfy ZeroGPU...", flush=True) # Use a random port, HF only checks if launch is called demo.launch(server_name="127.0.0.1", server_port=7861) except Exception as e: print(f"Dummy Gradio launch failed: {e}", flush=True) def start_services(): print(f"\n===== Application Startup at {time.strftime('%Y-%m-%d %H:%M:%S')} =====\n", flush=True) # Start dummy gradio in background threading.Thread(target=launch_dummy_gradio, daemon=True).start() # ── 1. Node.js ────────────────────────────────────────────────────────────── node_dir = "node-v22.14.0-linux-x64" if not os.path.exists(node_dir): print("Downloading Node.js 22...", flush=True) run_cmd("curl -O https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz") run_cmd("tar -xf node-v22.14.0-linux-x64.tar.xz") os.environ["PATH"] = ( f"{os.getcwd()}/{node_dir}/bin" f":/home/user/.local/bin" + ":" + os.environ.get("PATH", "") ) # ── 2. hermes-agent (backend Python package) ───────────────────────────────── if not os.path.exists("hermes-agent"): print("Cloning hermes-agent...", flush=True) run_cmd("git clone https://github.com/NousResearch/hermes-agent.git") run_cmd("pip install -e .", cwd="hermes-agent") # ── 3. Configure NVIDIA API provider ──────────────────────────────────────── nvidia_key = os.environ.get("NVIDIA_API_KEY", "") if nvidia_key: print("Configuring NVIDIA API provider...", flush=True) # Use OpenAI-compatible provider pointing to NVIDIA's API run_cmd("hermes config set model.provider openai", check=False) run_cmd("hermes config set model.base_url https://integrate.api.nvidia.com/v1", check=False) run_cmd("hermes config set model.default nvidia/llama-3.1-nemotron-ultra-253b-v1", check=False) # Expose key as OPENAI_API_KEY so openai SDK picks it up os.environ["OPENAI_API_KEY"] = nvidia_key print(f"NVIDIA API configured. Default model: nvidia/llama-3.1-nemotron-ultra-253b-v1", flush=True) else: print("WARNING: NVIDIA_API_KEY not set. Chat will not work without an API key.", flush=True) # ── 4. Restore persistent memory from Dataset ───────────────────────────────── print("Pulling dataset state...", flush=True) run_cmd("python sync.py pull", check=False) # ── 5. hermes-workspace (frontend) ─────────────────────────────────────────── if not os.path.exists("hermes-workspace"): print("Cloning hermes-workspace...", flush=True) run_cmd("git clone https://github.com/outsourc-e/hermes-workspace.git") else: print("Updating hermes-workspace...", flush=True) run_cmd("git pull", cwd="hermes-workspace", check=False) # Write .env so the UI knows where the gateway and agent live env_content = ( "HERMES_API_URL=http://127.0.0.1:8642\n" "HERMES_DASHBOARD_URL=http://127.0.0.1:9119\n" "HERMES_AGENT_PATH=/home/user/app/hermes-agent\n" "HERMES_ALLOW_INSECURE_REMOTE=1\n" "HOST=0.0.0.0\n" "PORT=7860\n" "COOKIE_SECURE=0\n" ) with open("hermes-workspace/.env", "w") as f: f.write(env_content) # Install pnpm + dependencies + build (only if dist/ doesn't exist yet) if not os.path.exists("hermes-workspace/dist"): run_cmd("npm install -g pnpm", check=False) run_cmd("pnpm install", cwd="hermes-workspace") print("Building production bundle...", flush=True) run_cmd("pnpm build", cwd="hermes-workspace") # ── 6. Start background services ───────────────────────────────────────────── print("Starting sync daemon...", flush=True) subprocess.Popen("python sync.py daemon", shell=True) # Build the shared env for all subprocesses so they all get the API key child_env = { **os.environ, "OPENAI_API_KEY": nvidia_key, "OPENAI_BASE_URL": "https://integrate.api.nvidia.com/v1", } # Write to ~/.hermes/.env as well since hermes-agent might read directly from there hermes_env_dir = os.path.expanduser("~/.hermes") os.makedirs(hermes_env_dir, exist_ok=True) hermes_env_file = os.path.join(hermes_env_dir, ".env") with open(hermes_env_file, "w") as f: f.write("API_SERVER_ENABLED=true\n") f.write("API_SERVER_HOST=127.0.0.1\n") f.write("API_SERVER_KEY=hf_space_local_api_key_123\n") if nvidia_key: f.write(f"OPENAI_API_KEY={nvidia_key}\n") f.write("OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1\n") print("Starting Hermes Gateway (port 8642)...", flush=True) subprocess.Popen("hermes gateway run > hermes-workspace/dist/client/gateway.log 2>&1", shell=True, env=child_env) print("Starting Hermes Dashboard (port 9119)...", flush=True) subprocess.Popen("hermes dashboard > hermes-workspace/dist/client/dashboard.log 2>&1", shell=True, env=child_env) print("Waiting 10s for gateway & dashboard to come up...", flush=True) time.sleep(10) # DEBUG: dump ss subprocess.Popen("ss -tlnp > hermes-workspace/dist/client/ss.log 2>&1", shell=True, env=child_env) # DEBUG: dump hermes version subprocess.Popen("hermes --version > hermes-workspace/dist/client/hermes_version.log 2>&1", shell=True, env=child_env) # ── 7. Start the production UI server ──────────────────────────────────────── print("Starting Hermes Workspace UI on port 7860...", flush=True) ui_env = { **child_env, "HOST": "0.0.0.0", "PORT": "7860", "HERMES_ALLOW_INSECURE_REMOTE": "1", "COOKIE_SECURE": "0", "HERMES_API_URL": "http://127.0.0.1:8642", "HERMES_DASHBOARD_URL": "http://127.0.0.1:9119", "HERMES_API_TOKEN": "hf_space_local_api_key_123", } ui_proc = subprocess.Popen( "node server-entry.js", shell=True, cwd="hermes-workspace", env=ui_env, ) ui_proc.wait() if __name__ == "__main__": start_services()