refactor: optimize onboard wizard - mask secrets, remove emoji, reduce repetition
- Mask sensitive fields (api_key/token/secret/password) in all display surfaces, showing only the last 4 characters - Replace all emoji with pure ASCII labels for consistent cross-platform terminal rendering - Extract _print_summary_panel helper, eliminating 5x duplicate table construction in _show_summary - Replace 3 one-line wrapper functions with declarative _SETTINGS_SECTIONS dispatch tables and _MENU_DISPATCH in run_onboard - Extract _handle_model_field / _handle_context_window_field into a _FIELD_HANDLERS registry, shrinking _configure_pydantic_model - Return FieldTypeInfo NamedTuple from _get_field_type_info for clarity - Replace global mutable _PROVIDER_INFO / _CHANNEL_INFO with @lru_cache - Use vars() instead of dir() in _get_channel_info for reliable config class discovery - Defer litellm import in model_info.py so non-wizard CLI paths stay fast - Clarify README Quick Start wording (Add -> Configure)
This commit is contained in:
@@ -61,7 +61,7 @@ def test_onboard_fresh_install(mock_paths):
|
||||
"""No existing config — should create from scratch."""
|
||||
config_file, workspace_dir, mock_ws = mock_paths
|
||||
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"])
|
||||
result = runner.invoke(app, ["onboard"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Created config" in result.stdout
|
||||
@@ -79,7 +79,7 @@ def test_onboard_existing_config_refresh(mock_paths):
|
||||
config_file, workspace_dir, _ = mock_paths
|
||||
config_file.write_text('{"existing": true}')
|
||||
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"], input="n\n")
|
||||
result = runner.invoke(app, ["onboard"], input="n\n")
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Config already exists" in result.stdout
|
||||
@@ -93,7 +93,7 @@ def test_onboard_existing_config_overwrite(mock_paths):
|
||||
config_file, workspace_dir, _ = mock_paths
|
||||
config_file.write_text('{"existing": true}')
|
||||
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"], input="y\n")
|
||||
result = runner.invoke(app, ["onboard"], input="y\n")
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Config already exists" in result.stdout
|
||||
@@ -107,7 +107,7 @@ def test_onboard_existing_workspace_safe_create(mock_paths):
|
||||
workspace_dir.mkdir(parents=True)
|
||||
config_file.write_text("{}")
|
||||
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"], input="n\n")
|
||||
result = runner.invoke(app, ["onboard"], input="n\n")
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Created workspace" not in result.stdout
|
||||
@@ -130,6 +130,7 @@ def test_onboard_help_shows_workspace_and_config_options():
|
||||
assert "-w" in stripped_output
|
||||
assert "--config" in stripped_output
|
||||
assert "-c" in stripped_output
|
||||
assert "--wizard" in stripped_output
|
||||
assert "--dir" not in stripped_output
|
||||
|
||||
|
||||
@@ -143,7 +144,7 @@ def test_onboard_interactive_discard_does_not_save_or_create_workspace(mock_path
|
||||
lambda initial_config: OnboardResult(config=initial_config, should_save=False),
|
||||
)
|
||||
|
||||
result = runner.invoke(app, ["onboard"])
|
||||
result = runner.invoke(app, ["onboard", "--wizard"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "No changes were saved" in result.stdout
|
||||
@@ -159,7 +160,7 @@ def test_onboard_uses_explicit_config_and_workspace_paths(tmp_path, monkeypatch)
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
["onboard", "--config", str(config_path), "--workspace", str(workspace_path), "--no-interactive"],
|
||||
["onboard", "--config", str(config_path), "--workspace", str(workspace_path)],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
@@ -173,6 +174,31 @@ def test_onboard_uses_explicit_config_and_workspace_paths(tmp_path, monkeypatch)
|
||||
assert f"--config {resolved_config}" in compact_output
|
||||
|
||||
|
||||
def test_onboard_wizard_preserves_explicit_config_in_next_steps(tmp_path, monkeypatch):
|
||||
config_path = tmp_path / "instance" / "config.json"
|
||||
workspace_path = tmp_path / "workspace"
|
||||
|
||||
from nanobot.cli.onboard_wizard import OnboardResult
|
||||
|
||||
monkeypatch.setattr(
|
||||
"nanobot.cli.onboard_wizard.run_onboard",
|
||||
lambda initial_config: OnboardResult(config=initial_config, should_save=True),
|
||||
)
|
||||
monkeypatch.setattr("nanobot.channels.registry.discover_all", lambda: {})
|
||||
|
||||
result = runner.invoke(
|
||||
app,
|
||||
["onboard", "--wizard", "--config", str(config_path), "--workspace", str(workspace_path)],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
stripped_output = _strip_ansi(result.stdout)
|
||||
compact_output = stripped_output.replace("\n", "")
|
||||
resolved_config = str(config_path.resolve())
|
||||
assert f'nanobot agent -m "Hello!" --config {resolved_config}' in compact_output
|
||||
assert f"nanobot gateway --config {resolved_config}" in compact_output
|
||||
|
||||
|
||||
def test_config_matches_github_copilot_codex_with_hyphen_prefix():
|
||||
config = Config()
|
||||
config.agents.defaults.model = "github-copilot/gpt-5.3-codex"
|
||||
|
||||
@@ -75,7 +75,7 @@ def test_onboard_refresh_rewrites_legacy_config_template(tmp_path, monkeypatch)
|
||||
from typer.testing import CliRunner
|
||||
from nanobot.cli.commands import app
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"], input="n\n")
|
||||
result = runner.invoke(app, ["onboard"], input="n\n")
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "contextWindowTokens" in result.stdout
|
||||
@@ -127,7 +127,7 @@ def test_onboard_refresh_backfills_missing_channel_fields(tmp_path, monkeypatch)
|
||||
from typer.testing import CliRunner
|
||||
from nanobot.cli.commands import app
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(app, ["onboard", "--no-interactive"], input="n\n")
|
||||
result = runner.invoke(app, ["onboard"], input="n\n")
|
||||
|
||||
assert result.exit_code == 0
|
||||
saved = json.loads(config_path.read_text(encoding="utf-8"))
|
||||
|
||||
@@ -401,7 +401,7 @@ class TestConfigurePydanticModelDrafts:
|
||||
if token == "first":
|
||||
return choices[0]
|
||||
if token == "done":
|
||||
return "✓ Done"
|
||||
return "[Done]"
|
||||
if token == "back":
|
||||
return _BACK_PRESSED
|
||||
return token
|
||||
@@ -461,9 +461,9 @@ class TestRunOnboardExitBehavior:
|
||||
|
||||
responses = iter(
|
||||
[
|
||||
"🤖 Configure Agent Settings",
|
||||
"[A] Agent Settings",
|
||||
KeyboardInterrupt(),
|
||||
"🗑️ Exit Without Saving",
|
||||
"[X] Exit Without Saving",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -479,12 +479,13 @@ class TestRunOnboardExitBehavior:
|
||||
def fake_select(*_args, **_kwargs):
|
||||
return FakePrompt(next(responses))
|
||||
|
||||
def fake_configure_agents(config):
|
||||
config.agents.defaults.model = "test/provider-model"
|
||||
def fake_configure_general_settings(config, section):
|
||||
if section == "Agent Settings":
|
||||
config.agents.defaults.model = "test/provider-model"
|
||||
|
||||
monkeypatch.setattr(onboard_wizard, "_show_main_menu_header", lambda: None)
|
||||
monkeypatch.setattr(onboard_wizard.questionary, "select", fake_select)
|
||||
monkeypatch.setattr(onboard_wizard, "_configure_agents", fake_configure_agents)
|
||||
monkeypatch.setattr(onboard_wizard, "questionary", SimpleNamespace(select=fake_select))
|
||||
monkeypatch.setattr(onboard_wizard, "_configure_general_settings", fake_configure_general_settings)
|
||||
|
||||
result = run_onboard(initial_config=initial_config)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user