refactor(onboarding): simplify setup flow

This commit is contained in:
banteg
2025-12-29 19:19:01 +04:00
parent db4429af4d
commit d151788edb
3 changed files with 47 additions and 110 deletions
+1 -13
View File
@@ -21,7 +21,7 @@ from .config import ConfigError, load_telegram_config
from .exec_render import ExecProgressRenderer, render_event_cli
from .logging import setup_logging
from .rendering import render_markdown
from .onboarding import check_setup, demo_results, render_setup_guide
from .onboarding import check_setup, render_setup_guide
from .telegram_client import TelegramClient
logger = logging.getLogger(__name__)
@@ -678,20 +678,8 @@ def run(
"--profile",
help="Codex profile name to pass to `codex --profile`.",
),
setup_demo: bool = typer.Option(
False,
"--setup-demo",
help="Render all onboarding guide variants and exit.",
),
) -> None:
setup_logging(debug=debug)
if setup_demo:
for idx, (label, result) in enumerate(demo_results()):
if idx:
typer.echo("", err=True)
typer.echo(f"[setup demo] {label}", err=True)
render_setup_guide(result)
raise typer.Exit(code=0)
setup = check_setup()
if not setup.ok:
render_setup_guide(setup)
+46 -96
View File
@@ -15,13 +15,13 @@ from .constants import HOME_CONFIG_PATH
_OCTOPUS = "\N{OCTOPUS}"
@dataclass
@dataclass(slots=True)
class SetupResult:
"""Collected setup issues."""
missing_codex: bool = False
missing_or_invalid_config: bool = False
config_path: Path | None = None
config_path: Path = HOME_CONFIG_PATH
@property
def ok(self) -> bool:
@@ -30,32 +30,28 @@ class SetupResult:
def check_setup() -> SetupResult:
"""Check all prerequisites and return collected issues."""
result = SetupResult()
if not shutil.which("codex"):
result.missing_codex = True
missing_codex = shutil.which("codex") is None
try:
config, config_path = load_telegram_config()
result.config_path = config_path
except ConfigError:
result.missing_or_invalid_config = True
result.config_path = HOME_CONFIG_PATH
return result
return SetupResult(
missing_codex=missing_codex,
missing_or_invalid_config=True,
config_path=HOME_CONFIG_PATH,
)
token = config.get("bot_token")
if not isinstance(token, str) or not token.strip():
result.missing_or_invalid_config = True
chat_id = config.get("chat_id")
chat_id_value = config.get("chat_id")
if (
chat_id_value is None
or isinstance(chat_id_value, bool)
or not isinstance(chat_id_value, int)
):
result.missing_or_invalid_config = True
missing_or_invalid_config = not (isinstance(token, str) and token.strip())
missing_or_invalid_config |= type(chat_id) is not int
return result
return SetupResult(
missing_codex=missing_codex,
missing_or_invalid_config=missing_or_invalid_config,
config_path=config_path,
)
def _config_path_display(path: Path) -> str:
@@ -67,65 +63,48 @@ def _config_path_display(path: Path) -> str:
return str(path)
def _step_marker(step: int) -> str:
return f"{step}."
def render_setup_guide(result: SetupResult) -> None:
"""Render a friendly setup guide panel to stderr."""
if result.ok:
return
console = Console(stderr=True)
parts: list[str] = []
step = 0
needs_credentials_help = False
def add_step(title: str, *lines: str) -> None:
nonlocal step
step += 1
parts.append(f"[bold yellow]{step}.[/] [bold]{title}[/]")
parts.append("")
parts.extend(lines)
parts.append("")
if result.missing_codex:
step += 1
parts.append(
f"[bold yellow]{_step_marker(step)}[/] [bold]Install the Codex CLI[/]"
add_step(
"Install the Codex CLI",
" [dim]$[/] npm install -g @openai/codex",
)
parts.append("")
parts.append(" [dim]$[/] npm install -g @openai/codex")
parts.append("")
config_display = (
_config_path_display(result.config_path)
if result.config_path
else _config_path_display(HOME_CONFIG_PATH)
)
if result.missing_or_invalid_config:
step += 1
parts.append(f"[bold yellow]{_step_marker(step)}[/] [bold]Create a config[/]")
parts.append("")
parts.append(f" [dim]{config_display}[/]")
parts.append("")
parts.append(' [cyan]bot_token[/] = [green]"123456789:ABCdef..."[/]')
parts.append(" [cyan]chat_id[/] = [green]123456789[/]")
parts.append("")
needs_credentials_help = True
if needs_credentials_help:
needs_token_help = True
needs_chat_id_help = True
parts.append("[dim]" + ("-" * 56) + "[/]")
parts.append("")
parts.append("[bold]Getting your Telegram credentials:[/]")
parts.append("")
if needs_token_help:
parts.append(
" [cyan]bot_token[/] create a bot with [link=https://t.me/BotFather]@BotFather[/]"
)
if needs_chat_id_help:
parts.append(
" [cyan]chat_id[/] get from [link=https://t.me/myidbot]@myidbot[/]"
)
while parts and not parts[-1].strip():
parts.pop()
config_display = _config_path_display(result.config_path)
add_step(
"Create a config",
f" [dim]{config_display}[/]",
"",
' [cyan]bot_token[/] = [green]"123456789:ABCdef..."[/]',
" [cyan]chat_id[/] = [green]123456789[/]",
"",
"[dim]" + ("-" * 56) + "[/]",
"",
"[bold]Getting your Telegram credentials:[/]",
"",
" [cyan]bot_token[/] create a bot with [link=https://t.me/BotFather]@BotFather[/]",
" [cyan]chat_id[/] get from [link=https://t.me/myidbot]@myidbot[/]",
)
panel = Panel(
"\n".join(parts),
"\n".join(parts).rstrip(),
title="[bold]Welcome to Takopi![/]",
subtitle=f"{_OCTOPUS} setup required",
border_style="yellow",
@@ -133,32 +112,3 @@ def render_setup_guide(result: SetupResult) -> None:
expand=False,
)
console.print(panel)
def demo_results() -> list[tuple[str, SetupResult]]:
"""Return sample setup results for previewing all onboarding modes."""
config_path = HOME_CONFIG_PATH
return [
(
"fresh-install",
SetupResult(
missing_codex=True,
missing_or_invalid_config=True,
config_path=config_path,
),
),
(
"missing-codex",
SetupResult(
missing_codex=True,
config_path=config_path,
),
),
(
"missing-or-invalid-config",
SetupResult(
missing_or_invalid_config=True,
config_path=config_path,
),
),
]