A Python-based remote monitoring and control system that lets you manage your AI coding assistant from your phone — monitor tasks, approve commands, switch models, and stream execution logs in real-time.
A pip-installable CLI tool and FastAPI server that bridges the gap between a desktop AI coding agent (Antigravity IDE) and a mobile phone, enabling remote oversight and control.
The system follows a hub-and-spoke model with the FastAPI server at the center, mediating between the mobile client, the desktop AI agent, and the local filesystem.
Each module has a clear responsibility within the system. The package is pip-installable and exposes a single CLI entry point.
antigravity-mobile/
├── antigravity_remote/ # Main Python package
│ ├── __init__.py # Package init, __version__ = "0.3.7"
│ ├── __main__.py # python -m entry point → cli.main()
│ ├── cli.py # CLI: argparse, setup wizard, start, remote, daemon (799 lines)
│ ├── server.py # FastAPI app: REST API, WebSocket, auth, state (638 lines)
│ ├── agent_daemon.py # Daemon: long-poll server for remote prompts (117 lines)
│ ├── agent_approve.py # Approval helper: file-based + HTTP fallback (159 lines)
│ ├── task_runner.py # Task lifecycle: schedule, run, cancel, log (212 lines)
│ └── templates/
│ └── index.html # Mobile dashboard SPA (1397 lines, single-file)
├── .agents/
│ ├── AGENTS.md # Agent behavior rules for remote mode
│ └── skills/remote_control/
│ └── SKILL.md # Step-by-step remote control loop skill
├── tests/
│ └── test_server.py # pytest suite: auth, rate limits, traversal, lifecycle
├── pyproject.toml # Build config, deps, CLI entry point
└── README.md # User-facing documentation
799 linesantigravity-mobile command. Built with Python's argparse, it provides 8 subcommands and a beautiful ASCII art welcome screen with ANSI coloring.
main() — argparse dispatcher, ANSI setupcmd_setup() — 4-step setup wizard (PIN, agents, mode, PATH)cmd_start() — regenerate PIN, kill port, launch uvicorncmd_run() — authenticate, schedule, confirm & stream a taskcmd_remote() — toggle remote_mode.json on/offcmd_daemon() — start the agent_daemon listenercmd_update() — pip install --upgradecmd_uninstall() — cleanup files + guide pip uninstallgenerate_config() — 6-digit PIN + 256-bit secret keykill_port_process() — psutil + taskkill fallbackconfigure_windows_path() — winreg PATH modificationremove_windows_path() — clean PATH on uninstallprint_banner() — ASCII art with ANSI color codesprint_quickstart() — formatted quick-start guide638 linesACTIVE_SESSIONS — dict[token → timestamp], 24h TTLLOGIN_ATTEMPTS — dict[IP → (count, time)]REMOTE_PROMPT — in-memory prompt state machineREMOTE_APPROVAL — in-memory approval state machineTaskRunner — thread-safe task lifecycle manager117 linesremote_mode.json — if disabled, sleep 5s and retryconfig.json for the access PINGET /api/agent/prompt/check every 2sremote_prompt.json159 lines0 APPROVED — proceed with command
1 REJECTED — skip the action
2 TIMEOUT — no response within timeout
3 OFFLINE — server unreachable, bypass to desktop
212 linesid — 8-char UUID prefixcommand — shell command stringstatus — pending | running | completed | failed | cancelledexit_code — subprocess return codecreated_by — "remote" origin tag1397 linesThree key workflows define the system's behavior: remote prompt execution, command approval, and real-time monitoring.
POST /api/agent/prompt with the task prompt. Server sets REMOTE_PROMPT status to "pending" and writes remote_prompt.json to disk.agent_daemon.py process is long-polling GET /api/agent/prompt/check every 2 seconds. When it detects a pending prompt, it writes remote_prompt.json locally and exits with code 0.remote_prompt.json, clears it, and begins executing the task following SKILL.md instructions.agent_status.json and appends to agent_execution.log. The server reads these on each /api/status call or WebSocket tick.agent_response.json with status + output. The server picks it up during the next status poll, updates REMOTE_PROMPT, and deletes the file. The mobile dashboard shows the response.agent_approval_request.json with {"type": "command", "target": "..."}.get_system_status() call (triggered by WebSocket or REST), the server reads the approval request file, sets REMOTE_APPROVAL to "pending", and deletes the file from disk.POST /api/agent/approve/response is called. The server writes agent_approval_response.json with the decision.agent_approval_response.json every 1.5 seconds. When found, it reads the status, deletes the file, and proceeds or aborts accordingly./ws/status (2s interval system broadcast) and /ws/logs/{id} (live task log streaming).
Complete list of all 18 REST endpoints and 2 WebSocket channels exposed by the FastAPI server.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/login | — | Authenticate with PIN → receive session token. Rate limited: 5 attempts / 15 min. |
| GET | /api/status | Bearer | System stats (CPU, RAM, Disk), agent status, last 20 log lines. |
| GET | /api/workspace | Bearer | Directory tree of the workspace (3 levels deep, hidden files excluded). |
| GET | /api/file?path= | Bearer | Read file contents. Protected against path traversal via os.path.realpath() containment check. |
| GET | /api/tasks | Bearer | List all scheduled/running/completed tasks. |
| POST | /api/schedule | Bearer | Schedule a new command task (status: pending). Input validated for null bytes, control chars, length. |
| POST | /api/tasks/{id}/confirm | Bearer | Confirm and start executing a pending task in a background thread. |
| POST | /api/tasks/{id}/cancel | Bearer | Cancel a pending or kill a running task (process tree termination). |
| GET | /api/tasks/{id}/logs | Bearer | Get task log content, status, and exit code. |
| POST | /api/tasks/summary | Bearer | Generate markdown summary of all tasks → writes summary.md. |
| GET | /api/agent/status | Bearer | Current model, all available models, IDE limits, prompt & approval state. |
| POST | /api/agent/model | Bearer | Switch active AI model in Antigravity IDE settings. |
| POST | /api/agent/prompt | Bearer | Send a remote prompt to the desktop agent. Writes remote_prompt.json. |
| GET | /api/agent/prompt/check | Bearer | Check/consume pending prompt (transitions status: pending → executing). |
| POST | /api/agent/response | Bearer | Post agent's task response (status + output). |
| POST | /api/agent/approve/request | Bearer | Post a new approval request (command/edit/create/delete). |
| GET | /api/agent/approve/check | Bearer | Check current approval decision status. |
| POST | /api/agent/approve/response | Bearer | Submit approval decision (approved/rejected). Writes agent_approval_response.json. |
| POST | /api/shutdown | Bearer | Kill localtunnel proxy processes. |
| WS | /ws/status?token= | Query | Real-time system status broadcast every 2 seconds. Includes tasks, agent info, model, limits. |
| WS | /ws/logs/{id}?token= | Query | Live log streaming for a specific task. Tails the log file and streams new lines. |
Multi-layered security approach covering authentication, authorization, input validation, and path traversal protection.
secrets.choice()secrets.token_hex(32)secrets.token_hex(16)LOGIN_ATTEMPTS dict keyed by client IP/api/file resolves paths with os.path.realpath()os.sepshell=True — relies on PIN auth as primary gateThe agent and server communicate through workspace JSON files. This is the "silent" protocol that avoids triggering desktop popups.
| File | Writer | Reader | Purpose |
|---|---|---|---|
remote_prompt.json |
Server + Daemon | Agent | Carries the user's remote prompt from phone to agent. Cleared after read. |
agent_status.json |
Agent | Server | Agent's current state: {"status": "working"|"idle", "task": "..."} |
agent_execution.log |
Agent | Server | Append-only log of step-by-step agent execution thoughts (last 20 lines served). |
agent_approval_request.json |
Agent | Server | Command approval request: {"type": "command", "target": "..."}. Deleted after server reads it. |
agent_approval_response.json |
Server | Agent | Approval decision: {"status": "approved"|"rejected"}. Deleted after agent reads it. |
agent_response.json |
Agent | Server | Final task response: {"status": "completed", "output": "..."}. Deleted after server reads it. |
remote_mode.json |
CLI | Agent + Daemon | Feature toggle: {"enabled": true|false}. Controls whether remote features are active. |
config.json |
CLI | Server + Daemon + Approve | Access credentials: {"pin": "...", "secret_key": "..."}. Regenerated on every server start. |
# Runtime dependencies
fastapi>=0.100.0 # Async web framework with auto OpenAPI docs
uvicorn>=0.22.0 # Lightning-fast ASGI server
jinja2>=3.1.0 # Template engine (available but templates are inline)
websockets>=11.0 # WebSocket protocol support
psutil>=5.9.0 # Cross-platform system monitoring
# Dev dependencies
pytest>=7.0.0 # Test framework
httpx>=0.24.0 # Async HTTP client for testing
# stdlib (no install needed)
argparse # CLI argument parsing
secrets # Cryptographic random generation
threading # Thread-based task execution
subprocess # Shell command execution
urllib.request # Daemon/approve HTTP calls (no requests dep)
The test suite uses pytest with FastAPI's TestClient to validate authentication, authorization, security, and task lifecycle.
test_login_invalid_pin — 401 on wrong PINtest_login_success — valid token returnedtest_unauthorized_endpoints — 401/403 without authtest_authorized_status — system stats returnedtest_login_rate_limiting — 429 after 5 failurestest_session_expiry — 401 on expired tokentest_path_traversal_protection — 403 on ../test_command_validation — null bytes, control chars, lengthtest_task_lifecycle — schedule → confirm → run → logsautouse=True999999