You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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)
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.
2. Extend praisonai gateway with daemon subcommands (~80 lines in cli/commands/gateway.py):
@app.command("install")defgateway_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)."""frompraisonai.daemonimportinstall_daemonres=install_daemon(config_path=config)
# print ok / error, exit code@app.command("uninstall")defgateway_uninstall(): ...
@app.command("logs")defgateway_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 interactivelylocal has_token=0
fortokin TELEGRAM_BOT_TOKEN DISCORD_BOT_TOKEN SLACK_BOT_TOKEN WHATSAPP_ACCESS_TOKEN;do
grep -qE "^${tok}=..+""$env_file"2>/dev/null && has_token=1
donelocal 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 quietread -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 ;; esacfi
}
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.
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.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
Daemon install writes only under ~/Library/LaunchAgents/ (macOS) / ~/.config/systemd/user/ (Linux) / %APPDATA%\...\Startup\ (Windows). Never needs sudo. Never touches system-wide units.
install.sh messaging prompt defaults to NO (safe default, AGENTS.md §4.6).
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.
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).
features/onboard.py::run_onboard() already does everything. Don't fork it; just expose it via Typer.
Daemon dispatch: reuse _detect_platform, don't create a second one.
install.sh prompt defaults to NO for messaging setup (AGENTS.md §4.6 safe defaults).
Windows ScheduledTask: use schtasks (always present); fall back to Startup folder only on exit code != 0.
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.
Pattern inspiration studied locally only (no copying): two reference agent CLIs that ship install → provider setup → messaging setup → daemon install as a single loop.
@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.
Overview
Follow-up to #1451 (merged in #1453). The first issue wired
praisonai setupfor 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.
praisonai/cli/features/onboard.py— full wizard (platforms, tokens, probe, bot.yaml, daemon install)cli/app.pypraisonai/daemon/{launchd,systemd}.py— install/uninstall/status/logspraisonai bot {telegram,discord,slack,whatsapp,email,agentmail}praisonai gateway {start,status,channels,send}praisonai gateway install(daemon)praisonai bot install-daemoninstall.shdetects messaging tokens → offers daemonpraisonai onboardCLI entry pointEnd-state user journey
Then at any time:
praisonai onboardre-runs the wizard,praisonai gateway install/start/stop/status/logs/uninstallmanages 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
.envand offers the daemon install right there. PraisonAI has stronger building blocks (a realBotabstraction with.probe(), per-platform adapters, OS-level daemon modules) but no user-facing path through them.Why
install.shneeds to knowBecause this is the moment the user is most engaged. By the time they
praisonai onboardmanually 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
src/praisonai/praisonai/cli/features/onboard.pyOnboardWizard.run()→ platform selection, token prompts,.probe()connection test,bot.yamlgeneration, optional daemon install viapraisonai.daemon.install_daemon. Hasrun_onboard()entry point.src/praisonai/praisonai/daemon/__init__.pyinstall_daemon(),get_daemon_status(),uninstall_daemon(). Routes to launchd/systemd.src/praisonai/praisonai/daemon/launchd.py_generate_plist,install,uninstall,get_status,get_logs. Writes~/Library/LaunchAgents/*.plist.src/praisonai/praisonai/daemon/systemd.py_generate_unit,install,uninstall,get_status,get_logs. Writes~/.config/systemd/user/*.service.src/praisonai/praisonai/cli/commands/bot.pybot {start,telegram,discord,slack,whatsapp,email,agentmail}— no daemon command.src/praisonai/praisonai/cli/commands/gateway.pygateway {start,status,channels,send}— noinstall/uninstall/logscommands.src/praisonai/praisonai/cli/app.pyonboard.bot_app/gateway_appare registered but incomplete.src/praisonai/scripts/install.shpraisonai setup. No messaging / gateway hook.Extension points we will NOT touch
praisonaiagentscore SDK — zero changes.Botclass,.probe(), per-platform adapters — reused as-is._generate_plist/_generate_unit— reused as-is.praisonai setup(feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453) — untouched; the new glue lives alongside it.Gap Analysis Summary
Critical Gaps
praisonai onboardnot registered incli/app.pypraisonai gateway install/uninstall/status/logsCLI commands missinginstall.shdoesn't detect messaging tokens post-setupFeature Parity With Reference CLIs
install.shtriggers messaging wizardgateway status/gateway logsstatusonly)Proposed Implementation
Phase 1 — Wire what exists (MVP, high value, tiny diff)
1. Register
praisonai onboardincli/app.py(new Typer sub-app, ~15 lines):In
cli/app.py, next to wheresetup_appis registered (post-#1453):2. Extend
praisonai gatewaywith daemon subcommands (~80 lines incli/commands/gateway.py):Add
bot install-daemonas an alias that forwards to the same functions, for discoverability.3.
install.shpost-setup hook (~25 lines, runs only when wizard ran interactively and a messaging token landed in~/.praisonai/.env):Called from
main()right afterrun_onboarding, guarded by the sameNO_ONBOARDflag.4. Daemon post-install nudge — after
praisonai onboardfinishes and the user selected "yes" to daemon install, print:The existing
onboard.pyalready 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:schtasks /Create /SC ONLOGON /TN PraisonAIGateway /TR "..."for per-user on-login start.%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\so it runs on next login.install / uninstall / get_status / get_logsimplemented symmetrically to launchd.py.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
src/praisonai/praisonai/cli/commands/onboard.pyfeatures/onboard.py::run_onboard()src/praisonai/praisonai/daemon/windows.pysrc/praisonai/tests/unit/cli/test_onboard_command.pysrc/praisonai/tests/unit/daemon/test_daemon_dispatch.py_detect_platformreturns correct backend on macOS / Linux / Windowssrc/praisonai/tests/unit/cli/test_gateway_install.pygateway install/uninstall/logscall daemon functions with correct argssrc/praisonai/tests/integration/test_install_sh_bot_onboarding.py.envtoken triggerspraisonai onboard(mocked subprocess)Modified files
src/praisonai/praisonai/cli/app.pyonboard_app; optionally addapp.add_typer(onboard_app, ...)next to thesetup_appline added in #1453src/praisonai/praisonai/cli/commands/__init__.pyonboardsrc/praisonai/praisonai/cli/commands/gateway.pyinstall,uninstall,logssubcommands; enrichstatusto also report daemon statesrc/praisonai/praisonai/cli/commands/bot.pyinstall-daemonas thin alias →daemon.install_daemonsrc/praisonai/praisonai/daemon/__init__.pywindowsbranch in_detect_platform+ dispatchersrc/praisonai/praisonai/cli/features/onboard.pysrc/praisonai/scripts/install.shmaybe_offer_bot_onboardingafterrun_onboarding; respectPRAISONAI_NO_ONBOARDsrc/praisonai/scripts/install.ps1praisonai onboardafter setupZero changes to
praisonaiagentscore SDK. Zero breaking changes.Technical Considerations
Dependencies
features/onboard.pyalready does lazyrichimport with a plain fallback.subprocess+shutil(no pywin32).Performance impact
cli/commands/Typer registration (no work until invoked). Cold-import unchanged (target < 200 ms per AGENTS.md §4.2).Safety / approval
~/Library/LaunchAgents/(macOS) /~/.config/systemd/user/(Linux) /%APPDATA%\...\Startup\(Windows). Never needs sudo. Never touches system-wide units..envtoken written with existingchmod 600path from feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453.Multi-agent safety
Botabstraction already handles channel multiplexing.Backward compatibility
praisonai bot/praisonai gatewaycommands unchanged.praisonai onboardis new — no collision.gateway install/uninstall/logsare additive.Acceptance Criteria
praisonai onboardappears inpraisonai --helpand launchesOnboardWizard.run().praisonai onboardwith existingTELEGRAM_BOT_TOKENin env does NOT re-prompt and successfully probes the bot.praisonai gateway installcreates the correct LaunchAgent.pliston macOS and loads it; exit 0.praisonai gateway installcreates the correct systemd user unit on Linux and enables/starts it; exit 0.praisonai gateway uninstallcleanly removes the service.praisonai gateway statusreports daemon installed/running + pid.praisonai gateway logsprints the last 50 lines (configurable via-n).praisonai gateway installcreates 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 afterpraisonai setupcompletes.install.sh --no-onboardandPRAISONAI_NO_ONBOARD=1still skip everything (from feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453).test_onboard_command.py,test_daemon_dispatch.py,test_gateway_install.pyall pass.~/.praisonai/.envcontainingTELEGRAM_BOT_TOKEN=faketriggerspraisonai onboardcall (mocked via-xtrace).python -c "from praisonai.cli.commands.onboard import app; print('OK')".python -c "from praisonai.daemon import install_daemon, get_daemon_status, uninstall_daemon; print('OK')".time python -c "import praisonai"stays within ±2 % of main..mdfiles added beyond what's already present (per project rule).praisonaiagentscore SDK.Implementation Notes
Key files to read first
src/praisonai/praisonai/cli/features/onboard.py(254 lines) — the wizard already exists, we are wiring it up, not rewriting it.src/praisonai/praisonai/daemon/__init__.py,daemon/launchd.py,daemon/systemd.py— the existing daemon backends and dispatch pattern.src/praisonai/praisonai/cli/commands/gateway.py— the pattern to extend.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 thatonboard.pyshould mirror.src/praisonai/scripts/install.sh— therun_onboarding()function added in feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453 is the template formaybe_offer_bot_onboarding.Critical integration points
features/onboard.py::run_onboard()already does everything. Don't fork it; just expose it via Typer._detect_platform, don't create a second one.schtasks(always present); fall back to Startup folder only onexit code != 0.bot.yamlin cwd; install.sh shouldcd "$HOME"before launching the wizard if cwd is the installer tmp dir, so the user can find it after.Testing commands
Design Principles Preserved (AGENTS.md)
praisonai/cli/,praisonai/daemon/);praisonaiagentsuntouched.OnboardWizard,install_daemon,launchd.py,systemd.py, and therun_onboardinginstall.sh pattern from feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453.onboard_app,OnboardHandlermirrorsetup_app/doctor_app.References
src/praisonai/praisonai/cli/features/onboard.py,src/praisonai/praisonai/daemon/, PR feat: add frictionless onboarding with praisonai setup wizard and post-install hooks #1453, issue Feature: Frictionless onboarding — praisonai setup wizard + install.sh post-install hook #1451.install → provider setup → messaging setup → daemon installas a single loop.@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
timeoutbefore pushing, and keep changes confined to the files listed in the "Files to Create / Modify" table — no scope creep, no rewriting of the existingOnboardWizardor daemon backends.