@vibecontrols/agent
v2026.513.1
Published
VibeControls Agent CLI - Remote development environment management with terminal multiplexing, tunnels, and extensible plugins. Powered by Bun + Elysia.
Downloads
6,734
Maintainers
Readme
@vibecontrols/agent
Remote development environment management CLI with terminal multiplexing, SSH tunneling, port forwarding, cloudflared integration, and a plugin system.
VibeControls Agent runs as a lightweight local service on your development machine, providing a unified REST API and CLI for managing tmux sessions, SSH connections, cloudflared tunnels, port forwards, background tasks, and more.
Installation
Requires Bun >= 1.3.
Supported operating systems
| OS | Status | Notes |
| ------------------- | -------------- | ------------------------------------------------------------------------- |
| Linux (glibc/musl) | ✅ first-class | systemd vibe autostart install ships out of the box |
| macOS (Apple/Intel) | ✅ first-class | launchd vibe autostart install ships out of the box |
| Windows 10/11 | ✅ supported | Task Scheduler autostart (vibe autostart install → schtasks); see notes |
| WSL2 | ✅ supported | Treated as Linux. systemd-on-WSL needed for autostart |
OS-specific abstraction lives behind src/core/os-adapter.ts (OsAdapter interface with one implementation per platform). Cross-platform pieces:
- Process liveness / kill uses
process.kill(pid, 0)on POSIX and the same on Windows; signal escalation translates totaskkill /T /Fon Windows. - Locating binaries uses
whichon POSIX andwhere.exeon Windows. - Listing child PIDs uses
pgrep -Pon POSIX andwmic process where (ParentProcessId=...)on Windows. - Shell quoting / shell invocation ships POSIX (
/bin/sh -c) and PowerShell (powershell -Command) implementations. - npm / bun spawns use
npm.cmd/bun.exeon Windows andnpm/bunon POSIX.
Windows-specific notes
- Run from PowerShell or Windows Terminal.
cmd.exeworks for basic commands but PowerShell is the tested shell. - Bun on Windows requires Windows 10 (1809+) or Windows 11.
vibe autostart installwrites a Task Scheduler entry under your user account (no admin rights needed). Trigger: logon. Restart on failure: 3 attempts, 1 min apart.vibe tunnel kill --orphanstranslates SIGTERM/SIGKILL intotaskkill /T /F(Windows has no POSIX signals).vibe deploy <ssh-target>works from any OS as long assshis on PATH (built into Windows 10 1809+).
macOS-specific notes
- First-time launch may need to grant Terminal "Full Disk Access" if you store agent state in protected locations like
~/Library. vibe autostart installwrites a launchd plist to~/Library/LaunchAgents/. Enabled withlaunchctl bootstrap gui/<uid>.- Apple Silicon: cloudflared must be the arm64 build. The plugin's
prereqs/installhandles this automatically.
Linux-specific notes
- systemd user services need
loginctl enable-linger <user>for the agent to keep running after logout.vibe autostart installdoes this for you. - On non-systemd distros (Alpine, Void, etc.)
vibe autostartis not yet supported — runvibe startfrom your service supervisor of choice.
WSL-specific notes
- WSL2 reports
linuxas platform. The agent treats it like a Linux install. vibe autostart installrequires systemd in WSL (Windows 11 22H2+ orwsl --update+systemd=truein/etc/wsl.conf).- Tunnels work, but the URL is reachable from the Windows host out of the box.
Global install (default)
npm install -g @vibecontrols/agent
# or
bun install -g @vibecontrols/agentThe vibe binary is on your PATH and plugins install into your global node_modules.
Local install (per-folder)
You can also install the agent into a single folder and keep all plugins scoped to that folder — useful for project-pinned setups, CI runners, multi-tenant machines, or trying things in isolation without touching the global node_modules.
mkdir my-vibe && cd my-vibe
npm install @vibecontrols/agent
# or: bun add @vibecontrols/agent
# Tell the agent to install plugins into this folder, not globally
export VIBECONTROLS_PLUGIN_INSTALL_MODE=local
./node_modules/.bin/vibe startIn local mode, plugins go to <cwd>/.boff/vibecontrols/agents/<agentId>/agent-plugins/node_modules/.
The plugin loader searches both local and global locations on every start, in this priority order:
<agentDir>/agent-plugins/node_modules/(per-agent local)VIBECONTROLS_PLUGIN_ROOTS(colon-separated extra paths)- CWD walk-up
node_modules/ - The agent's own location
- Bun global (
bun pm -g bin) - npm global (
npm root -g)
So you can mix-and-match: a globally installed agent will still pick up plugins dropped into a local folder, and vice versa. VIBECONTROLS_PLUGIN_INSTALL_MODE only controls where new installs land (default: global).
From source (local development)
cd ~/products/vibecontrols/vibecontrols-agent
bun install
# Standard dev (restarts on file change, new tunnel URL each time)
bun run dev
# Hot-reload dev (tunnel URL and API key survive restarts)
bun run dev:reload
# Or with a pinned API key
AGENT_API_KEY=vcak_your_key_here bun run dev:reloadQuick Start
# Start the agent as a background daemon (default)
vibe start
# Or start in foreground for debugging
vibe start --foreground
# Verify it's running
vibe health
vibe statusOn first run, the agent automatically:
- Detects missing system dependencies (tmux, ttyd, cloudflared)
- Installs them via
vibe setup - Detects port conflicts and auto-selects the next available port
Profiles
A profile is a single JSON file holding the gateway URLs, OAuth credentials, API key, and plugin config for one VibeControls environment. Profiles let one agent binary target local / alpha / prod gateways without rebuilds.
Layout:
~/.boff/vibecontrols/
profiles/
local.json # localhost dev URLs
alpha.json # alpha CF tunnel URLs
prod.json # production URLs (default)
<custom>.json # any operator-defined env
active # plain text -- name of the active profileThe active pointer defaults to prod. On first run the agent idempotently:
- Migrates a legacy
~/.boff/vibecontrols/config.jsontoprofiles/default.json(only ifprofiles/does not yet exist). - Creates
local,alpha,prodprofiles when absent (existing user profiles are never overwritten). - Writes
active=prod(ordefaultif a legacy config was migrated) when noactivepointer exists.
You can re-run that pass at any time with vibe profile seed.
Profile commands
vibe profile list # all profiles, active flagged
vibe profile current # name of the active profile
vibe profile show [<name>] # profile JSON (apiKey + secrets redacted)
vibe profile create <name> [--from <existing>] [--use]
# blank, or copy of another profile;
# if <name> is local|alpha|prod the
# built-in URLs are used as the seed
vibe profile use <name> # set the active pointer
vibe profile delete <name> --confirm # refuses if <name> is the active profile
vibe profile seed # idempotent migrate + seed passProfile resolution
getAgentConfig() reads the active pointer, loads profiles/<active>.json, then layers on per-agent config.json overrides and finally environment variables (env wins). No code paths require a rebuild to swap environments -- flip active (or run vibe profile use <name>) and restart.
Concurrent profiles
Each profile runs as its own daemon with its own port and tunnel:
vibe --profile alpha start
vibe --profile dev start
vibe list # both runningREST API for profile management
Frontends can manage profiles directly via the agent's tunnel:
GET /api/profiles # list
POST /api/profiles # create body: { name, gatewayUrl?, workspaceGatewayUrl?, clientId?, clientSecret?, copyFrom?, start? }
GET /api/profiles/:name # single profile + status
PUT /api/profiles/:name # patch config (excludes static-api-key, apiKeys, agentRecordId)
DELETE /api/profiles/:name # body: { confirm: true, keepConfig? }
POST /api/profiles/:name/start # spawn daemon
POST /api/profiles/:name/stop # graceful stop
POST /api/profiles/:name/switch # set as defaultAll data API routes are addressed via the profile prefix:
/api/profiles/<name>/agent/identity
/api/profiles/<name>/sessions/...
/api/profiles/<name>/tasks/...
/api/profiles/<name>/plugins/install
/api/profiles/<name>/agent/gateway-auth
... etc.When <name> matches the running daemon's profile, the request is served. Mismatches return 404 { "error": "profile-mismatch", "running": "<X>", "requested": "<Y>" } so clients can pick the right tunnel.
Bare /api/<rest> paths (no profile prefix) return 410 Gone — there's no longer a "default" data route for ambiguity reasons. Frontend / backend integrations must always carry the profile name in the URL. Discover the running profile via GET /api/profiles first.
Exempt from the rule (no profile prefix needed):
/health— process liveness/api/profiles[/:name][/...]— profile management/ui/,/ws/,/terminal/,/code-server/— websocket/UI bridges
Configuration
Create a .env file in your working directory or set environment variables:
| Variable | Description | Default |
| ---------------------------------- | -------------------------------------------------------------- | -------------------------------------------- |
| PORT | Agent server port | 3005 |
| NODE_ENV | Environment | development |
| CORS_ORIGIN | Allowed CORS origin | http://localhost:3000 |
| AGENT_API_KEY | Static API key (optional) | (auto-generated) |
| DB_PATH | SQLite database path | .boff/vibecontrols/agents/default/agent.db |
| AGENT_TUNNEL | Auto-start cloudflared tunnel | true |
| VIBECONTROLS_HOME | Override product state dir | .boff/vibecontrols/ |
| VIBECONTROLS_PROFILE | Profile name for isolated config | default |
| VIBECONTROLS_REGISTRY | Override plugin registry | https://registry.npmjs.org/ |
| VIBECONTROLS_PLUGIN_INSTALL_MODE | Where new plugins are installed: global or local | global |
| VIBECONTROLS_PLUGIN_ROOTS | Extra colon-separated node_modules roots to scan for plugins | (unset) |
Static API Key
By default, the agent generates a new API key on each restart. To persist one:
# Auto-generate and save a persistent key
vibe config --set static-api-key=true
# Or set an explicit key
vibe config --set static-api-key=my-secret-key-hereThe key is stored in .boff/vibecontrols/agents/{agentId}/config.json (relative to the directory where vibe start was run).
An .env.example file is included in the package for reference.
Command Reference
Core Commands
vibe start # Start as daemon (default)
vibe start --foreground # Start in foreground
vibe start -p 4000 # Start on custom port
vibe start -n myagent # Start with instance name
vibe start --db-path ~/my.db # Custom database path
vibe stop # Stop default instance
vibe stop -n myagent # Stop named instance
vibe stop --all # Stop all instances
vibe restart # Restart default instance
vibe restart -n myagent # Restart named instance
vibe status # Show all instance statuses
vibe status -n myagent # Show specific instance status
vibe list # List all instances
vibe kill # Force kill default instance
vibe kill --all # Force kill all
vibe logs # Show logs (default instance)
vibe logs -f # Follow logs (tail -f)
vibe logs --tail 50 # Last 50 lines
vibe health # Health check via HTTP
vibe info # Detailed version/system info
vibe key # Display the agent API key
vibe system # System info from running agent
vibe update # Update to latest version
vibe update --check # Check for updates without installingSetup & Configuration
vibe setup # Install dependencies (tmux, ttyd, cloudflared)
vibe setup --check # Check without installing
vibe config --list # List all config values
vibe config --get <key> # Get a specific config value
vibe config --set key=value # Set a config valuePlugin Management
Extend the agent with plugins that add CLI commands, routes, or both.
vibe plugin list # List installed plugins
vibe plugin install @vibecontrols/vibe-plugin-ssh # Install a plugin
vibe plugin install @vibecontrols/vibe-plugin-ai # Install AI tools plugin
vibe plugin remove @vibecontrols/vibe-plugin-ssh # Remove a plugin
vibe plugin create my-plugin # Scaffold a new plugin
vibe plugin create my-plugin --with-ui # Scaffold with React UI
vibe plugin create my-plugin --tag provider # Set plugin tagAvailable Plugins:
| Plugin | Description |
| --------------------------------------------- | ----------------------------------------------------- |
| @vibecontrols/vibe-plugin-ssh | SSH connections & port forwarding |
| @vibecontrols/vibe-plugin-ai | AI tool detection & prompt management (SQLite + REST) |
| @vibecontrols/vibe-plugin-session-tmux | tmux-based session provider |
| @vibecontrols/vibe-plugin-tunnel-cloudflare | Cloudflare tunnel provider |
| @vibecontrols/vibe-plugin-ui-ssh | Web UI for SSH management |
| @vibecontrols/vibe-plugin-ui-ai | Web UI for AI tools & prompt templates |
Tunnel Management
Manage cloudflared tunnels that expose local ports via public URLs.
# Per-port tunnels
vibe tunnel list # List all tunnels
vibe tunnel status # Overview (total, active, inactive, errored)
vibe tunnel start -p 8080 # Expose local port 8080
vibe tunnel start -p 3000 -s myapp # With preferred subdomain
vibe tunnel stop -i <id> # Stop a tunnel
vibe tunnel delete -i <id> # Delete a tunnel
# Agent tunnel (main agent cloudflared tunnel)
vibe tunnel agent # Show agent tunnel status
vibe tunnel agent --start # Start agent tunnel
vibe tunnel agent --stop # Stop agent tunnel
# Orphan handling — see "Tunnel survives the agent" below
vibe tunnel kill --orphans # Kill any tunnel process whose agent is goneTunnel survives the agent
The agent tunnel (cloudflared / frpc) is spawned as an independent OS process so a hard kill -9 of the agent does not drop the public URL — that's intentional. The trade-off: when the agent is gone, you have nothing to talk to over REST, so the standard vibe tunnel agent command can't tell you what's still running.
To work around that, every tunnel start writes <agentDir>/tunnel.state.json with the PID, URL, provider, and start time. The CLI uses that file as a fallback:
# Works even after `kill -9 <agent-pid>` — reads tunnel.state.json
vibe tunnel agent
# Output when the agent is dead but cloudflared is still running:
# Status: orphan
# Public URL: https://random-words-123.trycloudflare.com
# PID: 48211
# Provider: bootstrap-cloudflared
# Reap the orphan:
vibe tunnel kill --orphans # SIGTERM, then SIGKILL after 3s
vibe tunnel kill --orphans --force # SIGKILL immediatelyIf you prefer the unix-y route:
# Find it manually
cat ./.boff/vibecontrols/agents/default/tunnel.state.json
ps -p $(jq -r .pid ./.boff/vibecontrols/agents/default/tunnel.state.json)
# Kill it manually
pkill -f "cloudflared tunnel --url"Stale state files (where the recorded PID is no longer alive) are cleared automatically on the next vibe tunnel agent call.
Terminal Sessions (tmux)
vibe session list # List managed sessions
vibe session list --system # Include unmanaged tmux sessions
vibe session create --name dev # Create session named "dev"
vibe session create --name dev --cwd ~/project --command "npm run dev"
vibe session kill -i <id> # Kill a session
vibe session exec -i <id> -c "ls -la" # Run command in session
vibe session capture -i <id> # Capture session outputSSH Connections
vibe ssh list # List saved connections
vibe ssh add --name prod \
--host 10.0.1.50 \
--user deploy \
--port 22 \
--key ~/.ssh/id_rsa # Add a connection
vibe ssh remove -i <id> # Remove a connection
vibe ssh test -i <id> # Test connectivity
vibe ssh exec -i <id> -c "uptime" # Execute remote commandPort Forwarding
vibe forward list # List all forwards
vibe forward create \
--local 5432 \
--remote-host db.internal \
--remote-port 5432 \
--server prod # Create a forward rule
vibe forward start -i <id> # Start forwarding
vibe forward stop -i <id> # Stop forwarding
vibe forward delete -i <id> # Delete forward ruleBackground Tasks
vibe task list # List all tasks
vibe task list --status running # Filter by status
vibe task run -c "npm test" # Run command as background task
vibe task run -c "make build" --cwd ~/projectNotifications
vibe notify list # List all notifications
vibe notify list --unread # Show only unread
vibe notify read-all # Mark all as readGlobal Options
Every subcommand that communicates with a running agent accepts:
--agent-url <url> # Default: http://localhost:3005This lets you manage remote agents or agents on non-default ports:
vibe health --agent-url http://192.168.1.10:3005
vibe tunnel list --agent-url http://localhost:4000Architecture
The agent runs as a Bun + Elysia HTTP server with:
- bun:sqlite database for local state (sessions, connections, tunnels, tasks, config)
- tmux integration for terminal multiplexing
- cloudflared for secure tunnel management
- Plugin system for extensibility (SSH, AI tools, UI plugins, and custom plugins)
- WebSocket proxy for real-time terminal streaming
- API key authentication on all endpoints
- Daemon mode by default with proper process management
System Dependencies
| Tool | Purpose | Required | | ----------- | --------------------- | ----------- | | Bun >= 1.3 | Runtime | Yes | | tmux | Terminal multiplexing | Yes | | ttyd | Web-based terminal | Recommended | | cloudflared | Tunnel management | For tunnels |
Run vibe setup --check to verify all dependencies.
REST API
When the agent is running, it exposes a REST API at http://localhost:3005/api/ with 112 endpoints across these route groups:
/api/tmux/*- Terminal session management/api/ssh/*- SSH connection management/api/tunnel/*- Per-port tunnel CRUD/api/agent-tunnel/*- Agent tunnel management/api/port-forward/*- Port forwarding/api/tasks/*- Background task management/api/config/*- Configuration & system info/api/notifications/*- Notification management/api/files/*- File operations/api/git/*- Git repository scanning/api/bookmarks/*- Command bookmarks/api/projects/*- Project scanning/api/ai/tools- AI tool detection/api/ai/prompts/*- Prompt template CRUD (via AI plugin)
Authentication: Include x-agent-api-key header with your API key.
Examples
Development Workflow
# Start agent (daemon mode is the default)
vibe start
# Set up a dev session
vibe session create --name frontend --cwd ~/app --command "npm run dev"
vibe session create --name backend --cwd ~/api --command "npm start"
# Expose your dev server publicly
vibe tunnel start -p 3000
# Check everything is running
vibe session list
vibe tunnel list
vibe health
# Later, clean up
vibe stopRemote Server Management
# Save SSH connections
vibe ssh add --name staging --host staging.example.com --user deploy --key ~/.ssh/deploy
vibe ssh add --name production --host prod.example.com --user deploy --key ~/.ssh/deploy
# Test connections
vibe ssh test -i <staging-id>
# Run remote commands
vibe ssh exec -i <staging-id> -c "docker ps"
vibe ssh exec -i <production-id> -c "systemctl status nginx"
# Set up port forwarding to access remote databases
vibe forward create --local 5432 --remote-host localhost --remote-port 5432 --server staging
vibe forward start -i <forward-id>Part of the VibeControls Ecosystem
This agent is the local component of the VibeControls development environment platform. It can operate standalone or connect to the VibeControls backend for centralized management across multiple machines.
Author
Vignesh T.V. (@tvvignesh) [email protected]
License
Proprietary - Copyright 2025-2026 Burdenoff Consultancy Services Pvt. Ltd. All rights reserved.
See LICENSE for details.
