FPLAN-0410 repo cleanup + integration test bug fixes

VERA cleanup (FPLAN-0410):
- Remove all /home/aipass hardcoded paths (portability)
- Remove internal Telegram bot code from public repo (6k+ lines)
- Remove runtime logs, .claude/settings.local.json, stale files
- Clean READMEs across all branches
- Restructure spawn handlers into proper 3-layer architecture
- Add __init__.py files for proper package imports
- Update seedgo standards for public repo context
- Add log_structure standard to seedgo

Integration test bug fixes:
- Fix double @@ display in drone introspection output
- Fix spawn registry resolving to wrong file (walk up from package root)
- Fix email sender identity always showing as @ai_mail (pass AIPASS_CALLER_CWD)
- Fix branch_detection.py relative path resolution against CWD instead of registry dir
- Add .trinity/passport.json detection for spawned agents
- Archive duplicate json_ops.py, update imports to canonical json_handler
- Update .gitignore for runtime logs and .claude/ dirs

Tested end-to-end in Docker container (aipass-test:latest):
- Spawn → register → send email → receive → reply all verified working

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AIOSAI
2026-03-06 15:29:16 -08:00
co-authored by Claude Opus 4.6
parent a59f9c006b
commit 3b45824dc6
186 changed files with 1350 additions and 9490 deletions
-415
View File
@@ -1,415 +0,0 @@
# ===================AIPASS====================
# META DATA HEADER
# Name: telegram_bot.py - Telegram multi-bot module (replaces telegram_bridge.py + telegram_chat.py)
# Date: 2026-02-24
# Version: 1.0.0
# Category: api/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-02-24): Initial - unified multi-bot public API module
#
# CODE STANDARDS:
# - Module layer: thin orchestration over handler functions
# - This is the PUBLIC API that other branches import from
# - Replaces telegram_bridge.py and telegram_chat.py
# =============================================
"""
Telegram Multi-Bot Module - Public API
Unified entry point for the AIPass multi-bot Telegram architecture.
Re-exports all handler internals so other branches can import them
without triggering the cross-branch handler guard.
Replaces:
- telegram_bridge.py (bridge operations)
- telegram_chat.py (direct chat + standards re-exports)
Usage from other branches:
from aipass.api.apps.modules.telegram_bot import BaseBot, BranchPlugin
from aipass.api.apps.modules.telegram_bot import create_bot, delete_bot
from aipass.api.apps.modules.telegram_bot import list_bots, get_bot
from aipass.api.apps.modules.telegram_bot import load_bot_config
from aipass.api.apps.modules.telegram_bot import (
STANDARD_COMMANDS, build_help_text, build_welcome_text,
build_status_text, parse_command, handle_standard_command,
)
"""
# Infrastructure
import sys
from pathlib import Path
# Prax logger and CLI utilities
from aipass.prax.apps.modules.logger import system_logger as logger
from aipass.cli.apps.modules import console, header, success, error, warning
# Handler imports (operations)
from aipass.api.apps.handlers.telegram import bot_operations
# =============================================
# RE-EXPORTS FROM HANDLERS (Public API)
# =============================================
from aipass.api.apps.handlers.telegram.base_bot import BaseBot
from aipass.api.apps.handlers.telegram.branch_plugin import BranchPlugin
from aipass.api.apps.handlers.telegram.bot_registry import (
list_bots,
get_bot,
register_bot,
get_bot_by_branch,
)
from aipass.api.apps.handlers.telegram.bot_factory import create_bot, delete_bot
from aipass.api.apps.handlers.telegram.config import load_bot_config
from aipass.api.apps.handlers.telegram.telegram_standards import (
STANDARD_COMMANDS,
PROCESSING_MSG,
build_help_text,
build_welcome_text,
build_status_text,
build_botfather_commands,
parse_command,
handle_standard_command,
)
# =============================================
# MODULE INTROSPECTION
# =============================================
def print_introspection() -> None:
"""Show module introspection - connected handlers and capabilities"""
console.print()
header("Telegram Multi-Bot Module Introspection")
console.print()
console.print("[cyan]Purpose:[/cyan] Multi-bot Telegram architecture for AIPass")
console.print()
console.print("[cyan]Connected Handlers:[/cyan]")
console.print(" - api.apps.handlers.telegram.base_bot")
console.print(" - api.apps.handlers.telegram.branch_plugin")
console.print(" - api.apps.handlers.telegram.bot_registry")
console.print(" - api.apps.handlers.telegram.bot_factory")
console.print(" - api.apps.handlers.telegram.bot_operations")
console.print(" - api.apps.handlers.telegram.config")
console.print(" - api.apps.handlers.telegram.telegram_standards")
console.print()
console.print("[cyan]Available Commands:[/cyan]")
console.print(" - start <bot_id> - Start a specific bot (polling loop)")
console.print(" - stop <bot_id> - Stop a bot's systemd service")
console.print(" - status [bot_id] - Show bot status (single or all)")
console.print(" - list - List all registered bots")
console.print(" - create <bot_id> <token> [options] - Create new bot")
console.print(" - delete <bot_id> - Delete a bot")
console.print()
def print_help() -> None:
"""Print module help with argparse"""
import argparse
parser = argparse.ArgumentParser(
prog="python3 telegram_bot.py",
description="Telegram Multi-Bot Module - AIPass multi-bot management",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
COMMANDS:
start <bot_id> - Start a bot (long-polling, blocks)
stop <bot_id> - Stop a bot's systemd service
status [bot_id] - Show bot status (single or all)
list - List all registered bots
create <bot_id> <token> [opts] - Create a new bot
delete <bot_id> - Delete a bot
CREATE OPTIONS:
--branch <name> - Associate with an AIPass branch
--work-dir <path> - Working directory for Claude sessions
EXAMPLES:
# Start a bot (blocks until Ctrl+C)
python3 telegram_bot.py start dev_central
# Check all bot statuses
python3 telegram_bot.py status
# Create a new branch bot
python3 telegram_bot.py create dev_central 123:ABC --branch dev_central
# List all bots
python3 telegram_bot.py list
"""
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
start_p = subparsers.add_parser("start", help="Start a bot")
start_p.add_argument("bot_id", help="Bot identifier")
stop_p = subparsers.add_parser("stop", help="Stop a bot's systemd service")
stop_p.add_argument("bot_id", help="Bot identifier")
status_p = subparsers.add_parser("status", help="Show bot status")
status_p.add_argument("bot_id", nargs="?", help="Bot identifier (optional)")
subparsers.add_parser("list", help="List all registered bots")
create_p = subparsers.add_parser("create", help="Create a new bot")
create_p.add_argument("bot_id", help="Bot identifier")
create_p.add_argument("token", help="Telegram bot token")
create_p.add_argument("--branch", help="AIPass branch name")
create_p.add_argument("--work-dir", help="Working directory")
delete_p = subparsers.add_parser("delete", help="Delete a bot")
delete_p.add_argument("bot_id", help="Bot identifier")
console.print(parser.format_help())
# =============================================
# COMMAND HANDLING
# =============================================
def handle_command(command: str, args: list) -> bool:
"""
Handle module commands routed via drone.
Args:
command: Command name (e.g., "telegram_bot")
args: Command arguments
Returns:
True if command was handled, False to pass through
"""
if not args:
return False
subcommand = args[0]
if subcommand == "start" and len(args) >= 2:
_cmd_start(args[1])
return True
elif subcommand == "stop" and len(args) >= 2:
_cmd_stop(args[1])
return True
elif subcommand == "status":
bot_id = args[1] if len(args) >= 2 else None
_cmd_status(bot_id)
return True
elif subcommand == "list":
_cmd_list()
return True
elif subcommand == "create" and len(args) >= 3:
_cmd_create(args[1:])
return True
elif subcommand == "delete" and len(args) >= 2:
_cmd_delete(args[1])
return True
elif subcommand == "help":
print_help()
return True
return False
# =============================================
# THIN ORCHESTRATION (delegates to bot_operations handler)
# =============================================
def _cmd_start(bot_id: str) -> None:
"""Orchestrate bot start: header, delegate, exit."""
header(f"Starting Bot: {bot_id}")
console.print()
config = load_bot_config(bot_id)
if not config:
error(f"No config found for bot '{bot_id}'")
console.print("[dim]Expected: ~/.aipass/telegram_bots/{bot_id}.json[/dim]")
return
if not config.get("bot_token"):
error(f"No bot_token in config for '{bot_id}'")
return
bot_name = config.get("bot_name", f"AIPass {bot_id} Bot")
logger.info("Starting bot '%s' (%s)", bot_id, bot_name)
success(f"Launching {bot_name}...")
console.print()
exit_code = bot_operations.start_bot(bot_id)
if exit_code is None:
error(f"Bot '{bot_id}' failed to start")
else:
sys.exit(exit_code)
def _cmd_stop(bot_id: str) -> None:
"""Orchestrate bot stop: header, delegate, display result."""
header(f"Stopping Bot: {bot_id}")
console.print()
ok, msg = bot_operations.stop_bot(bot_id)
if ok:
logger.info("Stopped bot '%s'", bot_id)
success(msg)
else:
logger.warning("Failed to stop bot '%s': %s", bot_id, msg)
error(msg)
console.print()
def _cmd_status(bot_id: str | None = None) -> None:
"""Orchestrate status display: header, delegate, format."""
if bot_id:
header(f"Bot Status: {bot_id}")
else:
header("All Bots Status")
console.print()
bots = bot_operations.get_status(bot_id)
if not bots:
if bot_id:
error(f"Bot '{bot_id}' not found in registry")
else:
warning("No bots registered")
console.print("[dim]Use 'create' to add a new bot[/dim]")
console.print()
return
for bot in bots:
for line in bot_operations.format_bot_details(bot):
console.print(f" [cyan]{line}[/cyan]")
console.print()
def _cmd_list() -> None:
"""Orchestrate bot listing: header, delegate, format."""
header("Registered Bots")
console.print()
bots = bot_operations.get_all_bots()
if not bots:
warning("No bots registered")
console.print("[dim]Use 'create' to add a new bot[/dim]")
console.print()
return
for line in bot_operations.format_bot_table(bots):
console.print(line)
console.print()
def _cmd_create(args: list) -> None:
"""Orchestrate bot creation: parse args, delegate, display result."""
parsed = bot_operations.parse_create_args(args)
if not parsed:
error("Usage: create <bot_id> <token> [--branch <name>] [--work-dir <path>]")
return
bot_id = parsed["bot_id"]
header(f"Creating Bot: {bot_id}")
console.print()
result = create_bot(
bot_id=bot_id,
bot_token=parsed["bot_token"],
branch_name=parsed["branch_name"],
work_dir=parsed["work_dir"],
)
if result:
logger.info("Bot created via module: %s", bot_id)
success(f"Bot '{bot_id}' created successfully")
console.print()
details = bot_operations.format_bot_details({
"bot_id": result["bot_id"],
"username": result["username"],
"branch_name": result.get("branch_name"),
"work_dir": result["work_dir"],
"status": "active",
"service_name": result["service_name"],
})
for line in details:
console.print(f" [cyan]{line}[/cyan]")
else:
logger.warning("Bot creation failed for '%s'", bot_id)
error(f"Failed to create bot '{bot_id}'. Check logs for details.")
console.print()
def _cmd_delete(bot_id: str) -> None:
"""Orchestrate bot deletion: verify, delegate, display result."""
header(f"Deleting Bot: {bot_id}")
console.print()
bot = get_bot(bot_id)
if not bot:
error(f"Bot '{bot_id}' not found in registry")
console.print()
return
result = delete_bot(bot_id)
if result:
logger.info("Bot deleted via module: %s", bot_id)
success(f"Bot '{bot_id}' deleted successfully")
else:
logger.warning("Bot deletion failed for '%s'", bot_id)
error(f"Failed to delete bot '{bot_id}'. Check logs for details.")
console.print()
# =============================================
# STANDALONE EXECUTION
# =============================================
if __name__ == "__main__":
"""Standalone execution mode"""
args = sys.argv[1:]
# Show introspection when run without arguments
if len(args) == 0:
print_introspection()
sys.exit(0)
# Show help for explicit help flags
if args[0] in ['--help', '-h', 'help']:
print_help()
sys.exit(0)
# Execute command directly (standalone mode, not drone-routed)
command = args[0]
if command == "start" and len(args) >= 2:
_cmd_start(args[1])
elif command == "stop" and len(args) >= 2:
_cmd_stop(args[1])
elif command == "status":
bot_id = args[1] if len(args) >= 2 else None
_cmd_status(bot_id)
elif command == "list":
_cmd_list()
elif command == "create" and len(args) >= 3:
_cmd_create(args[1:])
elif command == "delete" and len(args) >= 2:
_cmd_delete(args[1])
else:
console.print()
console.print(f"[red]Unknown command: {command}[/red]")
console.print()
console.print("Run [dim]python3 telegram_bot.py --help[/dim] for available commands")
console.print()
sys.exit(1)
@@ -1,230 +0,0 @@
# ===================AIPASS====================
# META DATA HEADER
# Name: telegram_service.py - Telegram Service Control Module
# Date: 2026-02-03
# Version: 1.0.0
# Category: api/modules
# CODE STANDARDS: Seed v1.0.0
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-02-03): Initial module - systemd service control for telegram bridge
# =============================================
"""
Telegram Service Control Module
Manages the telegram-bridge systemd user service:
- drone @api telegram start → Start service
- drone @api telegram stop → Stop service
- drone @api telegram status → Check service status
"""
import sys
from pathlib import Path
from typing import List
from aipass.prax.apps.modules.logger import system_logger as logger
from aipass.cli.apps.modules import console, header, success, error, warning
from aipass.api.apps.handlers.telegram_service import service
def print_introspection() -> None:
"""Show module introspection"""
console.print()
header("Telegram Service Control Introspection")
console.print()
console.print("[cyan]Purpose:[/cyan] Manage telegram-bridge systemd service")
console.print()
console.print("[cyan]Connected Handlers:[/cyan]")
console.print(" • api.apps.handlers.telegram_service.service")
console.print()
console.print("[cyan]Available Commands:[/cyan]")
console.print(" • telegram start - Start the service")
console.print(" • telegram stop - Stop the service")
console.print(" • telegram status - Check service status")
console.print(" • telegram logs - View recent logs")
console.print()
console.print("[cyan]Service:[/cyan]")
console.print(f" • Name: {service.SERVICE_NAME}.service")
console.print(" • Type: systemd user service")
console.print()
def print_help() -> None:
"""Print module help"""
import argparse
parser = argparse.ArgumentParser(
prog="drone @api telegram",
description="Telegram Service Control - Manage the telegram-bridge service",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
SUBCOMMANDS:
start - Start the telegram-bridge service
stop - Stop the telegram-bridge service
status - Check service status
logs - View recent service logs
USAGE:
drone @api telegram start
drone @api telegram stop
drone @api telegram status
drone @api telegram logs
EXAMPLES:
# Start the bot
drone @api telegram start
# Check if running
drone @api telegram status
# Stop the bot
drone @api telegram stop
# View logs
drone @api telegram logs
"""
)
console.print(parser.format_help())
def handle_command(command: str, args: List[str]) -> bool:
"""
Handle telegram service commands
Args:
command: Command name (should be 'telegram')
args: Subcommand and arguments
Returns:
True if command was handled, False otherwise
"""
if command != "telegram":
return False
try:
if not args:
print_help()
return True
subcommand = args[0]
if subcommand == "start":
_handle_start()
elif subcommand == "stop":
_handle_stop()
elif subcommand == "status":
_handle_status()
elif subcommand == "logs":
_handle_logs()
elif subcommand in ["--help", "-h", "help"]:
print_help()
else:
error(f"Unknown subcommand: {subcommand}")
console.print()
console.print("[dim]Use 'drone @api telegram --help' for usage[/dim]")
return True
except Exception as e:
logger.error("Error in telegram_service.handle_command: %s", str(e))
raise
def _handle_start() -> None:
"""Orchestrate service start"""
header("Telegram Bridge Service")
console.print()
ok, msg = service.start_service()
if ok:
success(msg)
console.print()
console.print("[dim]Check status: drone @api telegram status[/dim]")
console.print("[dim]View logs: drone @api telegram logs[/dim]")
else:
error(f"Failed to start service: {msg}")
def _handle_stop() -> None:
"""Orchestrate service stop"""
header("Telegram Bridge Service")
console.print()
ok, msg = service.stop_service()
if ok:
success(msg)
else:
error(f"Failed to stop service: {msg}")
def _handle_status() -> None:
"""Orchestrate status check"""
header("Telegram Bridge Service Status")
console.print()
status_code, output = service.get_status()
if status_code == "running":
success("Service is running")
elif status_code == "stopped":
warning("Service is stopped")
elif status_code == "not_found":
error("Service not found - run 'systemctl --user daemon-reload'")
else:
warning("Unknown status")
console.print()
for line in output.split("\n"):
line = line.strip()
if any(k in line for k in ["Active:", "Main PID:", "Memory:", "CPU:"]):
console.print(f" {line}")
console.print()
console.print("[dim]Logs: drone @api telegram logs[/dim]")
def _handle_logs() -> None:
"""Orchestrate log retrieval"""
header("Telegram Bridge Service Logs")
console.print()
ok, content = service.get_logs(30)
if not ok:
warning(content)
return
console.print(f"[dim]Showing last 30 lines[/dim]")
console.print()
for line in content.split("\n"):
console.print(line.rstrip())
if __name__ == "__main__":
"""Standalone execution mode"""
args = sys.argv[1:]
if len(args) == 0:
print_introspection()
sys.exit(0)
if args[0] in ['--help', '-h', 'help']:
print_help()
sys.exit(0)
if handle_command("telegram", args):
sys.exit(0)
else:
console.print()
console.print(f"[red]Unknown command: {args[0]}[/red]")
console.print()
sys.exit(1)