feat(tui): add /detach command (#5461)

* feat(tui): add detach command

* fix(tui): print exact detached gateway stop command
This commit is contained in:
chengyongru
2026-08-21 15:50:07 +08:00
committed by GitHub
parent 5b44ebdfd7
commit 26764f2423
9 changed files with 163 additions and 9 deletions
+54
View File
@@ -191,6 +191,60 @@ def test_launcher_terminates_the_tui_when_gateway_start_fails(
assert terminated == [True]
def test_launcher_promotes_the_gateway_when_the_tui_detaches(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
config = Config()
events: list[str] = []
captured: dict[str, str] = {}
class FakeLease:
def mark_persistent(self) -> bool:
events.append("promoted")
return True
def release(self, *, wait_for_stop: bool = True) -> None:
assert wait_for_stop is False
events.append("released")
class FakeProcess:
def wait(self) -> int:
events.append("waited")
return tui_launcher._TUI_DETACH_EXIT_CODE
monkeypatch.setattr("nanobot.cli.tui_launcher._resolve_tui_command", lambda: ["nanobot-tui"])
def popen(command: list[str], *, env: dict[str, str]) -> FakeProcess:
assert command == ["nanobot-tui"]
captured.update(env)
return FakeProcess()
monkeypatch.setattr("nanobot.cli.tui_launcher.subprocess.Popen", popen)
monkeypatch.setattr(
"nanobot.cli.tui_launcher._ensure_gateway",
lambda *args, **kwargs: SimpleNamespace(
base_url="http://127.0.0.1:8765",
lease=FakeLease(),
),
)
config_path = tmp_path / "custom config" / "config.json"
workspace = tmp_path / "custom workspace"
result = launch_tui(
config,
config_path=config_path,
workspace_override=str(workspace),
session_id=None,
theme="auto",
)
assert result == 0
assert events == ["waited", "promoted", "released"]
assert captured["NANOBOT_TUI_GATEWAY_STOP_COMMAND"] == (
f"nanobot gateway stop --config '{config_path}' --workspace '{workspace.resolve()}'"
)
def test_explicit_tui_binary_must_exist(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,