@vibecontrols/agent 2026.601.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +558 -0
- package/dist/agent-config-85pskv43.js +2 -0
- package/dist/agent-ready-tracker-zp6p8e6f.js +2 -0
- package/dist/app-yvjadjmh.js +2 -0
- package/dist/bootstrap-workspace-zpm20zez.js +2 -0
- package/dist/bootstrap.service-pjmnpxha.js +2 -0
- package/dist/bridge-client-341r9rry.js +2 -0
- package/dist/cli.js +5 -0
- package/dist/daemon-profile-vas1vf2t.js +2 -0
- package/dist/esm-9fpye77x.js +2 -0
- package/dist/finalize-retry-handle-registry-vv241fsq.js +2 -0
- package/dist/finalize-retry-worker-xp1nhv3c.js +2 -0
- package/dist/gateway-client-43gzvj5s.js +2 -0
- package/dist/getMachineId-bsd-a56s0v8c.js +2 -0
- package/dist/getMachineId-darwin-w9k0yw9r.js +3 -0
- package/dist/getMachineId-linux-anh31jbf.js +2 -0
- package/dist/getMachineId-unsupported-5hv3pwca.js +2 -0
- package/dist/getMachineId-win-njb8tery.js +2 -0
- package/dist/highlights-8d9mgr01.js +2 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-jwvdxm9x.js +2 -0
- package/dist/highlights-qbx2vnme.js +2 -0
- package/dist/highlights-r3m83kn9.js +2 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-s7mqapt6.js +2 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index-01qzsnwd.js +16 -0
- package/dist/index-0248afsn.js +3 -0
- package/dist/index-04n4qgvd.js +407 -0
- package/dist/index-0ckffygp.js +5 -0
- package/dist/index-0cn9bv8z.js +4 -0
- package/dist/index-1hnw0rhc.js +178 -0
- package/dist/index-1zw3kea7.js +10 -0
- package/dist/index-2gsarrbn.js +4 -0
- package/dist/index-2xs9cvjn.js +28 -0
- package/dist/index-3ys16efc.js +231 -0
- package/dist/index-4wgjx8bf.js +3 -0
- package/dist/index-52cp759f.js +3 -0
- package/dist/index-5mw3eshk.js +4 -0
- package/dist/index-678rwfc0.js +5 -0
- package/dist/index-6jq17k9s.js +7 -0
- package/dist/index-6jzsthh9.js +3 -0
- package/dist/index-6mprnf7p.js +9 -0
- package/dist/index-8kvc8ttn.js +15 -0
- package/dist/index-8sm0nkh8.js +3 -0
- package/dist/index-9bqd8veb.js +21 -0
- package/dist/index-b5dhmybd.js +4 -0
- package/dist/index-c58g96mb.js +26 -0
- package/dist/index-cs78wq6y.js +3 -0
- package/dist/index-d5ysy1yn.js +3 -0
- package/dist/index-dm6yjmgq.js +3 -0
- package/dist/index-e1bw1bwr.js +4 -0
- package/dist/index-ef95xr4z.js +9 -0
- package/dist/index-g2raeeh4.js +11 -0
- package/dist/index-g3ap3xpr.js +5 -0
- package/dist/index-g8zv1gta.js +17 -0
- package/dist/index-h8a8s8sn.js +3 -0
- package/dist/index-hrdamx5j.js +2 -0
- package/dist/index-jw1k4vbk.js +3 -0
- package/dist/index-mtm8cfyt.js +158 -0
- package/dist/index-mxc61yr1.js +3 -0
- package/dist/index-n7qyrdr1.js +3 -0
- package/dist/index-qfz9fy56.js +3 -0
- package/dist/index-rc79x8fw.js +3 -0
- package/dist/index-rdp5xq4r.js +15 -0
- package/dist/index-scsjyj4m.js +171 -0
- package/dist/index-ssjmzqcz.js +13 -0
- package/dist/index-tmrbs96r.js +11 -0
- package/dist/index-tp4y9jde.js +83 -0
- package/dist/index-v9fx5wab.js +83 -0
- package/dist/index-vdahdt49.js +2 -0
- package/dist/index-x1h8r7pr.js +3 -0
- package/dist/index-xjzmb1pn.js +3 -0
- package/dist/index-yrgm89r8.js +3 -0
- package/dist/index-yy1mm8zs.js +3 -0
- package/dist/index-z5a4yxzz.js +8 -0
- package/dist/index.js +5 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/injections-srewsjcz.js +2 -0
- package/dist/interactive-22ta89hc.js +2 -0
- package/dist/key.cmd-wgcq6kt8.js +2 -0
- package/dist/log-shipper-k24m8yw5.js +2 -0
- package/dist/path-utils-35re7qf9.js +2 -0
- package/dist/plugin-system-c916v9an.js +2 -0
- package/dist/postinstall-shim-fix.cjs +382 -0
- package/dist/prereqs-runner-ca4kt803.js +2 -0
- package/dist/preuninstall.cjs +254 -0
- package/dist/profile-mount-npcknw6v.js +2 -0
- package/dist/prune-stale-shims-nkx9vq5m.js +2 -0
- package/dist/register-core-qrawzyym.js +2 -0
- package/dist/secondary-profile-attach-db5cr3e1.js +2 -0
- package/dist/subprocess-g9sk1ep9.js +2 -0
- package/dist/telemetry-tnq47dcs.js +2 -0
- package/dist/tree-sitter-javascript-3h25c6bs.js +2 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-3nemcjhe.js +2 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-16ftwa53.js +2 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-f6mq6ze6.js +2 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/tree-sitter-zig-s2trkm2d.js +2 -0
- package/dist/tunnel-bootstrap-2kg79ng8.js +2 -0
- package/package.json +122 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Burdenoff Consultancy Services Pvt. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the proprietary
|
|
6
|
+
property of Burdenoff Consultancy Services Pvt. Ltd. and its affiliates.
|
|
7
|
+
|
|
8
|
+
UNAUTHORIZED COPYING, MODIFICATION, DISTRIBUTION, OR USE OF THIS SOFTWARE, VIA ANY
|
|
9
|
+
MEDIUM, IS STRICTLY PROHIBITED.
|
|
10
|
+
|
|
11
|
+
The Software is provided under a proprietary license. You may use the Software only in
|
|
12
|
+
accordance with the terms of your license agreement with Burdenoff Consultancy Services
|
|
13
|
+
Pvt. Ltd.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
16
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
17
|
+
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
|
19
|
+
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
|
22
|
+
For licensing inquiries, contact: contact@vibecontrols.com
|
package/README.md
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
# @vibecontrols/agent
|
|
2
|
+
|
|
3
|
+
Remote development environment management CLI with terminal multiplexing, SSH tunneling, port forwarding, cloudflared integration, and a plugin system.
|
|
4
|
+
|
|
5
|
+
**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.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Requires **Bun >= 1.3**.
|
|
10
|
+
|
|
11
|
+
### Supported operating systems
|
|
12
|
+
|
|
13
|
+
| OS | Status | Notes |
|
|
14
|
+
| ------------------- | -------------- | ------------------------------------------------------------------------- |
|
|
15
|
+
| Linux (glibc/musl) | ✅ first-class | systemd `vibe autostart install` ships out of the box |
|
|
16
|
+
| macOS (Apple/Intel) | ✅ first-class | launchd `vibe autostart install` ships out of the box |
|
|
17
|
+
| Windows 10/11 | ✅ supported | Task Scheduler autostart (`vibe autostart install` → schtasks); see notes |
|
|
18
|
+
| WSL2 | ✅ supported | Treated as Linux. systemd-on-WSL needed for autostart |
|
|
19
|
+
|
|
20
|
+
OS-specific abstraction lives behind `src/core/os-adapter.ts` (`OsAdapter` interface with one implementation per platform). Cross-platform pieces:
|
|
21
|
+
|
|
22
|
+
- **Process liveness / kill** uses `process.kill(pid, 0)` on POSIX and the same on Windows; signal escalation translates to `taskkill /T /F` on Windows.
|
|
23
|
+
- **Locating binaries** uses `which` on POSIX and `where.exe` on Windows.
|
|
24
|
+
- **Listing child PIDs** uses `pgrep -P` on POSIX and `wmic process where (ParentProcessId=...)` on Windows.
|
|
25
|
+
- **Shell quoting / shell invocation** ships POSIX (`/bin/sh -c`) and PowerShell (`powershell -Command`) implementations.
|
|
26
|
+
- **npm / bun spawns** use `npm.cmd`/`bun.exe` on Windows and `npm`/`bun` on POSIX.
|
|
27
|
+
|
|
28
|
+
#### Windows-specific notes
|
|
29
|
+
|
|
30
|
+
- Run from PowerShell or Windows Terminal. `cmd.exe` works for basic commands but PowerShell is the tested shell.
|
|
31
|
+
- Bun on Windows requires Windows 10 (1809+) or Windows 11.
|
|
32
|
+
- `vibe autostart install` writes a Task Scheduler entry under your user account (no admin rights needed). Trigger: logon. Restart on failure: 3 attempts, 1 min apart.
|
|
33
|
+
- `vibe tunnel kill --orphans` translates SIGTERM/SIGKILL into `taskkill /T /F` (Windows has no POSIX signals).
|
|
34
|
+
- `vibe deploy <ssh-target>` works from any OS as long as `ssh` is on PATH (built into Windows 10 1809+).
|
|
35
|
+
|
|
36
|
+
#### macOS-specific notes
|
|
37
|
+
|
|
38
|
+
- First-time launch may need to grant Terminal "Full Disk Access" if you store agent state in protected locations like `~/Library`.
|
|
39
|
+
- `vibe autostart install` writes a launchd plist to `~/Library/LaunchAgents/`. Enabled with `launchctl bootstrap gui/<uid>`.
|
|
40
|
+
- Apple Silicon: cloudflared must be the arm64 build. The plugin's `prereqs/install` handles this automatically.
|
|
41
|
+
|
|
42
|
+
#### Linux-specific notes
|
|
43
|
+
|
|
44
|
+
- systemd user services need `loginctl enable-linger <user>` for the agent to keep running after logout. `vibe autostart install` does this for you.
|
|
45
|
+
- On non-systemd distros (Alpine, Void, etc.) `vibe autostart` is not yet supported — run `vibe start` from your service supervisor of choice.
|
|
46
|
+
|
|
47
|
+
#### WSL-specific notes
|
|
48
|
+
|
|
49
|
+
- WSL2 reports `linux` as platform. The agent treats it like a Linux install.
|
|
50
|
+
- `vibe autostart install` requires systemd in WSL (Windows 11 22H2+ or `wsl --update` + `systemd=true` in `/etc/wsl.conf`).
|
|
51
|
+
- Tunnels work, but the URL is reachable from the Windows host out of the box.
|
|
52
|
+
|
|
53
|
+
### Global install (default)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install -g @vibecontrols/agent
|
|
57
|
+
# or
|
|
58
|
+
bun install -g @vibecontrols/agent
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The `vibe` binary is on your PATH and plugins install into your global `node_modules`.
|
|
62
|
+
|
|
63
|
+
### Local install (per-folder)
|
|
64
|
+
|
|
65
|
+
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`.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
mkdir my-vibe && cd my-vibe
|
|
69
|
+
npm install @vibecontrols/agent
|
|
70
|
+
# or: bun add @vibecontrols/agent
|
|
71
|
+
|
|
72
|
+
# Tell the agent to install plugins into this folder, not globally
|
|
73
|
+
export VIBECONTROLS_PLUGIN_INSTALL_MODE=local
|
|
74
|
+
|
|
75
|
+
./node_modules/.bin/vibe start
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In `local` mode, plugins go to `<cwd>/.boff/vibecontrols/agents/<agentId>/agent-plugins/node_modules/`.
|
|
79
|
+
|
|
80
|
+
The plugin loader searches **both** local and global locations on every start, in this priority order:
|
|
81
|
+
|
|
82
|
+
1. `<agentDir>/agent-plugins/node_modules/` (per-agent local)
|
|
83
|
+
2. `VIBECONTROLS_PLUGIN_ROOTS` (colon-separated extra paths)
|
|
84
|
+
3. CWD walk-up `node_modules/`
|
|
85
|
+
4. The agent's own location
|
|
86
|
+
5. Bun global (`bun pm -g bin`)
|
|
87
|
+
6. npm global (`npm root -g`)
|
|
88
|
+
|
|
89
|
+
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`).
|
|
90
|
+
|
|
91
|
+
### From source (local development)
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd ~/products/vibecontrols/vibecontrols-agent
|
|
95
|
+
bun install
|
|
96
|
+
|
|
97
|
+
# Standard dev (restarts on file change, new tunnel URL each time)
|
|
98
|
+
bun run dev
|
|
99
|
+
|
|
100
|
+
# Hot-reload dev (tunnel URL and API key survive restarts)
|
|
101
|
+
bun run dev:reload
|
|
102
|
+
|
|
103
|
+
# Or with a pinned API key
|
|
104
|
+
AGENT_API_KEY=vcak_your_key_here bun run dev:reload
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Quick Start
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Start the agent as a background daemon (default)
|
|
111
|
+
vibe start
|
|
112
|
+
|
|
113
|
+
# Or start in foreground for debugging
|
|
114
|
+
vibe start --foreground
|
|
115
|
+
|
|
116
|
+
# Verify it's running
|
|
117
|
+
vibe health
|
|
118
|
+
vibe status
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
On **first run**, the agent automatically:
|
|
122
|
+
|
|
123
|
+
1. Detects missing system dependencies (tmux, ttyd, cloudflared)
|
|
124
|
+
2. Installs them via `vibe setup`
|
|
125
|
+
3. Detects port conflicts and auto-selects the next available port
|
|
126
|
+
|
|
127
|
+
## Profiles
|
|
128
|
+
|
|
129
|
+
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.
|
|
130
|
+
|
|
131
|
+
Layout:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
~/.boff/vibecontrols/
|
|
135
|
+
profiles/
|
|
136
|
+
local.json # localhost dev URLs
|
|
137
|
+
alpha.json # alpha CF tunnel URLs
|
|
138
|
+
prod.json # production URLs (default)
|
|
139
|
+
<custom>.json # any operator-defined env
|
|
140
|
+
active # plain text -- name of the active profile
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The `active` pointer defaults to `prod`. On first run the agent idempotently:
|
|
144
|
+
|
|
145
|
+
1. Migrates a legacy `~/.boff/vibecontrols/config.json` to `profiles/default.json` (only if `profiles/` does not yet exist).
|
|
146
|
+
2. Creates `local`, `alpha`, `prod` profiles when absent (existing user profiles are never overwritten).
|
|
147
|
+
3. Writes `active=prod` (or `default` if a legacy config was migrated) when no `active` pointer exists.
|
|
148
|
+
|
|
149
|
+
You can re-run that pass at any time with `vibe profile seed`.
|
|
150
|
+
|
|
151
|
+
### Profile commands
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
vibe profile list # all profiles, active flagged
|
|
155
|
+
vibe profile current # name of the active profile
|
|
156
|
+
vibe profile show [<name>] # profile JSON (apiKey + secrets redacted)
|
|
157
|
+
vibe profile create <name> [--from <existing>] [--use]
|
|
158
|
+
# blank, or copy of another profile;
|
|
159
|
+
# if <name> is local|alpha|prod the
|
|
160
|
+
# built-in URLs are used as the seed
|
|
161
|
+
vibe profile use <name> # set the active pointer
|
|
162
|
+
vibe profile delete <name> --confirm # refuses if <name> is the active profile
|
|
163
|
+
vibe profile seed # idempotent migrate + seed pass
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Profile resolution
|
|
167
|
+
|
|
168
|
+
`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.
|
|
169
|
+
|
|
170
|
+
### Concurrent profiles
|
|
171
|
+
|
|
172
|
+
Each profile runs as its own daemon with its own port and tunnel:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
vibe --profile alpha start
|
|
176
|
+
vibe --profile dev start
|
|
177
|
+
vibe list # both running
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### REST API for profile management
|
|
181
|
+
|
|
182
|
+
Frontends can manage profiles directly via the agent's tunnel:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
GET /api/profiles # list
|
|
186
|
+
POST /api/profiles # create body: { name, gatewayUrl?, workspaceGatewayUrl?, clientId?, clientSecret?, copyFrom?, start? }
|
|
187
|
+
GET /api/profiles/:name # single profile + status
|
|
188
|
+
PUT /api/profiles/:name # patch config (excludes static-api-key, apiKeys, agentRecordId)
|
|
189
|
+
DELETE /api/profiles/:name # body: { confirm: true, keepConfig? }
|
|
190
|
+
POST /api/profiles/:name/start # spawn daemon
|
|
191
|
+
POST /api/profiles/:name/stop # graceful stop
|
|
192
|
+
POST /api/profiles/:name/switch # set as default
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
All data API routes are addressed via the profile prefix:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
/api/profiles/<name>/agent/identity
|
|
199
|
+
/api/profiles/<name>/sessions/...
|
|
200
|
+
/api/profiles/<name>/tasks/...
|
|
201
|
+
/api/profiles/<name>/plugins/install
|
|
202
|
+
/api/profiles/<name>/agent/gateway-auth
|
|
203
|
+
... etc.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
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.
|
|
207
|
+
|
|
208
|
+
**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.
|
|
209
|
+
|
|
210
|
+
Exempt from the rule (no profile prefix needed):
|
|
211
|
+
|
|
212
|
+
- `/health` — process liveness
|
|
213
|
+
- `/api/profiles[/:name][/...]` — profile management
|
|
214
|
+
- `/ui/`, `/ws/`, `/terminal/`, `/code-server/` — websocket/UI bridges
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
Create a `.env` file in your working directory or set environment variables:
|
|
219
|
+
|
|
220
|
+
| Variable | Description | Default |
|
|
221
|
+
| ---------------------------------- | -------------------------------------------------------------- | -------------------------------------------- |
|
|
222
|
+
| `PORT` | Agent server port | `3005` |
|
|
223
|
+
| `NODE_ENV` | Environment | `development` |
|
|
224
|
+
| `CORS_ORIGIN` | Allowed CORS origin | `http://localhost:3000` |
|
|
225
|
+
| `AGENT_API_KEY` | Static API key (optional) | (auto-generated) |
|
|
226
|
+
| `DB_PATH` | SQLite database path | `.boff/vibecontrols/agents/default/agent.db` |
|
|
227
|
+
| `AGENT_TUNNEL` | Auto-start cloudflared tunnel | `true` |
|
|
228
|
+
| `VIBECONTROLS_HOME` | Override product state dir | `.boff/vibecontrols/` |
|
|
229
|
+
| `VIBECONTROLS_PROFILE` | Profile name for isolated config | `default` |
|
|
230
|
+
| `VIBECONTROLS_REGISTRY` | Override plugin registry | `https://registry.npmjs.org/` |
|
|
231
|
+
| `VIBECONTROLS_PLUGIN_INSTALL_MODE` | Where new plugins are installed: `global` or `local` | `global` |
|
|
232
|
+
| `VIBECONTROLS_PLUGIN_ROOTS` | Extra colon-separated `node_modules` roots to scan for plugins | (unset) |
|
|
233
|
+
|
|
234
|
+
### Static API Key
|
|
235
|
+
|
|
236
|
+
By default, the agent generates a new API key on each restart. To persist one:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Auto-generate and save a persistent key
|
|
240
|
+
vibe config --set static-api-key=true
|
|
241
|
+
|
|
242
|
+
# Or set an explicit key
|
|
243
|
+
vibe config --set static-api-key=my-secret-key-here
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The key is stored in `.boff/vibecontrols/agents/{agentId}/config.json` (relative to the directory where `vibe start` was run).
|
|
247
|
+
|
|
248
|
+
An `.env.example` file is included in the package for reference.
|
|
249
|
+
|
|
250
|
+
## Command Reference
|
|
251
|
+
|
|
252
|
+
### Core Commands
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
vibe start # Start as daemon (default)
|
|
256
|
+
vibe start --foreground # Start in foreground
|
|
257
|
+
vibe start -p 4000 # Start on custom port
|
|
258
|
+
vibe start -n myagent # Start with instance name
|
|
259
|
+
vibe start --db-path ~/my.db # Custom database path
|
|
260
|
+
|
|
261
|
+
vibe stop # Stop default instance
|
|
262
|
+
vibe stop -n myagent # Stop named instance
|
|
263
|
+
vibe stop --all # Stop all instances
|
|
264
|
+
|
|
265
|
+
vibe restart # Restart default instance
|
|
266
|
+
vibe restart -n myagent # Restart named instance
|
|
267
|
+
|
|
268
|
+
vibe status # Show all instance statuses
|
|
269
|
+
vibe status -n myagent # Show specific instance status
|
|
270
|
+
|
|
271
|
+
vibe list # List all instances
|
|
272
|
+
vibe kill # Force kill default instance
|
|
273
|
+
vibe kill --all # Force kill all
|
|
274
|
+
|
|
275
|
+
vibe logs # Show logs (default instance)
|
|
276
|
+
vibe logs -f # Follow logs (tail -f)
|
|
277
|
+
vibe logs --tail 50 # Last 50 lines
|
|
278
|
+
|
|
279
|
+
vibe health # Health check via HTTP
|
|
280
|
+
vibe info # Detailed version/system info
|
|
281
|
+
vibe key # Display the agent API key
|
|
282
|
+
vibe system # System info from running agent
|
|
283
|
+
|
|
284
|
+
vibe update # Update to latest version
|
|
285
|
+
vibe update --check # Check for updates without installing
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Setup & Configuration
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
vibe setup # Install dependencies (tmux, ttyd, cloudflared)
|
|
292
|
+
vibe setup --check # Check without installing
|
|
293
|
+
|
|
294
|
+
vibe config --list # List all config values
|
|
295
|
+
vibe config --get <key> # Get a specific config value
|
|
296
|
+
vibe config --set key=value # Set a config value
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Plugin Management
|
|
300
|
+
|
|
301
|
+
Extend the agent with plugins that add CLI commands, routes, or both.
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
vibe plugin list # List installed plugins
|
|
305
|
+
vibe plugin install @vibecontrols/vibe-plugin-ssh # Install a plugin
|
|
306
|
+
vibe plugin install @vibecontrols/vibe-plugin-ai # Install AI tools plugin
|
|
307
|
+
vibe plugin remove @vibecontrols/vibe-plugin-ssh # Remove a plugin
|
|
308
|
+
vibe plugin create my-plugin # Scaffold a new plugin
|
|
309
|
+
vibe plugin create my-plugin --with-ui # Scaffold with React UI
|
|
310
|
+
vibe plugin create my-plugin --tag provider # Set plugin tag
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Available Plugins:**
|
|
314
|
+
|
|
315
|
+
| Plugin | Description |
|
|
316
|
+
| --------------------------------------------- | ----------------------------------------------------- |
|
|
317
|
+
| `@vibecontrols/vibe-plugin-ssh` | SSH connections & port forwarding |
|
|
318
|
+
| `@vibecontrols/vibe-plugin-ai` | AI tool detection & prompt management (SQLite + REST) |
|
|
319
|
+
| `@vibecontrols/vibe-plugin-session-tmux` | tmux-based session provider |
|
|
320
|
+
| `@vibecontrols/vibe-plugin-tunnel-cloudflare` | Cloudflare tunnel provider |
|
|
321
|
+
| `@vibecontrols/vibe-plugin-ui-ssh` | Web UI for SSH management |
|
|
322
|
+
| `@vibecontrols/vibe-plugin-ui-ai` | Web UI for AI tools & prompt templates |
|
|
323
|
+
|
|
324
|
+
### Tunnel Management
|
|
325
|
+
|
|
326
|
+
Manage cloudflared tunnels that expose local ports via public URLs.
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# Per-port tunnels
|
|
330
|
+
vibe tunnel list # List all tunnels
|
|
331
|
+
vibe tunnel status # Overview (total, active, inactive, errored)
|
|
332
|
+
vibe tunnel start -p 8080 # Expose local port 8080
|
|
333
|
+
vibe tunnel start -p 3000 -s myapp # With preferred subdomain
|
|
334
|
+
vibe tunnel stop -i <id> # Stop a tunnel
|
|
335
|
+
vibe tunnel delete -i <id> # Delete a tunnel
|
|
336
|
+
|
|
337
|
+
# Agent tunnel (main agent cloudflared tunnel)
|
|
338
|
+
vibe tunnel agent # Show agent tunnel status
|
|
339
|
+
vibe tunnel agent --start # Start agent tunnel
|
|
340
|
+
vibe tunnel agent --stop # Stop agent tunnel
|
|
341
|
+
|
|
342
|
+
# Orphan handling — see "Tunnel survives the agent" below
|
|
343
|
+
vibe tunnel kill --orphans # Kill any tunnel process whose agent is gone
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Tunnel survives the agent
|
|
347
|
+
|
|
348
|
+
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.
|
|
349
|
+
|
|
350
|
+
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:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# Works even after `kill -9 <agent-pid>` — reads tunnel.state.json
|
|
354
|
+
vibe tunnel agent
|
|
355
|
+
|
|
356
|
+
# Output when the agent is dead but cloudflared is still running:
|
|
357
|
+
# Status: orphan
|
|
358
|
+
# Public URL: https://random-words-123.trycloudflare.com
|
|
359
|
+
# PID: 48211
|
|
360
|
+
# Provider: bootstrap-cloudflared
|
|
361
|
+
|
|
362
|
+
# Reap the orphan:
|
|
363
|
+
vibe tunnel kill --orphans # SIGTERM, then SIGKILL after 3s
|
|
364
|
+
vibe tunnel kill --orphans --force # SIGKILL immediately
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
If you prefer the unix-y route:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Find it manually
|
|
371
|
+
cat ./.boff/vibecontrols/agents/default/tunnel.state.json
|
|
372
|
+
ps -p $(jq -r .pid ./.boff/vibecontrols/agents/default/tunnel.state.json)
|
|
373
|
+
|
|
374
|
+
# Kill it manually
|
|
375
|
+
pkill -f "cloudflared tunnel --url"
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Stale state files (where the recorded PID is no longer alive) are cleared automatically on the next `vibe tunnel agent` call.
|
|
379
|
+
|
|
380
|
+
### Terminal Sessions (tmux)
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
vibe session list # List managed sessions
|
|
384
|
+
vibe session list --system # Include unmanaged tmux sessions
|
|
385
|
+
|
|
386
|
+
vibe session create --name dev # Create session named "dev"
|
|
387
|
+
vibe session create --name dev --cwd ~/project --command "npm run dev"
|
|
388
|
+
|
|
389
|
+
vibe session kill -i <id> # Kill a session
|
|
390
|
+
vibe session exec -i <id> -c "ls -la" # Run command in session
|
|
391
|
+
vibe session capture -i <id> # Capture session output
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### SSH Connections
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
vibe ssh list # List saved connections
|
|
398
|
+
|
|
399
|
+
vibe ssh add --name prod \
|
|
400
|
+
--host 10.0.1.50 \
|
|
401
|
+
--user deploy \
|
|
402
|
+
--port 22 \
|
|
403
|
+
--key ~/.ssh/id_rsa # Add a connection
|
|
404
|
+
|
|
405
|
+
vibe ssh remove -i <id> # Remove a connection
|
|
406
|
+
vibe ssh test -i <id> # Test connectivity
|
|
407
|
+
vibe ssh exec -i <id> -c "uptime" # Execute remote command
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Port Forwarding
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
vibe forward list # List all forwards
|
|
414
|
+
|
|
415
|
+
vibe forward create \
|
|
416
|
+
--local 5432 \
|
|
417
|
+
--remote-host db.internal \
|
|
418
|
+
--remote-port 5432 \
|
|
419
|
+
--server prod # Create a forward rule
|
|
420
|
+
|
|
421
|
+
vibe forward start -i <id> # Start forwarding
|
|
422
|
+
vibe forward stop -i <id> # Stop forwarding
|
|
423
|
+
vibe forward delete -i <id> # Delete forward rule
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Background Tasks
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
vibe task list # List all tasks
|
|
430
|
+
vibe task list --status running # Filter by status
|
|
431
|
+
vibe task run -c "npm test" # Run command as background task
|
|
432
|
+
vibe task run -c "make build" --cwd ~/project
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Notifications
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
vibe notify list # List all notifications
|
|
439
|
+
vibe notify list --unread # Show only unread
|
|
440
|
+
vibe notify read-all # Mark all as read
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Global Options
|
|
444
|
+
|
|
445
|
+
Every subcommand that communicates with a running agent accepts:
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
--agent-url <url> # Default: http://localhost:3005
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
This lets you manage remote agents or agents on non-default ports:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
vibe health --agent-url http://192.168.1.10:3005
|
|
455
|
+
vibe tunnel list --agent-url http://localhost:4000
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Architecture
|
|
459
|
+
|
|
460
|
+
The agent runs as a Bun + Elysia HTTP server with:
|
|
461
|
+
|
|
462
|
+
- **bun:sqlite** database for local state (sessions, connections, tunnels, tasks, config)
|
|
463
|
+
- **tmux** integration for terminal multiplexing
|
|
464
|
+
- **cloudflared** for secure tunnel management
|
|
465
|
+
- **Plugin system** for extensibility (SSH, AI tools, UI plugins, and custom plugins)
|
|
466
|
+
- **WebSocket** proxy for real-time terminal streaming
|
|
467
|
+
- **API key** authentication on all endpoints
|
|
468
|
+
- **Daemon mode** by default with proper process management
|
|
469
|
+
|
|
470
|
+
### System Dependencies
|
|
471
|
+
|
|
472
|
+
| Tool | Purpose | Required |
|
|
473
|
+
| ----------- | --------------------- | ----------- |
|
|
474
|
+
| Bun >= 1.3 | Runtime | Yes |
|
|
475
|
+
| tmux | Terminal multiplexing | Yes |
|
|
476
|
+
| ttyd | Web-based terminal | Recommended |
|
|
477
|
+
| cloudflared | Tunnel management | For tunnels |
|
|
478
|
+
|
|
479
|
+
Run `vibe setup --check` to verify all dependencies.
|
|
480
|
+
|
|
481
|
+
## REST API
|
|
482
|
+
|
|
483
|
+
When the agent is running, it exposes a REST API at `http://localhost:3005/api/` with 112 endpoints across these route groups:
|
|
484
|
+
|
|
485
|
+
- `/api/tmux/*` - Terminal session management
|
|
486
|
+
- `/api/ssh/*` - SSH connection management
|
|
487
|
+
- `/api/tunnel/*` - Per-port tunnel CRUD
|
|
488
|
+
- `/api/agent-tunnel/*` - Agent tunnel management
|
|
489
|
+
- `/api/port-forward/*` - Port forwarding
|
|
490
|
+
- `/api/tasks/*` - Background task management
|
|
491
|
+
- `/api/config/*` - Configuration & system info
|
|
492
|
+
- `/api/notifications/*` - Notification management
|
|
493
|
+
- `/api/files/*` - File operations
|
|
494
|
+
- `/api/git/*` - Git repository scanning
|
|
495
|
+
- `/api/bookmarks/*` - Command bookmarks
|
|
496
|
+
- `/api/projects/*` - Project scanning
|
|
497
|
+
- `/api/ai/tools` - AI tool detection
|
|
498
|
+
- `/api/ai/prompts/*` - Prompt template CRUD (via AI plugin)
|
|
499
|
+
|
|
500
|
+
Authentication: Include `x-agent-api-key` header with your API key.
|
|
501
|
+
|
|
502
|
+
## Examples
|
|
503
|
+
|
|
504
|
+
### Development Workflow
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
# Start agent (daemon mode is the default)
|
|
508
|
+
vibe start
|
|
509
|
+
|
|
510
|
+
# Set up a dev session
|
|
511
|
+
vibe session create --name frontend --cwd ~/app --command "npm run dev"
|
|
512
|
+
vibe session create --name backend --cwd ~/api --command "npm start"
|
|
513
|
+
|
|
514
|
+
# Expose your dev server publicly
|
|
515
|
+
vibe tunnel start -p 3000
|
|
516
|
+
|
|
517
|
+
# Check everything is running
|
|
518
|
+
vibe session list
|
|
519
|
+
vibe tunnel list
|
|
520
|
+
vibe health
|
|
521
|
+
|
|
522
|
+
# Later, clean up
|
|
523
|
+
vibe stop
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Remote Server Management
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
# Save SSH connections
|
|
530
|
+
vibe ssh add --name staging --host staging.example.com --user deploy --key ~/.ssh/deploy
|
|
531
|
+
vibe ssh add --name production --host prod.example.com --user deploy --key ~/.ssh/deploy
|
|
532
|
+
|
|
533
|
+
# Test connections
|
|
534
|
+
vibe ssh test -i <staging-id>
|
|
535
|
+
|
|
536
|
+
# Run remote commands
|
|
537
|
+
vibe ssh exec -i <staging-id> -c "docker ps"
|
|
538
|
+
vibe ssh exec -i <production-id> -c "systemctl status nginx"
|
|
539
|
+
|
|
540
|
+
# Set up port forwarding to access remote databases
|
|
541
|
+
vibe forward create --local 5432 --remote-host localhost --remote-port 5432 --server staging
|
|
542
|
+
vibe forward start -i <forward-id>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## Part of the VibeControls Ecosystem
|
|
546
|
+
|
|
547
|
+
This agent is the local component of the [VibeControls](https://vibecontrols.com) development environment platform. It can operate standalone or connect to the VibeControls backend for centralized management across multiple machines.
|
|
548
|
+
|
|
549
|
+
## Author
|
|
550
|
+
|
|
551
|
+
**Vignesh T.V.** ([@tvvignesh](https://github.com/tvvignesh))
|
|
552
|
+
[vignesh@burdenoff.com](mailto:vignesh@burdenoff.com)
|
|
553
|
+
|
|
554
|
+
## License
|
|
555
|
+
|
|
556
|
+
Proprietary - Copyright 2025-2026 Burdenoff Consultancy Services Pvt. Ltd. All rights reserved.
|
|
557
|
+
|
|
558
|
+
See [LICENSE](./LICENSE) for details.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{$c as g,Vc as a,Wc as b,Xc as c,Yc as d,Zc as e,_c as f,ad as h,bd as i,cd as j,dd as k,ed as l,fd as m,gd as n,hd as o,id as p,jd as q,kd as r,ld as s,md as t,nd as u,od as v,pd as w,qd as x,rd as y,sd as z,td as A,ud as B}from"./index-g2raeeh4.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{m as writeProfile,x as writeAgentConfigAwaitSecrets,w as writeAgentConfig,j as writeActiveProfileName,l as readProfile,y as readAgentConfigWithSecrets,u as readAgentConfig,i as readActiveProfileName,z as readActiveProfile,k as listProfileNames,f as getProfilesDir,h as getProfilePath,q as getProductionGatewayUrls,v as getOrCreateAgentInstanceId,t as getAgentConfigSeed,r as getAgentConfigPath,s as getAgentConfigDefaults,g as getActiveProfilePointerPath,p as ensureProfilesInitialized,n as deleteProfile,o as buildProfileSeed,e as assertValidProfileName,B as applyAgentSecretsToEnv,A as applyAgentConfigToEnv,c as NAMED_ENV_PRESETS,d as DEFAULT_PROFILE_NAME,a as CURRENT_CONFIG_SCHEMA_VERSION,b as BUILTIN_PROFILE_SEEDS};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{nc as G,uc as f,vc as y,wc as V}from"./index-z5a4yxzz.js";import"./index-yy1mm8zs.js";var N={pending:()=>G.gray("\u25CF"),active:()=>G.cyan("\u25B6"),ok:()=>G.green("\u2713"),warn:()=>G.yellow("\u26A0"),fail:()=>G.red("\u2717")};function L(E,A){if(A)console.log(" "+E)}async function O(E,A=2000){let W=new AbortController,j=setTimeout(()=>W.abort(),A);try{let q=await fetch(`${E}/health/ready`,{signal:W.signal});if(q.status!==200&&q.status!==503)return null;return await q.json()}catch{return null}finally{clearTimeout(j)}}function P(E){if(!E?.message)return null;let A=E.message.match(/provider:\s*(\S+)/);return A?A[1]:null}async function M(E,A={}){let W=A.timeoutMs??60000,j=A.pollIntervalMs??500,q=A.verbose!==!1;if(q)f("Waiting for agent to become ready");let C={daemon:!1,awaiting:!1,initializing:!1,db:!1,session:!1,tunnel:!1},z=Date.now(),X="",H=0,D=null;while(Date.now()-z<W){let Q=await O(E);if(!Q){await T(j);continue}if(!C.daemon)L(`${N.ok()} Daemon listening on ${E}`,q),C.daemon=!0;let F=Q.bootState??"unknown",Y=Q.components??{};if(F!==X){if(X=F,F==="awaiting-config"&&!C.awaiting)L(`${N.warn()} Workspace config: ${G.yellow("awaiting OAuth credentials \u2014 register this agent in the VibeControls UI")}`,q),C.awaiting=!0;else if(F==="initializing"&&!C.initializing)L(`${N.active()} Finalizing: fetching encryption key, opening storage, loading plugins\u2026`,q),C.initializing=!0}if(F==="awaiting-config"){if(H++,H>=2)return{outcome:"awaiting-config",bootState:F,degradedReasons:[],summary:"Agent is awaiting workspace registration. Open the VibeControls UI \u2192 Agents \u2192 Add Agent, paste the API Key and Tunnel URL printed above."}}else H=0;if(Y.db?.ok&&!C.db)L(`${N.ok()} Storage encryption key loaded, DB opened`,q),C.db=!0;if(Y.session?.ok&&!C.session){let x=Y.session.message??"";L(`${N.ok()} Plugins started \u2014 ${x}`,q),C.session=!0}let Z=P(Y.tunnel);if(Z&&Z!=="bootstrap"&&Z!==D){if(D=Z,!C.tunnel)L(`${N.ok()} Tunnel ready (provider: ${Z})`,q),C.tunnel=!0}let I=Q.status==="degraded";if(F==="ready"&&!I){if(q)V(),y("Status",G.green("ready"));return{outcome:"ready",bootState:F,degradedReasons:[],summary:"Agent ready."}}if(F==="degraded"||F==="ready"&&I){let x=Q.degradedReasons??[],_=x;if(x.length===0){let J=[];for(let[$,K]of Object.entries(Y)){if($==="boot"||$==="lifecycle")continue;if(K&&!K.ok&&K.message)J.push({plugin:$,message:K.message})}for(let[$,K]of Object.entries(Q.plugins??{}))if(K&&!K.ok&&K.message)J.push({plugin:`plugin:${$}`,message:K.message});_=J}if(q){V(),L(`${N.fail()} Agent boot ${G.red("degraded")} \u2014 investigate before retrying`,q);for(let J of _)L(` ${G.gray("\xB7")} [${J.plugin}] ${J.message}`,q);V()}return{outcome:"degraded",bootState:F,degradedReasons:_,summary:_.length?`Agent degraded: ${_.map((J)=>`[${J.plugin}] ${J.message}`).join("; ")}`:"Agent degraded with no reported reason \u2014 check vibe logs."}}await T(j)}if(q)V(),L(`${N.warn()} Timed out after ${Math.round(W/1000)}s; last bootState=${X||"unknown"}. Check ${G.bold("vibe logs")} for details.`,q),V();return{outcome:"timeout",bootState:X||"unknown",degradedReasons:[],summary:`Timed out waiting for ready. Last bootState=${X||"unknown"}.`}}function T(E){return new Promise((A)=>setTimeout(A,E))}export{M as trackAgentReady};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{n as a}from"./index-c58g96mb.js";import"./index-n7qyrdr1.js";import"./index-1hnw0rhc.js";import"./index-6jzsthh9.js";import"./index-g8zv1gta.js";import"./index-qfz9fy56.js";import"./index-ssjmzqcz.js";import"./index-tp4y9jde.js";import"./index-4wgjx8bf.js";import"./index-g3ap3xpr.js";import"./index-0ckffygp.js";import"./index-rc79x8fw.js";import"./index-yrgm89r8.js";import"./index-52cp759f.js";import"./index-v9fx5wab.js";import"./index-01qzsnwd.js";import"./index-d5ysy1yn.js";import"./index-04n4qgvd.js";import"./index-xjzmb1pn.js";import"./index-g2raeeh4.js";import"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{a as createApp};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{Hd as K,Nd as D}from"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";var b="bridge-client",U=2000,G=30000,N=30000,O=6,z={ws:null,pingTimer:null,reconnectTimer:null,reconnectAttempt:0,connected:!1,stopped:!1,agentId:null,profile:"default",pendingEvents:[]};function Z(J,Q,$){D().logger[J](b,Q,$)}function C(J){try{let Q=new URL(J);Q.protocol=Q.protocol==="https:"?"wss:":"ws:",Q.pathname="/agent-bridge",Q.search="",Q.hash="";let $=process.env.VIBE_BRIDGE_URL;if($)return $;return Q.toString()}catch{return process.env.VIBE_BRIDGE_URL??""}}async function x(){if(z.stopped||z.ws)return;if(!K.isConfigured()){Z("debug","Gateway client not configured yet; deferring bridge connect");return}if(!z.agentId){Z("debug","No agentId set yet; deferring bridge connect");return}let J,Q,$;try{let Y=await K.getAccessToken();J=Y.token,Q=Y.workspaceGatewayUrl,$=Y.workspaceId}catch(Y){Z("warn","Failed to fetch access token for bridge",{error:Y instanceof Error?Y.message:String(Y)}),W();return}if(!J||!Q){W();return}let q=C(Q);if(!q){Z("warn","Cannot resolve bridge URL from workspaceGatewayUrl",{workspaceGatewayUrl:Q}),W();return}Z("info","Connecting to agent-bridge",{url:q,agentId:z.agentId});let H;try{H=new WebSocket(q,{headers:{Authorization:`Bearer ${J}`,...$?{"x-workspace-id":$}:{}}})}catch(Y){Z("warn","Bridge connect threw",{error:Y instanceof Error?Y.message:String(Y)}),W();return}z.ws=H,H.addEventListener("open",()=>{z.reconnectAttempt=0,Z("info","Bridge socket open \u2014 sending hello"),X({type:"hello",agentId:z.agentId,profile:z.profile,version:process.env.npm_package_version??"unknown"})}),H.addEventListener("message",(Y)=>{let B=typeof Y.data==="string"?Y.data:Y.data instanceof Blob?"":new TextDecoder().decode(Y.data);if(!B)return;let V;try{V=JSON.parse(B)}catch{return}if(V.type==="welcome")z.connected=!0,Z("info","Bridge welcome received \u2014 connection ready",{workspaceId:typeof V.workspaceId==="string"?V.workspaceId:void 0}),L(),P();else if(V.type==="error")Z("warn","Bridge error frame",{error:V})}),H.addEventListener("close",(Y)=>{if(Z("info","Bridge socket closed",{code:Y.code,reason:Y.reason}),M(),!z.stopped)W()}),H.addEventListener("error",(Y)=>{Z("warn","Bridge socket error",{message:Y.message})})}function X(J){if(!z.ws||z.ws.readyState!==1)return!1;try{return z.ws.send(JSON.stringify(J)),!0}catch(Q){return Z("debug","safeSend failed",{error:Q instanceof Error?Q.message:String(Q)}),!1}}function L(){F(),z.pingTimer=setInterval(()=>{X({type:"ping"})},N),z.pingTimer.unref?.()}function F(){if(z.pingTimer)clearInterval(z.pingTimer),z.pingTimer=null}function M(){z.connected=!1,F(),z.ws=null}function W(){if(z.stopped||z.reconnectTimer)return;let J=z.reconnectAttempt+1,Q=Math.min(U*2**Math.min(J-1,5),G);if(z.reconnectAttempt=J,J===O)Z("warn",`Bridge unavailable after ${J} attempts \u2014 falling back to existing HTTP polling for heartbeat/tunnel sync. Will keep retrying in the background.`);z.reconnectTimer=setTimeout(()=>{z.reconnectTimer=null,x()},Q),z.reconnectTimer.unref?.()}function P(){if(!z.connected)return;while(z.pendingEvents.length>0){let J=z.pendingEvents.shift();if(!X({type:J.type,...J.payload})){z.pendingEvents.unshift(J);return}}}var R=500;function j(J){if(z.connected&&X({type:J.type,...J.payload}))return;if(z.pendingEvents.length>=R)z.pendingEvents.shift();z.pendingEvents.push(J)}var I={start(J,Q){if(z.stopped=!1,z.agentId===J&&z.connected)return;z.agentId=J,z.profile=Q||"default",x()},stop(){if(z.stopped=!0,z.reconnectTimer)clearTimeout(z.reconnectTimer),z.reconnectTimer=null;let J=z.ws;try{J?.close(1000,"agent shutdown")}catch{}M()},publishEvent(J){j({type:"event",payload:{topic:J.topic,payload:J.payload}})},publishLog(J){j({type:"log",payload:{payload:J}})},isConnected(){return z.connected},isFallbackEngaged(){return z.reconnectAttempt>=O&&!z.connected}};export{I as bridgeClient};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
import{H as O}from"./index-mxc61yr1.js";import{I as Q,J as w}from"./index-3ys16efc.js";import"./index-2gsarrbn.js";import"./index-b5dhmybd.js";import"./index-e1bw1bwr.js";import"./index-g8zv1gta.js";import"./index-qfz9fy56.js";import"./index-ssjmzqcz.js";import{wa as Z,za as I}from"./index-tp4y9jde.js";import{Aa as G,Ba as H}from"./index-4wgjx8bf.js";import{Wa as J}from"./index-g3ap3xpr.js";import{Xa as U}from"./index-0ckffygp.js";import"./index-rc79x8fw.js";import{bb as K}from"./index-yrgm89r8.js";import"./index-52cp759f.js";import"./index-01qzsnwd.js";import"./index-d5ysy1yn.js";import"./index-04n4qgvd.js";import{cc as $}from"./index-2xs9cvjn.js";import"./index-5mw3eshk.js";import{mc as E}from"./index-z5a4yxzz.js";import"./index-ef95xr4z.js";import"./index-xjzmb1pn.js";import"./index-g2raeeh4.js";import{Nd as Y}from"./index-9bqd8veb.js";import{_d as W,ce as X}from"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";import{join as B}from"path";if(typeof Bun>"u")console.error(`\x1B[31mError:\x1B[0m VibeControls Agent requires the Bun runtime.
|
|
4
|
+
Install Bun: https://bun.sh
|
|
5
|
+
Then run: bun run vibe ...`),process.exit(1);var T=process.argv.slice(2),_=T.includes("--plain")||T.includes("--json")||process.env.NO_COLOR==="1"||!process.stdout.isTTY;E(!_);try{O()}catch{}var N="1.0.0";try{let q=B(import.meta.dir,"..","package.json");N=(await Bun.file(q).json()).version}catch{}var x=new $;x.name("vibe").description("VibeControls Agent CLI \u2014 Remote development environment management").version(N,"-v, --version").option("--provider <name>","Override the default provider for tunnel/session operations").option("-p, --profile <name>","Target a specific profile (one-off; doesn't change the persisted default). See `vibe profile`.").option("--json","Emit machine-readable JSON instead of human output.").option("--plain","Force plain-text output (no interactive UI).").hook("preAction",(q,z)=>{let F=q.opts().profile;if(F)process.env.VIBECONTROLS_PROFILE=F;try{K.init(),K.emit("command.executed",{command_name:z.name()})}catch{}});w(x);async function M(){let q=new J;try{await q.loadCorePlugins()}catch{}try{await q.loadAll()}catch{}let z=new Z,F={storage:{async get(){return null},async set(){},async delete(){return!1},async list(){return[]},async deleteAll(){return 0}},logger:Y().logger,serviceRegistry:z,getProvider:(L)=>z.getProvider(L),getAgentBaseUrl:()=>process.env.AGENT_URL||"http://localhost:3005",getAgentVersion:()=>N,broadcast:()=>{},workspaceQuery:async()=>({data:void 0,errors:[{message:"workspaceQuery is not available in CLI mode"}]}),isGatewayConfigured:()=>!1,getAgentRecordId:async()=>null,getWorkspaceId:async()=>null,getConfig:async()=>{return},getPluginRegistry:()=>X(),getDataDir:()=>W(),cliContributors:new G,telemetry:{emit:(L,D)=>{try{K.emit(L,D??{})}catch{}}},os:U(),iframeBridge:I()};H(F.cliContributors);try{await q.dispatchCliSetup(x,F)}catch{}x.configureHelp({formatHelp:()=>Q(x,!0)}),await x.parseAsync()}M().then(()=>{process.exit(process.exitCode??0)}).catch((q)=>{console.error(`\x1B[31mFatal:\x1B[0m ${q instanceof Error?q.message:q}`),process.exit(1)});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{Ld as a,Md as b,Nd as c,Od as d,Pd as e}from"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";export{a as profileRegistry,d as getOrCreateProfile,c as getDaemonProfile,b as ensureDaemonProfile,e as __resetDaemonProfileForTests};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{$b as e,Xb as a,Yb as b,Zb as c,_b as d,ac as f,bc as g,cc as h,dc as i,ec as j,fc as k}from"./index-2xs9cvjn.js";import"./index-yy1mm8zs.js";export{a as program,d as createOption,b as createCommand,c as createArgument,j as Option,g as InvalidOptionArgumentError,f as InvalidArgumentError,k as Help,e as CommanderError,h as Command,i as Argument};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{Nd as Y}from"./index-9bqd8veb.js";import"./index-0cn9bv8z.js";import"./index-jw1k4vbk.js";import"./index-yy1mm8zs.js";var Z=[300000,600000,900000,1800000];function j(R){let W=R.backoffsMs??Z,w=0,z=!1,G=null,J=null,I=null,q=Y();if(q.getBootState()==="ready")return{cancel(){},async forceRetryNow(){return{ok:!0}},getState(){return{attempt:0,cancelled:!0,nextAttemptAt:null}}};function Q(){if(G!==null)clearTimeout(G),G=null,J=null}function X(){if(z)return Promise.resolve({ok:!1,error:"retry worker cancelled"});if(q.getBootState()==="ready")return z=!0,Q(),Promise.resolve({ok:!0});if(I)return I;return w+=1,R.onAttempt?.(w),q.logger.info("finalize-retry",`Background finalize retry attempt ${w}`),I=(async()=>{try{let v=await R.trigger();if(v.ok)q.logger.info("finalize-retry",`Background finalize succeeded on attempt ${w} \u2014 agent recovered`),z=!0,Q();else q.recordFinalizeError(v.error,w),q.logger.warn("finalize-retry",`Background finalize attempt ${w} failed`,{error:v.error});return v}catch(v){let H=v instanceof Error?v.message:String(v);return q.recordFinalizeError(H,w),q.logger.error("finalize-retry",`Background finalize attempt ${w} threw`,{error:H}),{ok:!1,error:H}}finally{I=null}})(),I}function V(){if(z)return;if(q.getBootState()==="ready"){z=!0;return}if(G!==null)return;let v=Math.min(w,W.length-1),H=W[v];J=Date.now()+H,G=setTimeout(async()=>{if(G=null,await X(),!z&&q.getBootState()!=="ready")V()},H)}return V(),{cancel(){z=!0,Q()},async forceRetryNow(){if(q.getBootState()==="ready")return{ok:!0};if(z)return{ok:!1,error:"retry worker cancelled"};Q();let v=await X();if(!z&&q.getBootState()!=="ready")V();return v},getState(){return{attempt:w,cancelled:z,nextAttemptAt:J?new Date(J).toISOString():null}}}}export{j as startFinalizeRetryWorker};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{f as D}from"./index-8sm0nkh8.js";import{g as C}from"./index-x1h8r7pr.js";import{fe as z,he as B}from"./index-yy1mm8zs.js";var H=z((v)=>{Object.defineProperty(v,"__esModule",{value:!0});v.getMachineId=void 0;var E=B("fs"),F=D(),q=C();async function G(){try{return(await E.promises.readFile("/etc/hostid",{encoding:"utf8"})).trim()}catch(k){q.diag.debug(`error reading machine id: ${k}`)}try{return(await(0,F.execAsync)("kenv -q smbios.system.uuid")).stdout.trim()}catch(k){q.diag.debug(`error reading machine id: ${k}`)}return}v.getMachineId=G});export default H();
|