diff --git a/README.md b/README.md index 06aee9fe..c1d14c9a 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,15 @@ The WebUI ships **inside the published wheel** — no extra build step. It is th Merge this block into your existing config: ```json -{ "channels": { "websocket": { "enabled": true } } } +{ + "channels": { + "websocket": { + "enabled": true, + "tokenIssueSecret": "your-webui-password", + "websocketRequiresToken": true + } + } +} ``` **2. Start the gateway** diff --git a/docs/quick-start.md b/docs/quick-start.md index f1343ddf..51e2ea20 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -280,7 +280,7 @@ Tell me exactly what changed and whether I need to run /restart. Exit interactive mode with `exit`, `quit`, `/exit`, `/quit`, `:q`, or `Ctrl+D`. -## 6. Choose Your Next Step +## 7. Choose Your Next Step | Want to... | Go to | |---|---| diff --git a/docs/start-without-technical-background.md b/docs/start-without-technical-background.md index c839fa8f..2c38ca1f 100644 --- a/docs/start-without-technical-background.md +++ b/docs/start-without-technical-background.md @@ -225,7 +225,9 @@ Merge them into one object: }, "channels": { "websocket": { - "enabled": true + "enabled": true, + "tokenIssueSecret": "your-webui-password", + "websocketRequiresToken": true } } } @@ -286,13 +288,15 @@ If this is a brand-new install and you have not configured anything else yet, re }, "channels": { "websocket": { - "enabled": true + "enabled": true, + "tokenIssueSecret": "your-webui-password", + "websocketRequiresToken": true } } } ``` -Replace `your-api-key`, `https://api.example.com/v1`, and `model-id-from-your-provider` with values from your provider. +Replace `your-api-key`, `https://api.example.com/v1`, `model-id-from-your-provider`, and `your-webui-password` with your own values. For copyable provider-specific examples, use [`provider-cookbook.md`](./provider-cookbook.md). @@ -316,7 +320,7 @@ Start the local browser UI: nanobot gateway ``` -Leave that terminal open, then open `http://127.0.0.1:8765` in your browser. If Quick Start enabled the WebSocket channel, enter the WebUI password you set in the wizard. +Leave that terminal open, then open `http://127.0.0.1:8765` in your browser. Enter the WebUI password you set in the wizard or the `tokenIssueSecret` value from your manual config. Send this first message in the browser: diff --git a/nanobot/cli/onboard.py b/nanobot/cli/onboard.py index 18d135f9..b21ecc7b 100644 --- a/nanobot/cli/onboard.py +++ b/nanobot/cli/onboard.py @@ -1700,14 +1700,17 @@ def _configure_quick_start(config: Config) -> bool: "Quick Start", "Choose provider endpoint, add credentials and model, then enable the local WebUI channel.", ) - if not _configure_quick_start_provider(config): + draft = config.model_copy(deep=True) + if not _configure_quick_start_provider(draft): _pause() return False - if not _enable_quick_start_websocket_defaults(config): + if not _enable_quick_start_websocket_defaults(draft): _pause() return False - _show_quick_start_summary(config) + _show_quick_start_summary(draft) _pause("Press Enter to save and exit...") + for field_name in type(config).model_fields: + setattr(config, field_name, getattr(draft, field_name)) return True diff --git a/tests/agent/test_onboard_logic.py b/tests/agent/test_onboard_logic.py index 7fb010de..92799bea 100644 --- a/tests/agent/test_onboard_logic.py +++ b/tests/agent/test_onboard_logic.py @@ -983,6 +983,41 @@ class TestMainMenuUpdate: assert websocket["websocketRequiresToken"] is True assert websocket["tokenIssueSecret"] == "webui-secret" + def test_quick_start_websocket_decline_rolls_back_provider_defaults(self, monkeypatch): + """A failed WebSocket step should not leave saveable Quick Start defaults behind.""" + config = Config() + original = config.model_dump(by_alias=True) + + class FakePrompt: + def __init__(self, response): + self.response = response + + def ask(self): + return self.response + + monkeypatch.setattr(onboard_wizard.console, "clear", lambda: None) + monkeypatch.setattr(onboard_wizard.console, "print", lambda *a, **kw: None) + monkeypatch.setattr(onboard_wizard, "_show_section_header", lambda *a, **kw: None) + monkeypatch.setattr(onboard_wizard, "_show_quick_start_progress", lambda *a, **kw: None) + monkeypatch.setattr(onboard_wizard, "_select_with_back", lambda *a, **kw: "DeepSeek") + monkeypatch.setattr(onboard_wizard, "_input_text", lambda *a, **kw: "sk-ds-test") + monkeypatch.setattr( + onboard_wizard, + "_input_model_with_autocomplete", + lambda *a, **kw: "deepseek-v4-flash", + ) + monkeypatch.setattr( + onboard_wizard, + "questionary", + SimpleNamespace(confirm=lambda *a, **kw: FakePrompt(False)), + ) + monkeypatch.setattr(onboard_wizard, "_pause", lambda message="": None) + + assert onboard_wizard._configure_quick_start(config) is False + + assert config.model_dump(by_alias=True) == original + assert getattr(config.channels, "websocket", None) is None + def test_quick_start_provider_choice_asks_for_model_id(self, monkeypatch): """Known providers should ask users for the model instead of fetching one.""" config = Config()