https://github.com/epiral/cli
Plug any machine into Epiral Agent — shell, files, and browser control in one binary
https://github.com/epiral/cli
browser-automation cli connectrpc go http2 remote-execution
Last synced: about 1 month ago
JSON representation
Plug any machine into Epiral Agent — shell, files, and browser control in one binary
- Host: GitHub
- URL: https://github.com/epiral/cli
- Owner: epiral
- License: other
- Created: 2026-02-06T04:23:37.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-09T10:04:17.000Z (5 months ago)
- Last Synced: 2026-05-11T13:55:30.719Z (about 1 month ago)
- Topics: browser-automation, cli, connectrpc, go, http2, remote-execution
- Language: Go
- Size: 334 KB
- Stars: 18
- Watchers: 0
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.en.md
- License: LICENSE
Awesome Lists containing this project
README
# Epiral CLI
**One binary. Any machine becomes your Agent's extension.**
[](https://go.dev)
[](LICENSE)
[中文](README.md) | English
---
One binary, and your machine becomes an extension of [Epiral Agent](https://github.com/epiral/agent). Workstation, VPS, Docker sandbox — the Agent doesn't care what it is, just that it's available.
Registers as a **Computer** resource (shell + file operations). Built-in web management panel for configuration, logs, and status at a glance.
```
Epiral Agent
┌──────────────────────┐
│ ComputerHub │
│ ┌────────────────┐ │
│ │ computers [ ] │ │
│ └────────────────┘ │
└──┬─────────────┬─────┘
│ │
┌─────────┘ └─────────┐
│ │
┌──────┴──────────┐ ┌──────────┴──────┐
│ Epiral CLI │ │ Epiral CLI │
│ my-pc │ │ homelab │
│ │ │ │
│ Computer ✓ │ │ Computer ✓ │
│ Web UI :19800 │ │ Web UI :19800 │
└─────────────────┘ └─────────────────┘
```
## Why
Agents need real machines. But machines are behind NATs, on different networks, in different places.
**Reverse connection**: the CLI connects outward to the Agent. No port forwarding. No SSH. The Agent sees all registered machines and dispatches to any of them.
Multiple machines at once, each for a different purpose:
| Scenario | Machine | Why |
|----------|---------|-----|
| Daily dev | Workstation | Full dev environment, IDE configs |
| Untrusted scripts | Docker sandbox | Run and throw away |
| GPU training | Cloud server | Rent on demand, disconnect when done |
| Deploy testing | VPS | Simulates production |
The Agent routes tasks to the right machine. Dangerous ops go to a sandbox. The Agent is always safe.
## Quick Start
### Install
```bash
git clone https://github.com/epiral/cli.git
cd cli && make build
# Binary at ./bin/epiral
```
### Run (Recommended: Web Panel)
```bash
# Start with the web management panel
./bin/epiral start
# With custom config and port (multi-instance)
./bin/epiral start --config ~/.epiral/dev.yaml --port 19802
```
Open `http://localhost:19800`, fill in Agent address and Computer ID on the Config page, click Save & Restart.
### Run (Direct Mode)
```bash
./bin/epiral \
--agent http://your-agent:8002 \
--computer-id my-machine \
--paths /home/me/projects
```
## Web Management Panel
`epiral start` launches an embedded web panel (default port 19800):
| Page | Features |
|------|----------|
| **Dashboard** | Connection status, Computer info, uptime, reconnect count |
| **Config** | Visual configuration for Agent/Computer, Save & Restart |
| **Logs** | Real-time log stream (SSE), level filtering, scroll and pause |
Configuration is persisted to `~/.epiral/config.yaml`. Changes automatically restart the daemon — no manual intervention needed.
### Multi-Instance
Run multiple CLI instances on the same machine (e.g., connecting to both dev and prod Agents):
```bash
# Dev instance
./bin/epiral start --config ~/.epiral/dev.yaml --port 19800
# Prod instance
./bin/epiral start --config ~/.epiral/prod.yaml --port 19801
```
Each instance has its own config file and web port.
## Usage
### `epiral start` (Recommended)
```
epiral start [flags]
```
| Flag | Default | Description |
|------|---------|-------------|
| `--config` | `~/.epiral/config.yaml` | Config file path |
| `--port` | 19800 | Web panel port |
### `epiral` (Direct Mode)
```
epiral [flags]
```
| Flag | Required | Default | Description |
|------|----------|---------|-------------|
| `--agent` | **yes** | — | Agent server URL |
| `--computer-id` | **yes** | — | Machine identifier |
| `--computer-desc` | no | same as id | Display name |
| `--paths` | no | unrestricted | Comma-separated allowed paths |
| `--token` | no | — | Authentication token |
### What gets reported on registration
| Field | Example |
|-------|---------|
| OS / Arch | `darwin/arm64` |
| Shell | `/bin/zsh` |
| Home | `/Users/kl` |
| Installed tools | `go 1.25`, `node v22.13.0`, `git 2.47.1` |
| Allowed paths | `/Users/kl/workspace` |
## Computer Resource
| Operation | Description |
|-----------|-------------|
| Shell execution | Streaming stdout/stderr in real-time |
| File read | With line offset and limit |
| File write | Auto-creates parent directories |
| File edit | Find-and-replace, supports replace_all |
All file operations are restricted to the path allowlist (`--paths`).
## Connection Resilience
Tested and tuned on unreliable networks (ZeroTier with ~10% packet loss):
```
Heartbeat: ──ping──ping──ping──ping──
3s 3s 3s 3s
Pong timeout: 10s without pong → disconnect → reconnect
Reconnect: 1s → 2s → 4s → 8s → 16s → 30s (cap)
└── resets to 1s after 60s stable
```
| Layer | Mechanism | Timeout |
|-------|-----------|---------|
| Application | Ping/Pong heartbeat | 3s interval, 10s deadline |
| HTTP/2 | ReadIdleTimeout | 30s |
| HTTP/2 | PingTimeout | 10s |
| TCP | Dial timeout | 10s |
Each reconnect creates a fresh HTTP/2 transport to avoid reusing broken connections.
## Internals
```
epiral-cli/
├── cmd/epiral/
│ └── main.go # Entry: subcommand dispatch, signals
├── internal/
│ ├── config/
│ │ └── config.go # YAML config load/save/Store
│ ├── daemon/
│ │ ├── daemon.go # Connect, register, heartbeat, dispatch
│ │ ├── manager.go # Daemon lifecycle (start/stop/restart)
│ │ ├── exec.go # Streaming shell execution
│ │ └── fileops.go # Read / write / edit files
│ ├── logger/
│ │ └── logger.go # Ring buffer logging + SSE subscriptions
│ └── webserver/
│ └── server.go # Web panel (REST API + embedded SPA)
├── web/ # React + Vite + Tailwind frontend source
├── proto/epiral/v1/
│ └── epiral.proto # Protocol definition
├── gen/ # Generated protobuf + Connect RPC code
├── Makefile # build · web · generate · lint · check
└── .golangci.yml # 14 linters configured
```
~1500 lines of hand-written Go. The rest is generated.
## Development
```bash
make build # Full build (frontend + Go)
make build-go # Go only (uses existing dist)
make web # Frontend only
make dev # Frontend dev mode (vite dev server)
make check # Format + lint + build (pre-commit)
make generate # Regenerate protobuf code (requires buf)
make clean # Remove build artifacts
```
### Requirements
- Go 1.25+
- Node.js 22+ / pnpm — frontend build
- [buf](https://buf.build/) for protobuf code generation
- [golangci-lint](https://golangci-lint.run/) for linting
## Roadmap
- [x] Computer: shell execution + file operations
- [x] Web management panel (Dashboard / Config / Logs)
- [x] YAML config persistence
- [x] Multi-instance support (`--config` + `--port`)
- [ ] Persistent shell sessions (shell pool)
- [ ] mTLS / token authentication
- [ ] systemd / launchd service files
- [ ] Cross-compilation + GitHub Releases
- [ ] Large file upload/download
## Related
- [Epiral Agent](https://github.com/epiral/agent) — the brain (Node.js)
## License
[BSL 1.1](LICENSE)