{"id":50360800,"url":"https://github.com/codeweiz/relais","last_synced_at":"2026-05-30T01:20:59.602Z","repository":{"id":347350457,"uuid":"1193713378","full_name":"codeweiz/relais","owner":"codeweiz","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-27T14:54:29.000Z","size":1032,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-28T00:35:26.572Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codeweiz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T14:01:10.000Z","updated_at":"2026-03-27T14:55:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/codeweiz/relais","commit_stats":null,"previous_names":["codeweiz/relais"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/codeweiz/relais","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeweiz%2Frelais","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeweiz%2Frelais/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeweiz%2Frelais/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeweiz%2Frelais/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codeweiz","download_url":"https://codeload.github.com/codeweiz/relais/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeweiz%2Frelais/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33677253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T01:20:58.780Z","updated_at":"2026-05-30T01:20:59.593Z","avatar_url":"https://github.com/codeweiz.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/logo.png\" alt=\"Relais Logo\" width=\"128\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eRelais\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eRemote terminal access and AI agent orchestration — from anywhere.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#architecture\"\u003eArchitecture\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#how-it-works\"\u003eHow It Works\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#releases\"\u003eReleases\u003c/a\u003e \u0026middot;\n  \u003ca href=\"README-zh.md\"\u003e中文文档\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nRelais is a self-hosted platform that lets you manage multiple AI coding agents and remote terminals from a single cross-platform client. Spawn Claude Code, Gemini CLI, Codex, or OpenCode sessions on your server, interact with them via chat or terminal, dispatch tasks across agents, and bridge conversations to IM platforms like Feishu/Lark — all from your phone, tablet, or desktop.\n\n## Features\n\n### Multi-Agent Workspace\n\nA unified interface to manage all your AI agents and terminals in one place.\n\n- **Supported providers**: Claude Code, Gemini CLI, Codex, OpenCode\n- **Live status tracking**: initializing, ready, working, idle, crashed — updated in real time via WebSocket\n- **Activity awareness**: see when an agent is thinking, calling tools, or waiting for approval\n- **Cost tracking**: per-agent USD cost display\n- **Swipe actions**: quick delete, info drawer, IM binding status\n- **Filter \u0026 sort**: filter by provider, sort by last active time\n\n### Remote Terminal\n\nFull PTY terminal access over WebSocket, backed by tmux for session persistence.\n\n- **xterm.js rendering**: true color, cursor positioning, full escape sequence support\n- **Session persistence**: tmux backend keeps sessions alive across reconnections; `capture-pane` sync on reconnect\n- **Quick-command bar**: pre-defined shortcuts (clear, top, history, etc.)\n- **Special key row**: Ctrl, Alt, Tab, Esc, arrow keys for mobile use\n- **Voice input**: speech-to-text for hands-free terminal use\n- **Resize support**: dynamic terminal resize propagated over WebSocket\n\n### Task Dispatch\n\nCreate, assign, and track tasks across agents with priority-based scheduling.\n\n- **Priority levels**: P0 (immediate), P1 (default), P2 (background)\n- **Lifecycle**: Queued → Blocked → Running → NeedsReview / Completed / Failed / Cancelled\n- **`@agent` dispatch**: type `@agent_name task description` in any agent chat to create a cross-agent task\n- **`/` slash commands**: autocomplete menu for common operations\n- **Dependency management**: tasks can declare `depends_on` to block until prerequisites complete\n- **Source callback**: originating agent receives notification when dispatched task completes\n- **Auto-scheduling**: scheduler matches pending tasks to idle agents with exponential backoff\n\n### IM Integration\n\nBridge AI agent conversations to IM platforms via a plugin system.\n\n- **Supported platforms**: Feishu/Lark (built-in plugin), Slack, DingTalk (via plugin API)\n- **Multi-account**: manage multiple IM accounts, each bound to different agents\n- **Per-group binding**: bind specific IM groups to specific agents with custom system prompts\n- **Reply modes**: streaming (real-time chunks), static (buffered), text-only\n- **Event filtering**: open, mention-required, allowlist, or disabled per group\n- **Tool policy**: per-group allow/deny lists for agent tool access\n- **OAuth support**: user-scoped Feishu operations (create docs, manage tasks on behalf of user)\n- **Deduplication**: prevents message storms from emoji triggers\n\n### Real-time Notifications\n\nPush notifications for agent events, task completion, and IM activity.\n\n- **Transport**: FCM (Android), APNs (iOS), Web Push, WebSocket (in-app)\n- **iOS Live Activities**: real-time agent status on lock screen (iOS 16+)\n- **Notification types**: agent errors, task completion, IM events\n- **Per-device preferences**: toggle notification types per device\n- **Deep links**: tap notification to jump directly to relevant agent/task/conversation\n- **In-app feed**: notification center with read/unread state and type-specific cards\n\n### Cross-Platform Client\n\nFlutter app with native experience on every platform.\n\n- **Platforms**: iOS, Android, macOS, Windows, Web\n- **QR code pairing**: scan QR from server CLI to connect instantly\n- **Deep links**: `relais://connect?url=host:port\u0026token=abc` for one-tap connection\n- **Multi-server**: save and switch between multiple server connections\n- **State management**: Riverpod with annotation-based providers\n- **Markdown rendering**: rich display of agent responses with code highlighting\n\n### Plugin System\n\nExtensible architecture for IM platforms and tunnel providers.\n\n- **Plugin types**: IM (bidirectional messaging) and Tunnel (public URL provisioning)\n- **Protocol**: JSON-RPC 2.0 over stdin/stdout (newline-delimited)\n- **Manifest**: TOML-based plugin descriptor (`plugin.toml`)\n- **Hot-reload**: file watcher auto-discovers and starts new plugins in `~/.relais/plugins/`\n- **Crash recovery**: automatic restart with exponential backoff (max 3 attempts, 3s/6s/12s)\n- **Built-in plugins**: Feishu/Lark IM (WebSocket + Protobuf), Cloudflare Tunnel\n\n---\n\n## Architecture\n\n```\nrelais/\n├── client/       Flutter app (iOS, Android, macOS, Windows, Web)\n├── server/       Rust backend (terminal, agents, task scheduler, plugins)\n└── protocol/     Shared API specification\n```\n\n### Server — Rust\n\nThe server is a multi-crate Rust workspace built on **Axum** and **Tokio**.\n\n| Crate | Role |\n|-------|------|\n| **relais-core** | Agent manager, PTY manager (tmux backend), task pool, event bus, session store, settings |\n| **relais-server** | HTTP/WebSocket API (Axum), JWT auth (ECDSA/JWK), push dispatcher (FCM/APNs/WebPush), notification store, rate limiting |\n| **relais-plugin-host** | Plugin lifecycle manager, IM bridge (message routing, group bindings, tool policy), JSON-RPC host |\n| **relais-cli** | CLI binary (`relais start`), QR code generation, local IP detection |\n\nStandalone plugins (excluded from workspace, built separately):\n\n| Plugin | Description |\n|--------|-------------|\n| **feishu-plugin** | Feishu/Lark real-time messaging via WebSocket long connection + Protobuf frames, OAuth user token exchange |\n| **cloudflare-tunnel** | Cloudflare Tunnel integration for exposing local server to the internet |\n\n### Event Bus\n\nThe server uses a hybrid event bus architecture:\n\n- **Broadcast channel** — low-frequency control events: session lifecycle, plugin events, notifications, task state changes\n- **Per-session mpsc channels** — high-volume data streams: PTY output, agent text/thinking/tool-use events\n\n### API Surface\n\n**REST** — `GET/POST/PATCH/DELETE` endpoints for sessions, tasks, agents, plugins, IM accounts, notifications, push registration, settings, and tunnel control.\n\n**WebSocket** — three persistent connections:\n| Endpoint | Purpose |\n|----------|---------|\n| `/ws/terminal?session={id}` | Real-time PTY I/O (binary frames + JSON control) |\n| `/ws/agent?session={id}` | Agent message streaming (text, thinking, tool use/result) |\n| `/ws/status` | Live status updates for all agents |\n\n**Auth** — Token-based with priority: Bearer header → HttpOnly cookie (`rtb_token`) → query param (auto-redirected to cookie).\n\n### Client — Flutter\n\n| Layer | Technology |\n|-------|------------|\n| State management | Riverpod (annotation-based code generation) |\n| Navigation | GoRouter with deep link support |\n| HTTP | Dio |\n| WebSocket | web_socket_channel |\n| Terminal rendering | xterm.dart |\n| QR scanning | mobile_scanner |\n| Voice input | speech_to_text |\n| Markdown | flutter_markdown |\n| Typography | google_fonts |\n\n**Key screens**:\n\n| Screen | Description |\n|--------|-------------|\n| **Connect** | Server URL + token input, QR scanner, multi-server management |\n| **Workspace** | Unified agent + terminal list with provider filters, status badges, cost display |\n| **Agent Chat** | Real-time conversation with streaming text, thinking blocks, tool use rendering, `/` commands, `@` dispatch |\n| **Terminal** | xterm view with quick-command bar, special key row, voice input |\n| **Tasks** | Priority-sorted task queue with status tracking and progress indicators |\n| **Notifications** | Real-time notification feed with type-specific card rendering |\n| **Office** | IM account management, group bindings, Feishu integration |\n| **Settings** | Appearance, terminal preferences, IM configuration |\n\n---\n\n## How It Works\n\n### Connection Flow\n\n```\n┌──────────┐     QR / Deep Link     ┌──────────────┐\n│  Client   │ ◄──────────────────── │  relais start │\n│ (Flutter) │                        │   (CLI)       │\n└─────┬─────┘                        └──────┬───────┘\n      │                                      │\n      │  REST + WebSocket (token auth)       │\n      ├──────────────────────────────────────►│\n      │                                      │\n      │  /ws/terminal  (PTY I/O)             │  tmux sessions\n      │  /ws/agent     (agent stream)        │  agent subprocesses\n      │  /ws/status    (live status)         │  plugin host\n      │◄─────────────────────────────────────►│\n```\n\n1. **Start server**: `relais start` prints a QR code containing the server URL and auth token\n2. **Connect client**: scan QR, enter URL manually, or open a `relais://` deep link\n3. **Create sessions**: spawn agent or terminal sessions from the workspace\n4. **Interact**: chat with agents, use the terminal, dispatch tasks, receive notifications\n\n### Agent Communication\n\nAgents communicate via **ACP (Agent Communication Protocol)** — JSON-RPC 2.0 over newline-delimited stdin/stdout. The server spawns agent CLI binaries as subprocesses and bridges their I/O to WebSocket clients.\n\nSupported events: `AgentText`, `AgentThinking`, `AgentToolUse`, `AgentToolResult`, `AgentError`, `AgentTurnComplete`, `AvailableCommands`.\n\nSessions are persisted to disk (`~/.relais/sessions/`) and can be resumed with `--resume`.\n\n### Plugin Protocol\n\nPlugins are standalone executables that communicate with the server via JSON-RPC 2.0 over stdin/stdout:\n\n```\nHost → Plugin:  im/initialize, im/send_message, im/send_image, im/shutdown\nPlugin → Host:  im/on_message, im/on_status\n```\n\nEach plugin has a `plugin.toml` manifest:\n\n```toml\n[plugin]\nid = \"feishu-im\"\nname = \"Feishu IM Plugin\"\nversion = \"0.1.0\"\ntype = \"im\"\nexecutable = \"feishu-plugin\"\n\n[config]\napp_id = \"\"\napp_secret = \"\"\n```\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n- **Rust** 1.82+ (server)\n- **Flutter** 3.x with Dart 3.11+ (client)\n\n### Server\n\n```bash\n# Development (builds and installs plugins automatically)\nmake server-dev\n\n# Production build\nmake server-build\n\n# Install to /usr/local/bin\nmake server-install\n```\n\n### Client\n\n```bash\n# macOS (development)\nmake client-dev\n\n# Web (development)\nmake client-web\n\n# Build web release\nmake client-build-web\n```\n\n### All Commands\n\n```bash\nmake help\n```\n\n---\n\n## Releases\n\nPre-built binaries are available on the [Releases](https://github.com/codeweiz/relais/releases) page.\n\n| Platform | Server | Client |\n|----------|--------|--------|\n| macOS (Apple Silicon) | `relais-server-aarch64-apple-darwin.tar.gz` | `Relais.dmg` |\n| macOS (Intel) | `relais-server-x86_64-apple-darwin.tar.gz` | — |\n| Linux (x86_64) | `relais-server-x86_64-unknown-linux-gnu.tar.gz` | — |\n| Windows (x86_64) | `relais-server-x86_64-pc-windows-msvc.zip` | `relais-windows.zip` |\n| Android | — | `relais.apk` |\n| iOS | — | TestFlight / Build from source |\n| Web | — | `relais-web.tar.gz` |\n\n---\n\n## Configuration\n\nThe server stores all data in `~/.relais/`:\n\n```\n~/.relais/\n├── config.toml              # Agent providers, default shell, scheduler settings\n├── settings.json            # User preferences (appearance, terminal, IM accounts)\n├── sessions/\n│   └── {session_id}/\n│       ├── meta.json        # Session metadata (type, provider, status)\n│       └── events.jsonl     # Event history (append-only)\n├── tasks.jsonl              # Task queue (JSONL persistence)\n└── plugins/\n    ├── feishu-im/\n    │   ├── plugin.toml      # Plugin manifest + config\n    │   └── feishu-plugin    # Plugin binary\n    └── cloudflare-tunnel/\n        ├── plugin.toml\n        └── cloudflare-tunnel\n```\n\n---\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeweiz%2Frelais","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodeweiz%2Frelais","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeweiz%2Frelais/lists"}