myctobot / aoe-php
AI Agent Session Manager - PHP port of agent-of-empires with multi-tenancy support
v1.0.1
2026-02-19 14:23 UTC
Requires
- php: >=8.1
- nesbot/carbon: ^3.0
- ramsey/uuid: ^4.0
- symfony/console: ^6.0
Requires (Dev)
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2026-03-19 14:32:26 UTC
README
A PHP port of agent-of-empires - a terminal session manager for AI coding agents (Claude Code, OpenCode) using tmux.
Features
- Multi-tenant support - Isolated sessions per tenant, integrated with myctobot config
- Session management - Create, list, remove, and manage AI agent sessions
- tmux integration - Each session runs in its own tmux session
- WebSocket server - Real-time terminal streaming via OpenSwoole (planned)
- CLI interface - Full command-line interface using Symfony Console
Requirements
- PHP 8.1+
- Composer
- tmux
- OpenSwoole extension (for WebSocket features)
Installation
composer require myctobot/aoe-php
Or clone and install locally:
git clone <repository> cd aoe-php composer install
Configuration
Default Configuration
The default configuration is in config/aoe.php:
return [ 'storage_path' => $_SERVER['HOME'] . '/.aoe-php', 'default_tool' => 'claude', 'myctobot_conf_path' => '/path/to/myctobot/conf', 'tmux' => [ 'prefix' => 'aoe-', // Session prefix: aoe-{tenant}-{id} ], 'websocket' => [ 'host' => '127.0.0.1', 'port' => 9502, 'poll_interval' => 100, ], ];
Tenant Configuration
Tenants are discovered from myctobot config files (conf/config.{tenant}.ini). Add an [aoe] section for tenant-specific overrides:
; In myctobot/conf/config.acme.ini [aoe] storage_path = /var/lib/aoe/acme default_tool = claude
Usage
All session commands require a tenant specified via --tenant flag or AOE_TENANT environment variable.
List Available Tenants
bin/aoe tenant:list
Output:
+---------+----------+----------------+
| Tenant | Sessions | Has AOE Config |
+---------+----------+----------------+
| acme | 3 | Yes |
| demo | 0 | No |
+---------+----------+----------------+
Add a Session
# Add current directory bin/aoe --tenant=acme add # Add specific path with options bin/aoe --tenant=acme add /path/to/project \ --title="My Project" \ --group="frontend/web" \ --tool=claude
List Sessions
bin/aoe --tenant=acme sessions # Filter by group bin/aoe --tenant=acme sessions --group="frontend" # JSON output bin/aoe --tenant=acme sessions --json
Output:
+----------+------------------+-----------+--------+-------+---------------------------+
| ID | Title | Status | Tool | Group | Path |
+----------+------------------+-----------+--------+-------+---------------------------+
| 49e8e3b0 | My Project | ⏹ Stopped | claude | - | /home/user/projects/myapp |
+----------+------------------+-----------+--------+-------+---------------------------+
Show Status Summary
bin/aoe --tenant=acme status
Output:
Session Status for tenant: acme
By Status:
⚡ Running: 1
⏳ Waiting: 0
💤 Idle: 2
⏹ Stopped: 5
By Tool:
claude: 6
opencode: 2
Total: 8 session(s), 1 active
Remove a Session
# With confirmation bin/aoe --tenant=acme remove abc12345 # Force (no confirmation) bin/aoe --tenant=acme remove abc1 --force
Using Environment Variable
export AOE_TENANT=acme
bin/aoe sessions
bin/aoe add /path/to/project
bin/aoe status
Start a Session
# Start session (creates tmux session) bin/aoe --tenant=acme session:start abc12345 # Override command bin/aoe --tenant=acme session:start abc1 --command="claude --continue" # Dry run (show what would happen) bin/aoe --tenant=acme session:start abc1 --dry-run
Stop a Session
# Stop with confirmation bin/aoe --tenant=acme session:stop abc12345 # Force stop (no confirmation) bin/aoe --tenant=acme session:stop abc1 --force
Restart a Session
bin/aoe --tenant=acme session:restart abc12345
Attach to a Session
bin/aoe --tenant=acme session:attach abc12345
# Press Ctrl+B then D to detach
Session Status Detail
# Show detailed status bin/aoe --tenant=acme session:status abc12345 # Include captured pane content bin/aoe --tenant=acme session:status abc1 --capture=30 # Update status by analyzing pane content bin/aoe --tenant=acme session:status abc1 --update # JSON output bin/aoe --tenant=acme session:status abc1 --json
Project Structure
aoe-php/
├── bin/
│ └── aoe # CLI entry point
├── config/
│ └── aoe.php # Default configuration
├── src/
│ ├── Aoe.php # Main facade (planned)
│ ├── Config/
│ │ └── AoeConfig.php # Configuration loader
│ ├── Tenant/
│ │ ├── TenantContext.php # Current tenant holder
│ │ ├── TenantResolver.php # Resolve tenant from CLI/env
│ │ └── TenantRequiredException.php
│ ├── Session/
│ │ ├── Instance.php # Session entity
│ │ ├── Status.php # Status enum
│ │ ├── Storage.php # JSON persistence
│ │ ├── WorktreeInfo.php # Git worktree info
│ │ └── Group.php # Session grouping
│ ├── Tmux/ # (Phase 2 - Complete)
│ │ ├── TmuxService.php # tmux wrapper (tenant-prefixed)
│ │ └── StatusDetector.php # Detect status from output
│ ├── Git/ # (Phase 5)
│ │ └── WorktreeService.php # Git worktree operations
│ ├── WebSocket/ # (Phase 3-4)
│ │ ├── Server.php # OpenSwoole WebSocket
│ │ ├── TerminalProxy.php # Terminal streaming
│ │ ├── MessageHandler.php # Command handling
│ │ └── ClientManager.php # Client tracking
│ └── Cli/
│ ├── Application.php # CLI application
│ └── Commands/
│ ├── BaseCommand.php
│ ├── TenantCommand.php
│ ├── AddCommand.php
│ ├── ListCommand.php
│ ├── RemoveCommand.php
│ ├── StatusCommand.php
│ ├── SessionStartCommand.php
│ ├── SessionStopCommand.php
│ ├── SessionRestartCommand.php
│ ├── SessionAttachCommand.php
│ └── SessionStatusCommand.php
└── tests/
Data Storage
Sessions are stored in JSON files, isolated per tenant:
~/.aoe-php/
└── tenants/
├── acme/
│ ├── sessions.json
│ └── groups.json
└── demo/
├── sessions.json
└── groups.json
Implementation Status
Phase 1: Core Session Management ✅
- Project structure and composer.json
- Multi-tenancy support (TenantContext, TenantResolver)
- Configuration with myctobot INI integration
- Session entities (Instance, Status, WorktreeInfo, Group)
- JSON storage (tenant-scoped)
- CLI commands:
tenant:list,add,sessions,remove,status
Phase 2: Tmux Integration ✅
- TmuxService wrapper (tenant-prefixed sessions)
- StatusDetector (analyze pane content for Running/Waiting/Idle/Error)
- CLI commands:
session:start,session:stop,session:restart,session:attach,session:status - Instance class integration:
start(),stop(),restart(),captureOutput(),sendKeys(),refreshStatus()
Phase 3: WebSocket Server (Planned)
- OpenSwoole WebSocket server
- ClientManager (tenant-scoped connections)
- MessageHandler with tenant authentication
- Commands:
server:start,server:stop
Phase 4: Terminal Proxy (Planned)
- TerminalProxy with polling loop
- Output diffing
- ANSI handling
- Real-time streaming to WebSocket clients
Phase 5: Advanced Features (Planned)
- Git worktree support
- Claude session ID detection
- Session forking
- Group management commands
Integration with myctobot
As a Composer package:
{
"require": {
"myctobot/aoe-php": "^1.0"
}
}
Usage in controllers:
use Aoe\Session\Storage; use Aoe\Tenant\TenantContext; use Aoe\Config\AoeConfig; // Set tenant from session $tenantSlug = $_SESSION['tenant_slug']; TenantContext::set($tenantSlug); // Load config $config = AoeConfig::createDefault(); $tenantConfig = $config->forTenant($tenantSlug); // Get sessions $storage = new Storage($tenantSlug); $sessions = $storage->loadAll();
WebSocket Protocol (Planned)
// Client -> Server {"action": "auth", "tenant": "acme", "token": "optional"} {"action": "subscribe", "sessionId": "abc123"} {"action": "command", "sessionId": "abc123", "command": "start"} {"action": "sendKeys", "sessionId": "abc123", "keys": "y\n"} // Server -> Client {"type": "auth", "success": true, "tenant": "acme"} {"type": "output", "sessionId": "abc123", "data": "terminal output..."} {"type": "status", "sessionId": "abc123", "status": "running"}
License
MIT