Dmitry Beresnev commited on
Commit
bbb7286
Β·
1 Parent(s): 15c8440

add nemoclaw from nvidia

Browse files
Files changed (1) hide show
  1. app.py +73 -0
app.py CHANGED
@@ -30,6 +30,7 @@ NANOBOT_CONFIG_PATH = Path(os.getenv("NANOBOT_CONFIG_PATH", "nanobot.json"))
30
  PICOCLAW_CONFIG_PATH = Path(os.getenv("PICOCLAW_CONFIG_PATH", "picoclaw.json"))
31
  ZEROCLAW_CONFIG_PATH = Path(os.getenv("ZEROCLAW_CONFIG_PATH", "zeroclaw.json"))
32
  NULLCLAW_CONFIG_PATH = Path(os.getenv("NULLCLAW_CONFIG_PATH", "nullclaw.json"))
 
33
  ENV_EXAMPLE_PATH = Path("config/openclaw.env.example")
34
  LOG_MAX_LINES = 300
35
  OPENCLAW_STANDARD_UI_URL = os.getenv(
@@ -92,6 +93,14 @@ NULLCLAW_ENABLED = os.getenv("NULLCLAW_ENABLED", "1") == "1"
92
  NULLCLAW_PROXY_LOCAL_URL = os.getenv(
93
  "NULLCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NULLCLAW_BASE_PATH}/"
94
  )
 
 
 
 
 
 
 
 
95
  IRONCLAW_LOG_PATH = Path(os.getenv("IRONCLAW_LOG_PATH", "/tmp/ironclaw.log"))
96
  IRONCLAW_ERR_LOG_PATH = Path(os.getenv("IRONCLAW_ERR_LOG_PATH", "/tmp/ironclaw.err.log"))
97
  IRONCLAW_ENABLED = os.getenv("IRONCLAW_ENABLED", "1") == "1"
@@ -119,6 +128,7 @@ def init_state() -> None:
119
  st.session_state.setdefault("picoclaw_config_text", load_picoclaw_config_text())
120
  st.session_state.setdefault("zeroclaw_config_text", load_zeroclaw_config_text())
121
  st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
 
122
  st.session_state.setdefault("auto_started", False)
123
  st.session_state.setdefault("auto_start_attempted", False)
124
  st.session_state.setdefault("backtest_result", None)
@@ -207,6 +217,19 @@ def load_nullclaw_config_json() -> dict:
207
  return {}
208
 
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  def gateway_process() -> subprocess.Popen | None:
211
  proc = st.session_state.get("gateway_process")
212
  if proc is None:
@@ -444,6 +467,12 @@ def is_nullclaw_port_open(timeout: float = 0.5) -> bool:
444
  return sock.connect_ex(("127.0.0.1", NULLCLAW_PORT)) == 0
445
 
446
 
 
 
 
 
 
 
447
  def supervisorctl(action: str, program: str) -> tuple[bool, str]:
448
  cmd = shutil.which("supervisorctl")
449
  if not cmd:
@@ -810,12 +839,14 @@ with svc_col_1:
810
  picoclaw_up = is_picoclaw_port_open() if PICOCLAW_ENABLED else False
811
  zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
812
  nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
 
813
  st.write(f"OpenClaw: {'🟒 Running' if openclaw_up else 'πŸ”΄ Down'} (port {OPENCLAW_PORT})")
814
  st.write(f"NanoClaw: {'🟒 Running' if nanoclaw_up else 'πŸ”΄ Down'} (port {NANOCLAW_PORT})")
815
  st.write(f"NanoBot: {'🟒 Running' if nanobot_up else 'πŸ”΄ Down'} (port {NANOBOT_PORT})")
816
  st.write(f"PicoClaw: {'🟒 Running' if picoclaw_up else 'πŸ”΄ Down'} (port {PICOCLAW_PORT})")
817
  st.write(f"ZeroClaw: {'🟒 Running' if zeroclaw_up else 'πŸ”΄ Down'} (port {ZEROCLAW_PORT})")
818
  st.write(f"NullClaw: {'🟒 Running' if nullclaw_up else 'πŸ”΄ Down'} (port {NULLCLAW_PORT})")
 
819
  st.write(f"IronClaw: {'🟒 Enabled' if IRONCLAW_ENABLED else 'πŸ”΄ Disabled'} (no gateway port)")
820
 
821
  with svc_col_2:
@@ -838,6 +869,9 @@ with svc_col_2:
838
  if st.button("Restart NullClaw", use_container_width=True):
839
  ok, msg = supervisorctl("restart", "nullclaw")
840
  (st.success if ok else st.error)(msg)
 
 
 
841
  if st.button("Restart IronClaw", use_container_width=True):
842
  ok, msg = supervisorctl("restart", "ironclaw")
843
  (st.success if ok else st.error)(msg)
@@ -851,6 +885,8 @@ with svc_col_2:
851
  st.info("ZeroClaw is disabled. Set `ZEROCLAW_ENABLED=1` to start it.")
852
  if not NULLCLAW_ENABLED:
853
  st.info("NullClaw is disabled. Set `NULLCLAW_ENABLED=1` to start it.")
 
 
854
  if not IRONCLAW_ENABLED:
855
  st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
856
 
@@ -1014,6 +1050,23 @@ with claw_ui_col_2:
1014
  else:
1015
  st.caption("Disabled")
1016
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1017
  st.markdown("**IronClaw**")
1018
  st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
1019
 
@@ -1146,6 +1199,22 @@ with cfg_b:
1146
  except json.JSONDecodeError as exc:
1147
  st.error(f"Invalid JSON: {exc}")
1148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1149
  st.divider()
1150
 
1151
  test_col, logs_col = st.columns([2, 3])
@@ -1188,6 +1257,10 @@ st.expander("Service Diagnostics (Supervisor/Caddy/Streamlit/OpenClaw)").code(
1188
  tail_text_file(NULLCLAW_ERR_LOG_PATH),
1189
  "=== nullclaw.log ===",
1190
  tail_text_file(NULLCLAW_LOG_PATH),
 
 
 
 
1191
  "=== ironclaw.err.log ===",
1192
  tail_text_file(IRONCLAW_ERR_LOG_PATH),
1193
  "=== ironclaw.log ===",
 
30
  PICOCLAW_CONFIG_PATH = Path(os.getenv("PICOCLAW_CONFIG_PATH", "picoclaw.json"))
31
  ZEROCLAW_CONFIG_PATH = Path(os.getenv("ZEROCLAW_CONFIG_PATH", "zeroclaw.json"))
32
  NULLCLAW_CONFIG_PATH = Path(os.getenv("NULLCLAW_CONFIG_PATH", "nullclaw.json"))
33
+ NEMOCLAW_CONFIG_PATH = Path(os.getenv("NEMOCLAW_CONFIG_PATH", "nemoclaw.json"))
34
  ENV_EXAMPLE_PATH = Path("config/openclaw.env.example")
35
  LOG_MAX_LINES = 300
36
  OPENCLAW_STANDARD_UI_URL = os.getenv(
 
93
  NULLCLAW_PROXY_LOCAL_URL = os.getenv(
94
  "NULLCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NULLCLAW_BASE_PATH}/"
95
  )
96
+ NEMOCLAW_LOG_PATH = Path(os.getenv("NEMOCLAW_LOG_PATH", "/tmp/nemoclaw.log"))
97
+ NEMOCLAW_ERR_LOG_PATH = Path(os.getenv("NEMOCLAW_ERR_LOG_PATH", "/tmp/nemoclaw.err.log"))
98
+ NEMOCLAW_PORT = int(os.getenv("NEMOCLAW_PORT", "18793"))
99
+ NEMOCLAW_BASE_PATH = os.getenv("NEMOCLAW_BASE_PATH", "/nemoclaw")
100
+ NEMOCLAW_ENABLED = os.getenv("NEMOCLAW_ENABLED", "1") == "1"
101
+ NEMOCLAW_PROXY_LOCAL_URL = os.getenv(
102
+ "NEMOCLAW_PROXY_LOCAL_URL", f"http://127.0.0.1:7860{NEMOCLAW_BASE_PATH}/"
103
+ )
104
  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"
 
128
  st.session_state.setdefault("picoclaw_config_text", load_picoclaw_config_text())
129
  st.session_state.setdefault("zeroclaw_config_text", load_zeroclaw_config_text())
130
  st.session_state.setdefault("nullclaw_config_text", load_nullclaw_config_text())
131
+ st.session_state.setdefault("nemoclaw_config_text", load_nemoclaw_config_text())
132
  st.session_state.setdefault("auto_started", False)
133
  st.session_state.setdefault("auto_start_attempted", False)
134
  st.session_state.setdefault("backtest_result", None)
 
217
  return {}
218
 
219
 
220
+ def load_nemoclaw_config_text() -> str:
221
+ if NEMOCLAW_CONFIG_PATH.exists():
222
+ return NEMOCLAW_CONFIG_PATH.read_text(encoding="utf-8")
223
+ return "{}"
224
+
225
+
226
+ def load_nemoclaw_config_json() -> dict:
227
+ try:
228
+ return json.loads(load_nemoclaw_config_text())
229
+ except json.JSONDecodeError:
230
+ return {}
231
+
232
+
233
  def gateway_process() -> subprocess.Popen | None:
234
  proc = st.session_state.get("gateway_process")
235
  if proc is None:
 
467
  return sock.connect_ex(("127.0.0.1", NULLCLAW_PORT)) == 0
468
 
469
 
470
+ def is_nemoclaw_port_open(timeout: float = 0.5) -> bool:
471
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
472
+ sock.settimeout(timeout)
473
+ return sock.connect_ex(("127.0.0.1", NEMOCLAW_PORT)) == 0
474
+
475
+
476
  def supervisorctl(action: str, program: str) -> tuple[bool, str]:
477
  cmd = shutil.which("supervisorctl")
478
  if not cmd:
 
839
  picoclaw_up = is_picoclaw_port_open() if PICOCLAW_ENABLED else False
840
  zeroclaw_up = is_zeroclaw_port_open() if ZEROCLAW_ENABLED else False
841
  nullclaw_up = is_nullclaw_port_open() if NULLCLAW_ENABLED else False
842
+ nemoclaw_up = is_nemoclaw_port_open() if NEMOCLAW_ENABLED else False
843
  st.write(f"OpenClaw: {'🟒 Running' if openclaw_up else 'πŸ”΄ Down'} (port {OPENCLAW_PORT})")
844
  st.write(f"NanoClaw: {'🟒 Running' if nanoclaw_up else 'πŸ”΄ Down'} (port {NANOCLAW_PORT})")
845
  st.write(f"NanoBot: {'🟒 Running' if nanobot_up else 'πŸ”΄ Down'} (port {NANOBOT_PORT})")
846
  st.write(f"PicoClaw: {'🟒 Running' if picoclaw_up else 'πŸ”΄ Down'} (port {PICOCLAW_PORT})")
847
  st.write(f"ZeroClaw: {'🟒 Running' if zeroclaw_up else 'πŸ”΄ Down'} (port {ZEROCLAW_PORT})")
848
  st.write(f"NullClaw: {'🟒 Running' if nullclaw_up else 'πŸ”΄ Down'} (port {NULLCLAW_PORT})")
849
+ st.write(f"NemoClaw: {'🟒 Running' if nemoclaw_up else 'πŸ”΄ Down'} (port {NEMOCLAW_PORT})")
850
  st.write(f"IronClaw: {'🟒 Enabled' if IRONCLAW_ENABLED else 'πŸ”΄ Disabled'} (no gateway port)")
851
 
852
  with svc_col_2:
 
869
  if st.button("Restart NullClaw", use_container_width=True):
870
  ok, msg = supervisorctl("restart", "nullclaw")
871
  (st.success if ok else st.error)(msg)
872
+ if st.button("Restart NemoClaw", use_container_width=True):
873
+ ok, msg = supervisorctl("restart", "nemoclaw")
874
+ (st.success if ok else st.error)(msg)
875
  if st.button("Restart IronClaw", use_container_width=True):
876
  ok, msg = supervisorctl("restart", "ironclaw")
877
  (st.success if ok else st.error)(msg)
 
885
  st.info("ZeroClaw is disabled. Set `ZEROCLAW_ENABLED=1` to start it.")
886
  if not NULLCLAW_ENABLED:
887
  st.info("NullClaw is disabled. Set `NULLCLAW_ENABLED=1` to start it.")
888
+ if not NEMOCLAW_ENABLED:
889
+ st.info("NemoClaw is disabled. Set `NEMOCLAW_ENABLED=1` to start it.")
890
  if not IRONCLAW_ENABLED:
891
  st.info("IronClaw is disabled. Set `IRONCLAW_ENABLED=1` to start it.")
892
 
 
1050
  else:
1051
  st.caption("Disabled")
1052
 
1053
+ st.markdown("**NemoClaw**")
1054
+ nemoclaw_ok = False
1055
+ if NEMOCLAW_ENABLED:
1056
+ try:
1057
+ nemoclaw_resp = requests.get(NEMOCLAW_PROXY_LOCAL_URL, timeout=2, allow_redirects=True)
1058
+ nemoclaw_ok = nemoclaw_resp.status_code < 500
1059
+ except Exception:
1060
+ nemoclaw_ok = False
1061
+ if NEMOCLAW_ENABLED:
1062
+ st.markdown(
1063
+ f'<a href="{NEMOCLAW_PROXY_LOCAL_URL}" target="_blank" rel="noopener noreferrer">Open NemoClaw</a>',
1064
+ unsafe_allow_html=True,
1065
+ )
1066
+ st.caption(f"Proxy: {NEMOCLAW_PROXY_LOCAL_URL} ({'ok' if nemoclaw_ok else 'down'})")
1067
+ else:
1068
+ st.caption("Disabled")
1069
+
1070
  st.markdown("**IronClaw**")
1071
  st.caption("No gateway port detected. Run `ironclaw` CLI after onboarding.")
1072
 
 
1199
  except json.JSONDecodeError as exc:
1200
  st.error(f"Invalid JSON: {exc}")
1201
 
1202
+ nemoclaw_text = st.text_area(
1203
+ "nemoclaw.json",
1204
+ value=st.session_state.get("nemoclaw_config_text", load_nemoclaw_config_text()),
1205
+ height=220,
1206
+ )
1207
+ st.session_state["nemoclaw_config_text"] = nemoclaw_text
1208
+ if st.button("Save NemoClaw Config", use_container_width=True):
1209
+ try:
1210
+ parsed = json.loads(nemoclaw_text)
1211
+ NEMOCLAW_CONFIG_PATH.write_text(
1212
+ json.dumps(parsed, indent=2) + "\n", encoding="utf-8"
1213
+ )
1214
+ st.success(f"Saved {NEMOCLAW_CONFIG_PATH}.")
1215
+ except json.JSONDecodeError as exc:
1216
+ st.error(f"Invalid JSON: {exc}")
1217
+
1218
  st.divider()
1219
 
1220
  test_col, logs_col = st.columns([2, 3])
 
1257
  tail_text_file(NULLCLAW_ERR_LOG_PATH),
1258
  "=== nullclaw.log ===",
1259
  tail_text_file(NULLCLAW_LOG_PATH),
1260
+ "=== nemoclaw.err.log ===",
1261
+ tail_text_file(NEMOCLAW_ERR_LOG_PATH),
1262
+ "=== nemoclaw.log ===",
1263
+ tail_text_file(NEMOCLAW_LOG_PATH),
1264
  "=== ironclaw.err.log ===",
1265
  tail_text_file(IRONCLAW_ERR_LOG_PATH),
1266
  "=== ironclaw.log ===",