feat(webui): add guided setup flows

* feat(channels): add guided setup flows

* test(channels): preserve setup config values

* fix(channels): reflect saved setup state

* refactor(channels): simplify setup state metadata

* fix(channels): harden setup lifecycle

* refactor(channels): centralize setup contracts

* fix(channels): route setup actions through webui shim

* fix(channels): adapt settings for compact screens

* fix(models): preserve default preset display

* feat(models): add curated Codex catalog

* fix(webui): stop attached gateway on interrupt

* fix(webui): simplify apps catalog

* docs(webui): clarify apps and runtime features

* feat(settings): add guided capability setup

* fix(webui): harden setup and managed services

* test: keep managed runtime checks portable

* test: scope POSIX runtime coverage

* fix(webui): simplify file settings

* feat(files): bundle document reading

* fix(webui): harden setup request boundaries

* fix(webui): prevent channel setup status squeeze

* fix(settings): group provider compatibility aliases

* refactor(settings): remove redundant setup surfaces

* fix(webui): harden guided setup lifecycle

* fix(webui): preserve channel setup compatibility
This commit is contained in:
Xubin Ren
2026-07-13 13:11:46 +08:00
committed by GitHub
parent 791c7fd505
commit fe0717b385
92 changed files with 15058 additions and 1311 deletions
+24
View File
@@ -42,6 +42,15 @@ class TestGenerateCode:
assert store.approve_code(code2) is None
class TestFormatPairingReply:
def test_points_owner_to_webui_with_command_fallback(self) -> None:
reply = store.format_pairing_reply("ABCD-EFGH")
assert "nanobot WebUI" in reply
assert "ABCD-EFGH" in reply
assert "/pairing approve ABCD-EFGH" in reply
class TestApproveDeny:
def test_approve_moves_to_approved(self) -> None:
code = store.generate_code("telegram", "123")
@@ -82,6 +91,21 @@ class TestRevoke:
def test_revoke_unknown_returns_false(self) -> None:
assert store.revoke("telegram", "999") is False
def test_clear_channel_removes_approved_and_pending(self) -> None:
code = store.generate_code("telegram", "123")
store.approve_code(code)
store.generate_code("telegram", "456")
store.generate_code("discord", "789")
assert store.clear_channel("telegram") == {"approved": 1, "pending": 1}
assert store.is_approved("telegram", "123") is False
pending = store.list_pending()
assert [item["channel"] for item in pending] == ["discord"]
def test_clear_channel_unknown_returns_zero_counts(self) -> None:
assert store.clear_channel("telegram") == {"approved": 0, "pending": 0}
class TestListPending:
def test_empty(self) -> None: