Skip to content

Feature: Wire up bot onboarding (praisonai onboard) + gateway daemon install (launchd/systemd/Windows) + install.sh hook #1454

@MervinPraison

Description

@MervinPraison

Overview

Follow-up to #1451 (merged in #1453). The first issue wired praisonai setup for LLM-provider onboarding. This issue wires the messaging-bot onboarding + background daemon half of the funnel, so that a user who wants a 24/7 Telegram/Discord/Slack/WhatsApp bot gets to a running service in one command — same bar as the reference agent CLIs.

The shock finding: PraisonAI already has ~90 % of the code. We just never connected it.

Thing Exists Registered / Reachable
praisonai/cli/features/onboard.py — full wizard (platforms, tokens, probe, bot.yaml, daemon install) ✅ 254 lines not in cli/app.py
praisonai/daemon/{launchd,systemd}.py — install/uninstall/status/logs ✅ complete ❌ no CLI command calls it
praisonai bot {telegram,discord,slack,whatsapp,email,agentmail}
praisonai gateway {start,status,channels,send}
praisonai gateway install (daemon)
praisonai bot install-daemon
Windows daemon (Scheduled Task / Startup-folder)
install.sh detects messaging tokens → offers daemon
praisonai onboard CLI entry point

End-state user journey

curl -fsSL https://praison.ai/install.sh | bash
  │
  ├─ (from #1453) praisonai setup  →  picks LLM provider, writes ~/.praisonai/.env
  │
  └─ NEW: if --with-bots or a messaging token is detected in env / .env:
          praisonai onboard
             → pick platforms (Telegram / Discord / Slack / WhatsApp)
             → paste tokens (or Confirm reuse if already in env)
             → probe connection → ✓ @your_bot ( 120 ms )
             → write bot.yaml in cwd
             → "Install as background service? [Y/n]"
                    └─ yes → praisonai gateway install → running 24/7

Then at any time: praisonai onboard re-runs the wizard, praisonai gateway install/start/stop/status/logs/uninstall manages the service.


Background

The reference agent CLIs we studied locally (pattern inspiration only, no copying) ship one unified loop: install → LLM provider setup → messaging setup → daemon install → running. The glue script detects when a messaging token landed in .env and offers the daemon install right there. PraisonAI has stronger building blocks (a real Bot abstraction with .probe(), per-platform adapters, OS-level daemon modules) but no user-facing path through them.

Why install.sh needs to know

Because this is the moment the user is most engaged. By the time they praisonai onboard manually a day later, 70 % are gone. The install-to-first-message window is the single highest-leverage UX moment we have.


Architecture Analysis

Current state — key files

File Lines Purpose
src/praisonai/praisonai/cli/features/onboard.py 254 Full wizard already implemented: OnboardWizard.run() → platform selection, token prompts, .probe() connection test, bot.yaml generation, optional daemon install via praisonai.daemon.install_daemon. Has run_onboard() entry point.
src/praisonai/praisonai/daemon/__init__.py ~70 OS dispatcher: install_daemon(), get_daemon_status(), uninstall_daemon(). Routes to launchd/systemd.
src/praisonai/praisonai/daemon/launchd.py macOS LaunchAgent: _generate_plist, install, uninstall, get_status, get_logs. Writes ~/Library/LaunchAgents/*.plist.
src/praisonai/praisonai/daemon/systemd.py Linux user-unit: _generate_unit, install, uninstall, get_status, get_logs. Writes ~/.config/systemd/user/*.service.
src/praisonai/praisonai/cli/commands/bot.py bot {start,telegram,discord,slack,whatsapp,email,agentmail} — no daemon command.
src/praisonai/praisonai/cli/commands/gateway.py gateway {start,status,channels,send} — no install/uninstall/logs commands.
src/praisonai/praisonai/cli/app.py ~600 Does not register onboard. bot_app/gateway_app are registered but incomplete.
src/praisonai/scripts/install.sh ~700 Post-#1453: calls praisonai setup. No messaging / gateway hook.

Extension points we will NOT touch


Gap Analysis Summary

Critical Gaps

Gap Impact Effort
praisonai onboard not registered in cli/app.py The entire bot wizard is unreachable XS
praisonai gateway install/uninstall/status/logs CLI commands missing Users can't install the daemon without Python import tricks S
install.sh doesn't detect messaging tokens post-setup Biggest moment of user engagement is wasted S
No Windows daemon backend Windows users can't run 24/7 bots M

Feature Parity With Reference CLIs

Feature Reference CLI PraisonAI today After this PR
Platform selection wizard ⚠️ (code exists, not wired)
Token probe (verify before save) ⚠️ (code exists)
macOS LaunchAgent install ⚠️ (code exists, no CLI)
Linux systemd user install ⚠️ (code exists, no CLI)
Windows Scheduled Task install
install.sh triggers messaging wizard ✅ (opt-in)
Auto-install daemon post-wizard ⚠️ (prompted but unreachable)
gateway status / gateway logs Partial (status only)
WhatsApp QR pair flow Cloud API only (no local QR) ⚠️ (tracked separately)

Proposed Implementation

Phase 1 — Wire what exists (MVP, high value, tiny diff)

1. Register praisonai onboard in cli/app.py (new Typer sub-app, ~15 lines):

# src/praisonai/praisonai/cli/commands/onboard.py — NEW
import typer
from ..output.console import get_output_controller

app = typer.Typer(help="Messaging bot onboarding (platforms, tokens, daemon)")

@app.callback(invoke_without_command=True)
def onboard_callback(ctx: typer.Context):
    """Run the bot onboarding wizard."""
    if ctx.invoked_subcommand:
        return
    from ..features.onboard import run_onboard
    try:
        run_onboard()
    except KeyboardInterrupt:
        get_output_controller().print_info("Cancelled.")
        raise typer.Exit(130)

In cli/app.py, next to where setup_app is registered (post-#1453):

app.add_typer(onboard_app, name="onboard", help="Messaging bot onboarding wizard")

2. Extend praisonai gateway with daemon subcommands (~80 lines in cli/commands/gateway.py):

@app.command("install")
def gateway_install(
    config: str = typer.Option("bot.yaml", "--config", help="Path to bot.yaml"),
    start: bool = typer.Option(True, "--start/--no-start", help="Start after install"),
):
    """Install the gateway as an OS daemon (LaunchAgent / systemd)."""
    from praisonai.daemon import install_daemon
    res = install_daemon(config_path=config)
    # print ok / error, exit code

@app.command("uninstall")
def gateway_uninstall(): ...

@app.command("logs")
def gateway_logs(lines: int = typer.Option(50, "-n")): ...

# gateway_status already exists — enhance to also show daemon status

Add bot install-daemon as an alias that forwards to the same functions, for discoverability.

3. install.sh post-setup hook (~25 lines, runs only when wizard ran interactively and a messaging token landed in ~/.praisonai/.env):

maybe_offer_bot_onboarding() {
    # Only run after praisonai setup completed interactively
    [[ "$NO_ONBOARD" == "1" ]] && return 0
    [[ "$NO_PROMPT" == "1" ]] && return 0
    [ -e /dev/tty ] || return 0

    local env_file="$HOME/.praisonai/.env"
    [[ -f "$env_file" ]] || return 0

    # Only if any messaging token already present (user set up bots elsewhere)
    # OR if user opts in interactively
    local has_token=0
    for tok in TELEGRAM_BOT_TOKEN DISCORD_BOT_TOKEN SLACK_BOT_TOKEN WHATSAPP_ACCESS_TOKEN; do
        grep -qE "^${tok}=..+" "$env_file" 2>/dev/null && has_token=1
    done

    local py="$venv_dir/bin/python"; [[ -x "$py" ]] || py="python3"

    if [[ "$has_token" == "1" ]]; then
        log_info "Detected a messaging token — launching bot onboarding..."
        "$py" -m praisonai onboard < /dev/tty || log_warn "Bot onboarding skipped."
    else
        # Offer it; default NO so quiet installs stay quiet
        read -p "$(echo -e "${CYAN}Set up a messaging bot now? [y/N] ${NC}")" yn < /dev/tty || yn=""
        case "$yn" in [yY]*) "$py" -m praisonai onboard < /dev/tty || true ;; esac
    fi
}

Called from main() right after run_onboarding, guarded by the same NO_ONBOARD flag.

4. Daemon post-install nudge — after praisonai onboard finishes and the user selected "yes" to daemon install, print:

✓ Service installed and started.
  Status: praisonai gateway status
  Logs:   praisonai gateway logs
  Stop:   launchctl unload ...plist (macOS) / systemctl --user stop praisonai-gateway (Linux)

The existing onboard.py already has this success panel — we just need to enrich the daemon-install branch.

Phase 2 — Windows daemon backend (~150 lines)

New src/praisonai/praisonai/daemon/windows.py:

  • Primary: schtasks /Create /SC ONLOGON /TN PraisonAIGateway /TR "..." for per-user on-login start.
  • Fallback (if the user's account lacks the right): drop a shortcut/.cmd in %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ so it runs on next login.
  • install / uninstall / get_status / get_logs implemented symmetrically to launchd.py.
  • Wire into daemon/__init__.py's _detect_platform().

Defer WhatsApp QR local-client (Baileys bridge) to a separate issue — it's its own stack.


Files to Create / Modify

New files

File Purpose
src/praisonai/praisonai/cli/commands/onboard.py Typer app that calls existing features/onboard.py::run_onboard()
src/praisonai/praisonai/daemon/windows.py Windows Scheduled Task + Startup-folder fallback
src/praisonai/tests/unit/cli/test_onboard_command.py Registration + callback test; mock wizard
src/praisonai/tests/unit/daemon/test_daemon_dispatch.py _detect_platform returns correct backend on macOS / Linux / Windows
src/praisonai/tests/unit/cli/test_gateway_install.py gateway install / uninstall / logs call daemon functions with correct args
src/praisonai/tests/integration/test_install_sh_bot_onboarding.py install.sh with PRAISONAI_NO_ONBOARD=0 and a seeded .env token triggers praisonai onboard (mocked subprocess)

Modified files

File Change
src/praisonai/praisonai/cli/app.py Register onboard_app; optionally add app.add_typer(onboard_app, ...) next to the setup_app line added in #1453
src/praisonai/praisonai/cli/commands/__init__.py Export onboard
src/praisonai/praisonai/cli/commands/gateway.py Add install, uninstall, logs subcommands; enrich status to also report daemon state
src/praisonai/praisonai/cli/commands/bot.py Add install-daemon as thin alias → daemon.install_daemon
src/praisonai/praisonai/daemon/__init__.py Add windows branch in _detect_platform + dispatcher
src/praisonai/praisonai/cli/features/onboard.py Return a summary dict instead of only printing (so the CLI can format); keep current print path as fallback
src/praisonai/scripts/install.sh Add maybe_offer_bot_onboarding after run_onboarding; respect PRAISONAI_NO_ONBOARD
src/praisonai/scripts/install.ps1 Mirror: offer praisonai onboard after setup

Zero changes to praisonaiagents core SDK. Zero breaking changes.


Technical Considerations

Dependencies

  • No new required deps. features/onboard.py already does lazy rich import with a plain fallback.
  • Windows backend uses only stdlib subprocess + shutil (no pywin32).

Performance impact

  • All new commands lazy-loaded via the existing cli/commands/ Typer registration (no work until invoked). Cold-import unchanged (target < 200 ms per AGENTS.md §4.2).

Safety / approval

Multi-agent safety

  • Gateway is a single long-running process per user; not concurrency-sensitive. The existing Bot abstraction already handles channel multiplexing.

Backward compatibility

  • All existing praisonai bot / praisonai gateway commands unchanged.
  • praisonai onboard is new — no collision.
  • New gateway install/uninstall/logs are additive.

Acceptance Criteria

  • praisonai onboard appears in praisonai --help and launches OnboardWizard.run().
  • Invoking praisonai onboard with existing TELEGRAM_BOT_TOKEN in env does NOT re-prompt and successfully probes the bot.
  • praisonai gateway install creates the correct LaunchAgent .plist on macOS and loads it; exit 0.
  • praisonai gateway install creates the correct systemd user unit on Linux and enables/starts it; exit 0.
  • praisonai gateway uninstall cleanly removes the service.
  • praisonai gateway status reports daemon installed/running + pid.
  • praisonai gateway logs prints the last 50 lines (configurable via -n).
  • On Windows, praisonai gateway install creates a per-user Scheduled Task; if that fails due to ACL, falls back to Startup-folder shortcut and reports which path was used.
  • install.sh (interactive TTY, NO_ONBOARD=0) offers messaging onboarding after praisonai setup completes.
  • install.sh --no-onboard and PRAISONAI_NO_ONBOARD=1 still skip everything (from feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453).
  • Unit tests: test_onboard_command.py, test_daemon_dispatch.py, test_gateway_install.py all pass.
  • Integration test: install.sh in a tmp dir with a seeded ~/.praisonai/.env containing TELEGRAM_BOT_TOKEN=fake triggers praisonai onboard call (mocked via -x trace).
  • Smoke: python -c "from praisonai.cli.commands.onboard import app; print('OK')".
  • Smoke: python -c "from praisonai.daemon import install_daemon, get_daemon_status, uninstall_daemon; print('OK')".
  • Import-time regression: time python -c "import praisonai" stays within ±2 % of main.
  • No .md files added beyond what's already present (per project rule).
  • No changes to praisonaiagents core SDK.

Implementation Notes

Key files to read first

  1. src/praisonai/praisonai/cli/features/onboard.py (254 lines) — the wizard already exists, we are wiring it up, not rewriting it.
  2. src/praisonai/praisonai/daemon/__init__.py, daemon/launchd.py, daemon/systemd.py — the existing daemon backends and dispatch pattern.
  3. src/praisonai/praisonai/cli/commands/gateway.py — the pattern to extend.
  4. src/praisonai/praisonai/cli/commands/setup.py (just merged in feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453) — the Typer pattern that onboard.py should mirror.
  5. src/praisonai/scripts/install.sh — the run_onboarding() function added in feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453 is the template for maybe_offer_bot_onboarding.

Critical integration points

  1. features/onboard.py::run_onboard() already does everything. Don't fork it; just expose it via Typer.
  2. Daemon dispatch: reuse _detect_platform, don't create a second one.
  3. install.sh prompt defaults to NO for messaging setup (AGENTS.md §4.6 safe defaults).
  4. Windows ScheduledTask: use schtasks (always present); fall back to Startup folder only on exit code != 0.
  5. Config file path: the wizard writes bot.yaml in cwd; install.sh should cd "$HOME" before launching the wizard if cwd is the installer tmp dir, so the user can find it after.

Testing commands

# Unit
timeout 60 python -m pytest src/praisonai/tests/unit/cli/test_onboard_command.py src/praisonai/tests/unit/cli/test_gateway_install.py src/praisonai/tests/unit/daemon/test_daemon_dispatch.py -v --tb=short

# Smoke
timeout 10 python -c "from praisonai.cli.commands.onboard import app; from praisonai.daemon import install_daemon, get_daemon_status; print('OK')"
timeout 10 praisonai onboard --help
timeout 10 praisonai gateway install --help
timeout 10 praisonai gateway logs --help

# install.sh dry-run — should mention bot onboarding prompt
bash src/praisonai/scripts/install.sh --dry-run

# Real agentic test (requires a fake Telegram token and mocked Bot.probe)
TELEGRAM_BOT_TOKEN=fake:test timeout 30 praisonai onboard --platforms telegram --agent-name test --no-probe --no-daemon
test -f ./bot.yaml && echo OK

# Daemon install on macOS (local)
timeout 15 praisonai gateway install --config /tmp/fake-bot.yaml --no-start
test -f ~/Library/LaunchAgents/*praisonai* && echo OK
timeout 10 praisonai gateway uninstall

# Cold-import regression (AGENTS.md §4.2)
timeout 10 python -c "import time; t=time.time(); import praisonai; print(f'{(time.time()-t)*1000:.1f} ms')"

Design Principles Preserved (AGENTS.md)

  • §4.1 Protocol-driven core — everything lives in the wrapper (praisonai/cli/, praisonai/daemon/); praisonaiagents untouched.
  • §4.2 No performance impact — new CLI modules only loaded on invocation; daemon modules lazy-imported inside functions.
  • §4.3 DRY — reuses existing OnboardWizard, install_daemon, launchd.py, systemd.py, and the run_onboarding install.sh pattern from feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453.
  • §4.4 Agent-centric — goal is turning "agent + messaging token" into a running 24/7 bot in one command.
  • §4.5 Async-safe — wizard is single-shot; daemon runs one process per user.
  • §4.6 Invariants — additive only, safe defaults (NO to messaging install), backward compatible.
  • §4.9 Namingonboard_app, OnboardHandler mirror setup_app/doctor_app.

References


@claude please implement Phase 1 end-to-end first (wiring + install.sh hook + gateway daemon subcommands), then Phase 2 (Windows backend) if the diff stays under ~500 lines total. Follow AGENTS.md strictly: minimal code change, protocol-driven, no performance impact, lazy imports, backward-compatible, safe defaults. Write TDD tests first, run the verification commands locally with timeout before pushing, and keep changes confined to the files listed in the "Files to Create / Modify" table — no scope creep, no rewriting of the existing OnboardWizard or daemon backends.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingclaudeAuto-trigger Claude analysisperformance

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions