Files
ajarbot/ajarbot.py

206 lines
6.9 KiB
Python
Raw Permalink Normal View History

"""
Unified launcher for ajarbot with pre-flight checks.
This launcher:
1. Performs environment checks (Node.js, Claude CLI auth)
2. Sets sensible defaults (agent-sdk mode)
3. Delegates to bot_runner.main() for actual execution
Usage:
python ajarbot.py # Run with default config
python ajarbot.py --config custom.yaml # Use custom config file
python ajarbot.py --init # Generate config template
python ajarbot.py --setup-google # Set up Google OAuth
python ajarbot.py --health # Run health check
Environment variables:
AJARBOT_LLM_MODE # LLM mode: "agent-sdk" or "api" (default: agent-sdk)
AJARBOT_SLACK_BOT_TOKEN # Slack bot token (xoxb-...)
AJARBOT_SLACK_APP_TOKEN # Slack app token (xapp-...)
AJARBOT_TELEGRAM_BOT_TOKEN # Telegram bot token
ANTHROPIC_API_KEY # Claude API key (only needed for api mode)
"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
class PreflightCheck:
"""Performs environment checks before launching the bot."""
def __init__(self):
self.warnings = []
self.errors = []
def check_nodejs(self) -> bool:
"""Check if Node.js is available (required for agent-sdk mode)."""
try:
result = subprocess.run(
["node", "--version"],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
version = result.stdout.strip()
print(f"✓ Node.js found: {version}")
return True
else:
self.warnings.append("Node.js not found (required for agent-sdk mode)")
return False
except FileNotFoundError:
self.warnings.append("Node.js not found (required for agent-sdk mode)")
return False
except Exception as e:
self.warnings.append(f"Error checking Node.js: {e}")
return False
def check_claude_cli_auth(self) -> bool:
"""Check if Claude CLI is authenticated."""
try:
result = subprocess.run(
["claude", "auth", "status"],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0 and "Authenticated" in result.stdout:
print("✓ Claude CLI authenticated")
return True
else:
self.warnings.append("Claude CLI not authenticated (run: claude auth login)")
return False
except FileNotFoundError:
self.warnings.append("Claude CLI not found (install from: https://claude.ai/download)")
return False
except Exception as e:
self.warnings.append(f"Error checking Claude CLI: {e}")
return False
def check_python_version(self) -> bool:
"""Check if Python version is compatible."""
version_info = sys.version_info
if version_info >= (3, 10):
print(f"✓ Python {version_info.major}.{version_info.minor}.{version_info.micro}")
return True
else:
self.errors.append(
f"Python 3.10+ required (found {version_info.major}.{version_info.minor}.{version_info.micro})"
)
return False
def check_env_file(self) -> bool:
"""Check if .env file exists (for API key storage)."""
env_path = Path(".env")
if env_path.exists():
print(f"✓ .env file found")
return True
else:
self.warnings.append(".env file not found (create one if using API mode)")
return False
def check_config_file(self) -> bool:
"""Check if adapter config exists."""
config_path = Path("config/adapters.local.yaml")
if config_path.exists():
print(f"✓ Config file found: {config_path}")
return True
else:
self.warnings.append(
"config/adapters.local.yaml not found (run: python ajarbot.py --init)"
)
return False
def set_default_llm_mode(self):
"""Set default LLM mode to agent-sdk if not specified."""
if "AJARBOT_LLM_MODE" not in os.environ:
os.environ["AJARBOT_LLM_MODE"] = "agent-sdk"
print(" Using LLM mode: agent-sdk (default)")
else:
mode = os.environ["AJARBOT_LLM_MODE"]
print(f" Using LLM mode: {mode} (from environment)")
def run_all_checks(self) -> bool:
"""Run all pre-flight checks. Returns True if safe to proceed."""
print("=" * 60)
print("Ajarbot Pre-Flight Checks")
print("=" * 60)
print()
# Critical checks
self.check_python_version()
# LLM mode dependent checks
llm_mode = os.environ.get("AJARBOT_LLM_MODE", "agent-sdk")
if llm_mode == "agent-sdk":
print("\n[Agent SDK Mode Checks]")
self.check_nodejs()
self.check_claude_cli_auth()
elif llm_mode == "api":
print("\n[API Mode Checks]")
has_env = self.check_env_file()
if has_env:
if not os.environ.get("ANTHROPIC_API_KEY"):
self.errors.append("ANTHROPIC_API_KEY not set in .env file (required for API mode)")
else:
self.errors.append(".env file with ANTHROPIC_API_KEY required for API mode")
# Common checks
print("\n[Configuration Checks]")
self.check_config_file()
# Display results
print()
print("=" * 60)
if self.errors:
print("ERRORS (must fix before running):")
for error in self.errors:
print(f"{error}")
print()
return False
if self.warnings:
print("WARNINGS (optional, but recommended):")
for warning in self.warnings:
print(f"{warning}")
print()
print("Pre-flight checks complete!")
print("=" * 60)
print()
return True
def main():
"""Main entry point with pre-flight checks."""
# Set default LLM mode before checks
checker = PreflightCheck()
checker.set_default_llm_mode()
# Special commands that bypass pre-flight checks
bypass_commands = ["--init", "--help", "-h"]
if any(arg in sys.argv for arg in bypass_commands):
# Import and run bot_runner directly
from bot_runner import main as bot_main
bot_main()
return
# Run pre-flight checks for normal operation
if not checker.run_all_checks():
print("\nPre-flight checks failed. Please fix the errors above.")
sys.exit(1)
# All checks passed - delegate to bot_runner
print("Launching ajarbot...\n")
from bot_runner import main as bot_main
bot_main()
if __name__ == "__main__":
main()