Dmitry Beresnev commited on
Commit ·
2f4c2db
1
Parent(s): 20f1d0e
Add Agent OS and Odysseus claw services
Browse filesWire both into the Docker image (npm install + env defaults), supervisord,
Caddy reverse proxy, and the Streamlit control center (status, restart
controls, UI links, config editors), following the existing claw-stack
pattern
- Caddyfile +8 -0
- Dockerfile +17 -1
- agent-os.json +32 -0
- app.py +138 -0
- odysseus.json +32 -0
- supervisord.conf +22 -0
Caddyfile
CHANGED
|
@@ -33,6 +33,14 @@ handle /hermes-agent* {
|
|
| 33 |
reverse_proxy 127.0.0.1:{$HERMES_AGENT_PORT}
|
| 34 |
}
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# OpenClaw UI uses absolute runtime paths; proxy them too.
|
| 37 |
handle /ws* {
|
| 38 |
reverse_proxy 127.0.0.1:18789 {
|
|
|
|
| 33 |
reverse_proxy 127.0.0.1:{$HERMES_AGENT_PORT}
|
| 34 |
}
|
| 35 |
|
| 36 |
+
handle /agent-os* {
|
| 37 |
+
reverse_proxy 127.0.0.1:{$AGENT_OS_PORT}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
handle /odysseus* {
|
| 41 |
+
reverse_proxy 127.0.0.1:{$ODYSSEUS_PORT}
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
# OpenClaw UI uses absolute runtime paths; proxy them too.
|
| 45 |
handle /ws* {
|
| 46 |
reverse_proxy 127.0.0.1:18789 {
|
Dockerfile
CHANGED
|
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 13 |
supervisor \
|
| 14 |
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
| 15 |
&& apt-get install -y --no-install-recommends nodejs \
|
| 16 |
-
&& npm install -g openclaw \
|
| 17 |
&& command -v openclaw \
|
| 18 |
&& openclaw --help >/dev/null 2>&1 \
|
| 19 |
&& node --version \
|
|
@@ -111,6 +111,22 @@ ENV NEMOCLAW_BASE_PATH=/nemoclaw
|
|
| 111 |
ENV NEMOCLAW_CONFIG_PATH=/app/nemoclaw.json
|
| 112 |
ENV NEMOCLAW_LOG_PATH=/tmp/nemoclaw.log
|
| 113 |
ENV NEMOCLAW_ERR_LOG_PATH=/tmp/nemoclaw.err.log
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
RUN mkdir -p /app/vault /app/.openclaw/state
|
| 116 |
|
|
|
|
| 13 |
supervisor \
|
| 14 |
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
| 15 |
&& apt-get install -y --no-install-recommends nodejs \
|
| 16 |
+
&& npm install -g openclaw agent-os odysseus \
|
| 17 |
&& command -v openclaw \
|
| 18 |
&& openclaw --help >/dev/null 2>&1 \
|
| 19 |
&& node --version \
|
|
|
|
| 111 |
ENV NEMOCLAW_CONFIG_PATH=/app/nemoclaw.json
|
| 112 |
ENV NEMOCLAW_LOG_PATH=/tmp/nemoclaw.log
|
| 113 |
ENV NEMOCLAW_ERR_LOG_PATH=/tmp/nemoclaw.err.log
|
| 114 |
+
ENV AGENT_OS_ENABLED=1
|
| 115 |
+
ENV AGENT_OS_PORT=18795
|
| 116 |
+
ENV AGENT_OS_CMD=agent-os
|
| 117 |
+
ENV AGENT_OS_ARGS="gateway --port 18795"
|
| 118 |
+
ENV AGENT_OS_BASE_PATH=/agent-os
|
| 119 |
+
ENV AGENT_OS_CONFIG_PATH=/app/agent-os.json
|
| 120 |
+
ENV AGENT_OS_LOG_PATH=/tmp/agent-os.log
|
| 121 |
+
ENV AGENT_OS_ERR_LOG_PATH=/tmp/agent-os.err.log
|
| 122 |
+
ENV ODYSSEUS_ENABLED=1
|
| 123 |
+
ENV ODYSSEUS_PORT=18796
|
| 124 |
+
ENV ODYSSEUS_CMD=odysseus
|
| 125 |
+
ENV ODYSSEUS_ARGS="gateway --port 18796"
|
| 126 |
+
ENV ODYSSEUS_BASE_PATH=/odysseus
|
| 127 |
+
ENV ODYSSEUS_CONFIG_PATH=/app/odysseus.json
|
| 128 |
+
ENV ODYSSEUS_LOG_PATH=/tmp/odysseus.log
|
| 129 |
+
ENV ODYSSEUS_ERR_LOG_PATH=/tmp/odysseus.err.log
|
| 130 |
|
| 131 |
RUN mkdir -p /app/vault /app/.openclaw/state
|
| 132 |
|
agent-os.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"agents": {
|
| 3 |
+
"defaults": {
|
| 4 |
+
"workspace": "/app/vault",
|
| 5 |
+
"model_name": "deepseek-chat",
|
| 6 |
+
"max_tokens": 8192,
|
| 7 |
+
"temperature": 0.7,
|
| 8 |
+
"max_tool_iterations": 20
|
| 9 |
+
}
|
| 10 |
+
},
|
| 11 |
+
"model_list": [
|
| 12 |
+
{
|
| 13 |
+
"model_name": "deepseek-chat",
|
| 14 |
+
"model": "openai/deepseek-chat",
|
| 15 |
+
"api_key": "${LLM_SPACE_API_KEY}",
|
| 16 |
+
"base_url": "https://researchengineering-agi.hf.space/v1",
|
| 17 |
+
"request_timeout": 120
|
| 18 |
+
}
|
| 19 |
+
],
|
| 20 |
+
"gateway": {
|
| 21 |
+
"host": "127.0.0.1",
|
| 22 |
+
"port": 18795
|
| 23 |
+
},
|
| 24 |
+
"autonomy": {
|
| 25 |
+
"level": "supervised",
|
| 26 |
+
"workspace_only": true,
|
| 27 |
+
"max_actions_per_hour": 20
|
| 28 |
+
},
|
| 29 |
+
"tunnel": {
|
| 30 |
+
"kind": "none"
|
| 31 |
+
}
|
| 32 |
+
}
|
app.py
CHANGED
|
@@ -105,6 +105,24 @@ IRONCLAW_LOG_PATH = Path(os.getenv("IRONCLAW_LOG_PATH", "/tmp/ironclaw.log"))
|
|
| 105 |
IRONCLAW_ERR_LOG_PATH = Path(os.getenv("IRONCLAW_ERR_LOG_PATH", "/tmp/ironclaw.err.log"))
|
| 106 |
IRONCLAW_ENABLED = os.getenv("IRONCLAW_ENABLED", "1") == "1"
|
| 107 |
IRONCLAW_CONFIG_PATH = Path(os.getenv("IRONCLAW_CONFIG_PATH", "ironclaw.json"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
STREAMLIT_AUTH_ENABLED = os.getenv("STREAMLIT_AUTH_ENABLED", "1") == "1"
|
| 109 |
STREAMLIT_AUTH_USERNAME = os.getenv("STREAMLIT_AUTH_USERNAME", "").strip()
|
| 110 |
STREAMLIT_AUTH_PASSWORD = os.getenv("STREAMLIT_AUTH_PASSWORD", "").strip()
|
|
@@ -131,6 +149,8 @@ def init_state() -> None:
|
|
| 131 |
st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
|
| 132 |
st.session_state.setdefault("nemoclaw_config_text", load_nemoclaw_config_text())
|
| 133 |
st.session_state.setdefault("ironclaw_config_text", load_ironclaw_config_text())
|
|
|
|
|
|
|
| 134 |
st.session_state.setdefault("auto_started", False)
|
| 135 |
st.session_state.setdefault("auto_start_attempted", False)
|
| 136 |
st.session_state.setdefault("backtest_result", None)
|
|
@@ -245,6 +265,32 @@ def load_ironclaw_config_json() -> dict:
|
|
| 245 |
return {}
|
| 246 |
|
| 247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
def gateway_process() -> subprocess.Popen | None:
|
| 249 |
proc = st.session_state.get("gateway_process")
|
| 250 |
if proc is None:
|
|
@@ -488,6 +534,18 @@ def is_nemoclaw_port_open(timeout: float = 0.5) -> bool:
|
|
| 488 |
return sock.connect_ex(("127.0.0.1", NEMOCLAW_PORT)) == 0
|
| 489 |
|
| 490 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
def supervisorctl(action: str, program: str) -> tuple[bool, str]:
|
| 492 |
cmd = shutil.which("supervisorctl")
|
| 493 |
if not cmd:
|
|
@@ -855,6 +913,8 @@ with svc_col_1:
|
|
| 855 |
zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
|
| 856 |
nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
|
| 857 |
nemoclaw_up = is_nemoclaw_port_open() if NEMOCLAW_ENABLED else False
|
|
|
|
|
|
|
| 858 |
st.write(f"OpenClaw: {'🟢 Running' if openclaw_up else '🔴 Down'} (port {OPENCLAW_PORT})")
|
| 859 |
st.write(f"NanoClaw: {'🟢 Running' if nanoclaw_up else '🔴 Down'} (port {NANOCLAW_PORT})")
|
| 860 |
st.write(f"NanoBot: {'🟢 Running' if nanobot_up else '🔴 Down'} (port {NANOBOT_PORT})")
|
|
@@ -863,6 +923,8 @@ with svc_col_1:
|
|
| 863 |
st.write(f"NullClaw: {'🟢 Running' if nullclaw_up else '🔴 Down'} (port {NULLCLAW_PORT})")
|
| 864 |
st.write(f"NemoClaw: {'🟢 Running' if nemoclaw_up else '🔴 Down'} (port {NEMOCLAW_PORT})")
|
| 865 |
st.write(f"IronClaw: {'🟢 Enabled' if IRONCLAW_ENABLED else '🔴 Disabled'} (no gateway port)")
|
|
|
|
|
|
|
| 866 |
|
| 867 |
with svc_col_2:
|
| 868 |
st.markdown("**Controls**")
|
|
@@ -890,6 +952,12 @@ with svc_col_2:
|
|
| 890 |
if st.button("Restart IronClaw", use_container_width=True):
|
| 891 |
ok, msg = supervisorctl("restart", "ironclaw")
|
| 892 |
(st.success if ok else st.error)(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 893 |
if not NANOCLAW_ENABLED:
|
| 894 |
st.info("NanoClaw is disabled. Set `NANOCLAW_ENABLED=1` to start it.")
|
| 895 |
if not NANOBOT_ENABLED:
|
|
@@ -904,6 +972,10 @@ with svc_col_2:
|
|
| 904 |
st.info("NemoClaw is disabled. Set `NEMOCLAW_ENABLED=1` to start it.")
|
| 905 |
if not IRONCLAW_ENABLED:
|
| 906 |
st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 907 |
|
| 908 |
st.divider()
|
| 909 |
|
|
@@ -1085,6 +1157,40 @@ with claw_ui_col_2:
|
|
| 1085 |
st.markdown("**IronClaw**")
|
| 1086 |
st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
|
| 1087 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1088 |
st.divider()
|
| 1089 |
|
| 1090 |
cfg_col, env_col = st.columns(2)
|
|
@@ -1181,6 +1287,22 @@ with cfg_a:
|
|
| 1181 |
except json.JSONDecodeError as exc:
|
| 1182 |
st.error(f"Invalid JSON: {exc}")
|
| 1183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1184 |
with cfg_b:
|
| 1185 |
zeroclaw_text = st.text_area(
|
| 1186 |
"zeroclaw.json",
|
|
@@ -1246,6 +1368,22 @@ with cfg_b:
|
|
| 1246 |
except json.JSONDecodeError as exc:
|
| 1247 |
st.error(f"Invalid JSON: {exc}")
|
| 1248 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1249 |
st.divider()
|
| 1250 |
|
| 1251 |
test_col, logs_col = st.columns([2, 3])
|
|
|
|
| 105 |
IRONCLAW_ERR_LOG_PATH = Path(os.getenv("IRONCLAW_ERR_LOG_PATH", "/tmp/ironclaw.err.log"))
|
| 106 |
IRONCLAW_ENABLED = os.getenv("IRONCLAW_ENABLED", "1") == "1"
|
| 107 |
IRONCLAW_CONFIG_PATH = Path(os.getenv("IRONCLAW_CONFIG_PATH", "ironclaw.json"))
|
| 108 |
+
AGENT_OS_CONFIG_PATH = Path(os.getenv("AGENT_OS_CONFIG_PATH", "agent-os.json"))
|
| 109 |
+
AGENT_OS_LOG_PATH = Path(os.getenv("AGENT_OS_LOG_PATH", "/tmp/agent-os.log"))
|
| 110 |
+
AGENT_OS_ERR_LOG_PATH = Path(os.getenv("AGENT_OS_ERR_LOG_PATH", "/tmp/agent-os.err.log"))
|
| 111 |
+
AGENT_OS_PORT = int(os.getenv("AGENT_OS_PORT", "18795"))
|
| 112 |
+
AGENT_OS_BASE_PATH = os.getenv("AGENT_OS_BASE_PATH", "/agent-os")
|
| 113 |
+
AGENT_OS_ENABLED = os.getenv("AGENT_OS_ENABLED", "1") == "1"
|
| 114 |
+
AGENT_OS_PROXY_LOCAL_URL = os.getenv(
|
| 115 |
+
"AGENT_OS_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{AGENT_OS_BASE_PATH}/"
|
| 116 |
+
)
|
| 117 |
+
ODYSSEUS_CONFIG_PATH = Path(os.getenv("ODYSSEUS_CONFIG_PATH", "odysseus.json"))
|
| 118 |
+
ODYSSEUS_LOG_PATH = Path(os.getenv("ODYSSEUS_LOG_PATH", "/tmp/odysseus.log"))
|
| 119 |
+
ODYSSEUS_ERR_LOG_PATH = Path(os.getenv("ODYSSEUS_ERR_LOG_PATH", "/tmp/odysseus.err.log"))
|
| 120 |
+
ODYSSEUS_PORT = int(os.getenv("ODYSSEUS_PORT", "18796"))
|
| 121 |
+
ODYSSEUS_BASE_PATH = os.getenv("ODYSSEUS_BASE_PATH", "/odysseus")
|
| 122 |
+
ODYSSEUS_ENABLED = os.getenv("ODYSSEUS_ENABLED", "1") == "1"
|
| 123 |
+
ODYSSEUS_PROXY_LOCAL_URL = os.getenv(
|
| 124 |
+
"ODYSSEUS_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{ODYSSEUS_BASE_PATH}/"
|
| 125 |
+
)
|
| 126 |
STREAMLIT_AUTH_ENABLED = os.getenv("STREAMLIT_AUTH_ENABLED", "1") == "1"
|
| 127 |
STREAMLIT_AUTH_USERNAME = os.getenv("STREAMLIT_AUTH_USERNAME", "").strip()
|
| 128 |
STREAMLIT_AUTH_PASSWORD = os.getenv("STREAMLIT_AUTH_PASSWORD", "").strip()
|
|
|
|
| 149 |
st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
|
| 150 |
st.session_state.setdefault("nemoclaw_config_text", load_nemoclaw_config_text())
|
| 151 |
st.session_state.setdefault("ironclaw_config_text", load_ironclaw_config_text())
|
| 152 |
+
st.session_state.setdefault("agent_os_config_text", load_agent_os_config_text())
|
| 153 |
+
st.session_state.setdefault("odysseus_config_text", load_odysseus_config_text())
|
| 154 |
st.session_state.setdefault("auto_started", False)
|
| 155 |
st.session_state.setdefault("auto_start_attempted", False)
|
| 156 |
st.session_state.setdefault("backtest_result", None)
|
|
|
|
| 265 |
return {}
|
| 266 |
|
| 267 |
|
| 268 |
+
def load_agent_os_config_text() -> str:
|
| 269 |
+
if AGENT_OS_CONFIG_PATH.exists():
|
| 270 |
+
return AGENT_OS_CONFIG_PATH.read_text(encoding="utf-8")
|
| 271 |
+
return "{}"
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def load_agent_os_config_json() -> dict:
|
| 275 |
+
try:
|
| 276 |
+
return json.loads(load_agent_os_config_text())
|
| 277 |
+
except json.JSONDecodeError:
|
| 278 |
+
return {}
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def load_odysseus_config_text() -> str:
|
| 282 |
+
if ODYSSEUS_CONFIG_PATH.exists():
|
| 283 |
+
return ODYSSEUS_CONFIG_PATH.read_text(encoding="utf-8")
|
| 284 |
+
return "{}"
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def load_odysseus_config_json() -> dict:
|
| 288 |
+
try:
|
| 289 |
+
return json.loads(load_odysseus_config_text())
|
| 290 |
+
except json.JSONDecodeError:
|
| 291 |
+
return {}
|
| 292 |
+
|
| 293 |
+
|
| 294 |
def gateway_process() -> subprocess.Popen | None:
|
| 295 |
proc = st.session_state.get("gateway_process")
|
| 296 |
if proc is None:
|
|
|
|
| 534 |
return sock.connect_ex(("127.0.0.1", NEMOCLAW_PORT)) == 0
|
| 535 |
|
| 536 |
|
| 537 |
+
def is_agent_os_port_open(timeout: float = 0.5) -> bool:
|
| 538 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
| 539 |
+
sock.settimeout(timeout)
|
| 540 |
+
return sock.connect_ex(("127.0.0.1", AGENT_OS_PORT)) == 0
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
def is_odysseus_port_open(timeout: float = 0.5) -> bool:
|
| 544 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
| 545 |
+
sock.settimeout(timeout)
|
| 546 |
+
return sock.connect_ex(("127.0.0.1", ODYSSEUS_PORT)) == 0
|
| 547 |
+
|
| 548 |
+
|
| 549 |
def supervisorctl(action: str, program: str) -> tuple[bool, str]:
|
| 550 |
cmd = shutil.which("supervisorctl")
|
| 551 |
if not cmd:
|
|
|
|
| 913 |
zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
|
| 914 |
nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
|
| 915 |
nemoclaw_up = is_nemoclaw_port_open() if NEMOCLAW_ENABLED else False
|
| 916 |
+
agent_os_up = is_agent_os_port_open() if AGENT_OS_ENABLED else False
|
| 917 |
+
odysseus_up = is_odysseus_port_open() if ODYSSEUS_ENABLED else False
|
| 918 |
st.write(f"OpenClaw: {'🟢 Running' if openclaw_up else '🔴 Down'} (port {OPENCLAW_PORT})")
|
| 919 |
st.write(f"NanoClaw: {'🟢 Running' if nanoclaw_up else '🔴 Down'} (port {NANOCLAW_PORT})")
|
| 920 |
st.write(f"NanoBot: {'🟢 Running' if nanobot_up else '🔴 Down'} (port {NANOBOT_PORT})")
|
|
|
|
| 923 |
st.write(f"NullClaw: {'🟢 Running' if nullclaw_up else '🔴 Down'} (port {NULLCLAW_PORT})")
|
| 924 |
st.write(f"NemoClaw: {'🟢 Running' if nemoclaw_up else '🔴 Down'} (port {NEMOCLAW_PORT})")
|
| 925 |
st.write(f"IronClaw: {'🟢 Enabled' if IRONCLAW_ENABLED else '🔴 Disabled'} (no gateway port)")
|
| 926 |
+
st.write(f"Agent OS: {'🟢 Running' if agent_os_up else '🔴 Down'} (port {AGENT_OS_PORT})")
|
| 927 |
+
st.write(f"Odysseus: {'🟢 Running' if odysseus_up else '🔴 Down'} (port {ODYSSEUS_PORT})")
|
| 928 |
|
| 929 |
with svc_col_2:
|
| 930 |
st.markdown("**Controls**")
|
|
|
|
| 952 |
if st.button("Restart IronClaw", use_container_width=True):
|
| 953 |
ok, msg = supervisorctl("restart", "ironclaw")
|
| 954 |
(st.success if ok else st.error)(msg)
|
| 955 |
+
if st.button("Restart Agent OS", use_container_width=True):
|
| 956 |
+
ok, msg = supervisorctl("restart", "agent-os")
|
| 957 |
+
(st.success if ok else st.error)(msg)
|
| 958 |
+
if st.button("Restart Odysseus", use_container_width=True):
|
| 959 |
+
ok, msg = supervisorctl("restart", "odysseus")
|
| 960 |
+
(st.success if ok else st.error)(msg)
|
| 961 |
if not NANOCLAW_ENABLED:
|
| 962 |
st.info("NanoClaw is disabled. Set `NANOCLAW_ENABLED=1` to start it.")
|
| 963 |
if not NANOBOT_ENABLED:
|
|
|
|
| 972 |
st.info("NemoClaw is disabled. Set `NEMOCLAW_ENABLED=1` to start it.")
|
| 973 |
if not IRONCLAW_ENABLED:
|
| 974 |
st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
|
| 975 |
+
if not AGENT_OS_ENABLED:
|
| 976 |
+
st.info("Agent OS is disabled. Set `AGENT_OS_ENABLED=1` to start it.")
|
| 977 |
+
if not ODYSSEUS_ENABLED:
|
| 978 |
+
st.info("Odysseus is disabled. Set `ODYSSEUS_ENABLED=1` to start it.")
|
| 979 |
|
| 980 |
st.divider()
|
| 981 |
|
|
|
|
| 1157 |
st.markdown("**IronClaw**")
|
| 1158 |
st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
|
| 1159 |
|
| 1160 |
+
st.markdown("**Agent OS**")
|
| 1161 |
+
agent_os_ok = False
|
| 1162 |
+
if AGENT_OS_ENABLED:
|
| 1163 |
+
try:
|
| 1164 |
+
agent_os_resp = requests.get(AGENT_OS_PROXY_LOCAL_URL, timeout=2, allow_redirects=True)
|
| 1165 |
+
agent_os_ok = agent_os_resp.status_code < 500
|
| 1166 |
+
except Exception:
|
| 1167 |
+
agent_os_ok = False
|
| 1168 |
+
if AGENT_OS_ENABLED:
|
| 1169 |
+
st.markdown(
|
| 1170 |
+
f'<a href="{AGENT_OS_PROXY_LOCAL_URL}" target="_blank" rel="noopener noreferrer">Open Agent OS</a>',
|
| 1171 |
+
unsafe_allow_html=True,
|
| 1172 |
+
)
|
| 1173 |
+
st.caption(f"Proxy: {AGENT_OS_PROXY_LOCAL_URL} ({'ok' if agent_os_ok else 'down'})")
|
| 1174 |
+
else:
|
| 1175 |
+
st.caption("Disabled")
|
| 1176 |
+
|
| 1177 |
+
st.markdown("**Odysseus**")
|
| 1178 |
+
odysseus_ok = False
|
| 1179 |
+
if ODYSSEUS_ENABLED:
|
| 1180 |
+
try:
|
| 1181 |
+
odysseus_resp = requests.get(ODYSSEUS_PROXY_LOCAL_URL, timeout=2, allow_redirects=True)
|
| 1182 |
+
odysseus_ok = odysseus_resp.status_code < 500
|
| 1183 |
+
except Exception:
|
| 1184 |
+
odysseus_ok = False
|
| 1185 |
+
if ODYSSEUS_ENABLED:
|
| 1186 |
+
st.markdown(
|
| 1187 |
+
f'<a href="{ODYSSEUS_PROXY_LOCAL_URL}" target="_blank" rel="noopener noreferrer">Open Odysseus</a>',
|
| 1188 |
+
unsafe_allow_html=True,
|
| 1189 |
+
)
|
| 1190 |
+
st.caption(f"Proxy: {ODYSSEUS_PROXY_LOCAL_URL} ({'ok' if odysseus_ok else 'down'})")
|
| 1191 |
+
else:
|
| 1192 |
+
st.caption("Disabled")
|
| 1193 |
+
|
| 1194 |
st.divider()
|
| 1195 |
|
| 1196 |
cfg_col, env_col = st.columns(2)
|
|
|
|
| 1287 |
except json.JSONDecodeError as exc:
|
| 1288 |
st.error(f"Invalid JSON: {exc}")
|
| 1289 |
|
| 1290 |
+
agent_os_text = st.text_area(
|
| 1291 |
+
"agent-os.json",
|
| 1292 |
+
value=st.session_state.get("agent_os_config_text", load_agent_os_config_text()),
|
| 1293 |
+
height=220,
|
| 1294 |
+
)
|
| 1295 |
+
st.session_state["agent_os_config_text"] = agent_os_text
|
| 1296 |
+
if st.button("Save Agent OS Config", use_container_width=True):
|
| 1297 |
+
try:
|
| 1298 |
+
parsed = json.loads(agent_os_text)
|
| 1299 |
+
AGENT_OS_CONFIG_PATH.write_text(
|
| 1300 |
+
json.dumps(parsed, indent=2) + "\n", encoding="utf-8"
|
| 1301 |
+
)
|
| 1302 |
+
st.success(f"Saved {AGENT_OS_CONFIG_PATH}.")
|
| 1303 |
+
except json.JSONDecodeError as exc:
|
| 1304 |
+
st.error(f"Invalid JSON: {exc}")
|
| 1305 |
+
|
| 1306 |
with cfg_b:
|
| 1307 |
zeroclaw_text = st.text_area(
|
| 1308 |
"zeroclaw.json",
|
|
|
|
| 1368 |
except json.JSONDecodeError as exc:
|
| 1369 |
st.error(f"Invalid JSON: {exc}")
|
| 1370 |
|
| 1371 |
+
odysseus_text = st.text_area(
|
| 1372 |
+
"odysseus.json",
|
| 1373 |
+
value=st.session_state.get("odysseus_config_text", load_odysseus_config_text()),
|
| 1374 |
+
height=220,
|
| 1375 |
+
)
|
| 1376 |
+
st.session_state["odysseus_config_text"] = odysseus_text
|
| 1377 |
+
if st.button("Save Odysseus Config", use_container_width=True):
|
| 1378 |
+
try:
|
| 1379 |
+
parsed = json.loads(odysseus_text)
|
| 1380 |
+
ODYSSEUS_CONFIG_PATH.write_text(
|
| 1381 |
+
json.dumps(parsed, indent=2) + "\n", encoding="utf-8"
|
| 1382 |
+
)
|
| 1383 |
+
st.success(f"Saved {ODYSSEUS_CONFIG_PATH}.")
|
| 1384 |
+
except json.JSONDecodeError as exc:
|
| 1385 |
+
st.error(f"Invalid JSON: {exc}")
|
| 1386 |
+
|
| 1387 |
st.divider()
|
| 1388 |
|
| 1389 |
test_col, logs_col = st.columns([2, 3])
|
odysseus.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"agents": {
|
| 3 |
+
"defaults": {
|
| 4 |
+
"workspace": "/app/vault",
|
| 5 |
+
"model_name": "deepseek-chat",
|
| 6 |
+
"max_tokens": 8192,
|
| 7 |
+
"temperature": 0.7,
|
| 8 |
+
"max_tool_iterations": 20
|
| 9 |
+
}
|
| 10 |
+
},
|
| 11 |
+
"model_list": [
|
| 12 |
+
{
|
| 13 |
+
"model_name": "deepseek-chat",
|
| 14 |
+
"model": "openai/deepseek-chat",
|
| 15 |
+
"api_key": "${LLM_SPACE_API_KEY}",
|
| 16 |
+
"base_url": "https://researchengineering-agi.hf.space/v1",
|
| 17 |
+
"request_timeout": 120
|
| 18 |
+
}
|
| 19 |
+
],
|
| 20 |
+
"gateway": {
|
| 21 |
+
"host": "127.0.0.1",
|
| 22 |
+
"port": 18796
|
| 23 |
+
},
|
| 24 |
+
"autonomy": {
|
| 25 |
+
"level": "supervised",
|
| 26 |
+
"workspace_only": true,
|
| 27 |
+
"max_actions_per_hour": 20
|
| 28 |
+
},
|
| 29 |
+
"tunnel": {
|
| 30 |
+
"kind": "none"
|
| 31 |
+
}
|
| 32 |
+
}
|
supervisord.conf
CHANGED
|
@@ -111,3 +111,25 @@ killasgroup=true
|
|
| 111 |
stdout_logfile=/tmp/hermes-agent.log
|
| 112 |
stderr_logfile=/tmp/hermes-agent.err.log
|
| 113 |
environment=HOME="/root",HERMES_AGENT_CONFIG_PATH="/app/hermes-agent.json",HERMES_AGENT_PORT="18794",HERMES_AGENT_BASE_PATH="/hermes-agent"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
stdout_logfile=/tmp/hermes-agent.log
|
| 112 |
stderr_logfile=/tmp/hermes-agent.err.log
|
| 113 |
environment=HOME="/root",HERMES_AGENT_CONFIG_PATH="/app/hermes-agent.json",HERMES_AGENT_PORT="18794",HERMES_AGENT_BASE_PATH="/hermes-agent"
|
| 114 |
+
|
| 115 |
+
[program:agent-os]
|
| 116 |
+
command=/bin/sh -lc "if [ \"${AGENT_OS_ENABLED:-1}\" = \"1\" ]; then ${AGENT_OS_CMD} ${AGENT_OS_ARGS}; else echo '[agent-os] disabled'; sleep infinity; fi"
|
| 117 |
+
autorestart=true
|
| 118 |
+
startsecs=5
|
| 119 |
+
startretries=10
|
| 120 |
+
stopasgroup=true
|
| 121 |
+
killasgroup=true
|
| 122 |
+
stdout_logfile=/tmp/agent-os.log
|
| 123 |
+
stderr_logfile=/tmp/agent-os.err.log
|
| 124 |
+
environment=HOME="/root",AGENT_OS_CONFIG_PATH="/app/agent-os.json",AGENT_OS_PORT="18795",AGENT_OS_BASE_PATH="/agent-os"
|
| 125 |
+
|
| 126 |
+
[program:odysseus]
|
| 127 |
+
command=/bin/sh -lc "if [ \"${ODYSSEUS_ENABLED:-1}\" = \"1\" ]; then ${ODYSSEUS_CMD} ${ODYSSEUS_ARGS}; else echo '[odysseus] disabled'; sleep infinity; fi"
|
| 128 |
+
autorestart=true
|
| 129 |
+
startsecs=5
|
| 130 |
+
startretries=10
|
| 131 |
+
stopasgroup=true
|
| 132 |
+
killasgroup=true
|
| 133 |
+
stdout_logfile=/tmp/odysseus.log
|
| 134 |
+
stderr_logfile=/tmp/odysseus.err.log
|
| 135 |
+
environment=HOME="/root",ODYSSEUS_CONFIG_PATH="/app/odysseus.json",ODYSSEUS_PORT="18796",ODYSSEUS_BASE_PATH="/odysseus"
|