fix: make quick start rollback on websocket failure
maintainer edit: stage Quick Start edits on a draft config so declining WebSocket/password setup cannot leave saveable provider defaults behind. Align beginner WebUI docs with the password-protected setup.
This commit is contained in:
@@ -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:
|
Merge this block into your existing config:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "channels": { "websocket": { "enabled": true } } }
|
{
|
||||||
|
"channels": {
|
||||||
|
"websocket": {
|
||||||
|
"enabled": true,
|
||||||
|
"tokenIssueSecret": "your-webui-password",
|
||||||
|
"websocketRequiresToken": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Start the gateway**
|
**2. Start the gateway**
|
||||||
|
|||||||
+1
-1
@@ -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`.
|
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 |
|
| Want to... | Go to |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
@@ -225,7 +225,9 @@ Merge them into one object:
|
|||||||
},
|
},
|
||||||
"channels": {
|
"channels": {
|
||||||
"websocket": {
|
"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": {
|
"channels": {
|
||||||
"websocket": {
|
"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).
|
For copyable provider-specific examples, use [`provider-cookbook.md`](./provider-cookbook.md).
|
||||||
|
|
||||||
@@ -316,7 +320,7 @@ Start the local browser UI:
|
|||||||
nanobot gateway
|
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:
|
Send this first message in the browser:
|
||||||
|
|
||||||
|
|||||||
@@ -1700,14 +1700,17 @@ def _configure_quick_start(config: Config) -> bool:
|
|||||||
"Quick Start",
|
"Quick Start",
|
||||||
"Choose provider endpoint, add credentials and model, then enable the local WebUI channel.",
|
"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()
|
_pause()
|
||||||
return False
|
return False
|
||||||
if not _enable_quick_start_websocket_defaults(config):
|
if not _enable_quick_start_websocket_defaults(draft):
|
||||||
_pause()
|
_pause()
|
||||||
return False
|
return False
|
||||||
_show_quick_start_summary(config)
|
_show_quick_start_summary(draft)
|
||||||
_pause("Press Enter to save and exit...")
|
_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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -983,6 +983,41 @@ class TestMainMenuUpdate:
|
|||||||
assert websocket["websocketRequiresToken"] is True
|
assert websocket["websocketRequiresToken"] is True
|
||||||
assert websocket["tokenIssueSecret"] == "webui-secret"
|
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):
|
def test_quick_start_provider_choice_asks_for_model_id(self, monkeypatch):
|
||||||
"""Known providers should ask users for the model instead of fetching one."""
|
"""Known providers should ask users for the model instead of fetching one."""
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|||||||
Reference in New Issue
Block a user