viberun 0.0.1 → 0.3.5
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 +29 -0
- package/README.md +379 -2
- package/bin/viberun.js +176 -0
- package/package.json +15 -88
- package/vendor/aarch64-apple-darwin/viberun/viberun +0 -0
- package/vendor/aarch64-pc-windows-msvc/viberun/viberun.exe +0 -0
- package/vendor/aarch64-unknown-linux-musl/viberun/viberun +0 -0
- package/vendor/x86_64-apple-darwin/viberun/viberun +0 -0
- package/vendor/x86_64-pc-windows-msvc/viberun/viberun.exe +0 -0
- package/vendor/x86_64-unknown-linux-musl/viberun/viberun +0 -0
- package/CHANGELOG.md +0 -5
- package/LICENSE.md +0 -21
- package/dist/cli/bin.d.ts +0 -0
- package/dist/cli/bin.js +0 -9
- package/dist/cli/bin.js.map +0 -1
- package/dist/cli/builder.d.ts +0 -18
- package/dist/cli/builder.js +0 -44
- package/dist/cli/builder.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -10
- package/dist/cli/commands.js +0 -35
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/run.d.ts +0 -4
- package/dist/cli/run.js +0 -37
- package/dist/cli/run.js.map +0 -1
- package/dist/config/babelrc.d.ts +0 -27
- package/dist/config/babelrc.js +0 -47
- package/dist/config/babelrc.js.map +0 -1
- package/dist/config/webpack.common.d.ts +0 -13
- package/dist/config/webpack.common.js +0 -137
- package/dist/config/webpack.common.js.map +0 -1
- package/dist/config/webpack.dev.d.ts +0 -9
- package/dist/config/webpack.dev.js +0 -39
- package/dist/config/webpack.dev.js.map +0 -1
- package/dist/config/webpack.prod.d.ts +0 -5
- package/dist/config/webpack.prod.js +0 -63
- package/dist/config/webpack.prod.js.map +0 -1
- package/dist/config/webpack.profile.d.ts +0 -5
- package/dist/config/webpack.profile.js +0 -29
- package/dist/config/webpack.profile.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/utils.d.ts +0 -3
- package/dist/utils.js +0 -27
- package/dist/utils.js.map +0 -1
- package/typings/global.d.ts +0 -2
- package/typings/script-ext-html-webpack-plugin.d.ts +0 -17
- package/typings/webpack-bundle-analyzer.d.ts +0 -45
- package/yarn.lock +0 -4937
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, AUTHORS
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,380 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/viberun-logo.png" alt="viberun logo" width="320">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# viberun
|
|
6
|
+
|
|
7
|
+
`viberun` is a CLI-first, agent-native app host. Run `viberun <app>` locally and get dropped into an agent session inside a persistent Ubuntu container on a remote host. App data is stored under `/home/viberun` and survives container restarts or image updates.
|
|
8
|
+
|
|
9
|
+
## Quick start (end-to-end)
|
|
10
|
+
|
|
11
|
+
You need a local machine with `ssh` and a reachable Ubuntu host (VM or server) that you can SSH into with sudo access.
|
|
12
|
+
|
|
13
|
+
### 1) Install the CLI
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
curl -fsSL https://viberun.sh | bash
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Verify:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
viberun --version
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Optional overrides (advanced):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -fsSL https://viberun.sh | bash -s -- --nightly
|
|
29
|
+
curl -fsSL https://viberun.sh | bash -s -- --dir ~/.local/bin --bin viberun
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or with env vars:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
curl -fsSL https://viberun.sh | VIBERUN_INSTALL_DIR=~/.local/bin VIBERUN_INSTALL_BIN=viberun bash
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2) Bootstrap a host (once per VM)
|
|
39
|
+
|
|
40
|
+
Use any SSH host alias (for example, `myhost` in `~/.ssh/config`):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
viberun bootstrap myhost
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Optional: set it as your default host (and agent) so you can omit `@host` later:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
viberun config --host myhost --agent codex
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3) Start your first app
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
viberun hello-world
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If this is the first run, the server will prompt to create the container. Press Enter to accept.
|
|
59
|
+
|
|
60
|
+
Detach without stopping the agent: Ctrl-\\ . Reattach later with `viberun hello-world`.
|
|
61
|
+
Paste clipboard images into the session with Ctrl-V; `viberun` uploads the image and inserts a `/tmp/viberun-clip-*.png` path.
|
|
62
|
+
|
|
63
|
+
### 4) Hello-world prompt (paste inside the session)
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Create a beautiful hello-world web app with a simple, tasteful landing page.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 5) Open the app
|
|
70
|
+
|
|
71
|
+
While the session is active, `viberun` starts a localhost proxy to the host port. The agent will print a URL like:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
http://localhost:8080
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Open it in your browser.
|
|
78
|
+
If you've configured app URLs, `viberun <app> url` shows the HTTPS address.
|
|
79
|
+
|
|
80
|
+
## Common commands
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
viberun myapp
|
|
84
|
+
viberun myapp@hostb
|
|
85
|
+
viberun --forward-agent myapp
|
|
86
|
+
viberun myapp shell
|
|
87
|
+
viberun myapp snapshot
|
|
88
|
+
viberun myapp snapshots
|
|
89
|
+
viberun myapp restore latest
|
|
90
|
+
viberun myapp url
|
|
91
|
+
viberun myapp users
|
|
92
|
+
viberun myapp --delete -y
|
|
93
|
+
viberun myapp update
|
|
94
|
+
viberun bootstrap [<host>]
|
|
95
|
+
viberun proxy setup
|
|
96
|
+
viberun users list
|
|
97
|
+
viberun wipe
|
|
98
|
+
viberun config --host myhost --agent codex
|
|
99
|
+
viberun version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
<summary>Table of contents</summary>
|
|
104
|
+
|
|
105
|
+
- [Quick start (end-to-end)](#quick-start-end-to-end)
|
|
106
|
+
- [Common commands](#common-commands)
|
|
107
|
+
- [Git setup](#git-setup)
|
|
108
|
+
- [Development](#development)
|
|
109
|
+
- [Architecture](#architecture)
|
|
110
|
+
- [High-level flow](#high-level-flow)
|
|
111
|
+
- [Core components](#core-components)
|
|
112
|
+
- [Session lifecycle](#session-lifecycle)
|
|
113
|
+
- [Bootstrap pipeline](#bootstrap-pipeline)
|
|
114
|
+
- [Networking and ports](#networking-and-ports)
|
|
115
|
+
- [App URLs and proxy](#app-urls-and-proxy)
|
|
116
|
+
- [Snapshots and restore](#snapshots-and-restore)
|
|
117
|
+
- [Host RPC bridge](#host-rpc-bridge)
|
|
118
|
+
- [Configuration and state](#configuration-and-state)
|
|
119
|
+
- [Agents](#agents)
|
|
120
|
+
- [Security model](#security-model)
|
|
121
|
+
- [Wipe (safety)](#wipe-safety)
|
|
122
|
+
- [Repository layout](#repository-layout)
|
|
123
|
+
- [Troubleshooting](#troubleshooting)
|
|
124
|
+
|
|
125
|
+
</details>
|
|
126
|
+
|
|
127
|
+
## Git setup
|
|
128
|
+
|
|
129
|
+
Git, SSH, and the GitHub CLI are installed in containers. viberun seeds `git config --global user.name` and `user.email` from your local Git config into a host-managed config file that is mounted into each app container and applied on startup. This removes the common "first commit" setup step without auto-authing you.
|
|
130
|
+
|
|
131
|
+
Choose one of these auth paths:
|
|
132
|
+
|
|
133
|
+
- **SSH (agent forwarding)**: Run `viberun --forward-agent <app>`. For existing apps, run `viberun <app> update` once to recreate the container with the agent socket mounted. Then `ssh -T git@github.com` inside the container to verify access.
|
|
134
|
+
- **HTTPS (GitHub CLI)**: Run `gh auth login` and choose HTTPS, then `gh auth setup-git`. Verify with `gh auth status`.
|
|
135
|
+
|
|
136
|
+
If you update your local Git identity later, restart the app container (or run `viberun <app> update`) to re-apply the new values on startup.
|
|
137
|
+
|
|
138
|
+
## Development
|
|
139
|
+
|
|
140
|
+
This repo is Go-first and uses `mise` for tool and task orchestration.
|
|
141
|
+
|
|
142
|
+
### Setup
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
mise install
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Build
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
mise exec -- go build ./cmd/viberun
|
|
152
|
+
mise exec -- go build ./cmd/viberun-server
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Run locally
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
mise exec -- go run ./cmd/viberun -- --help
|
|
159
|
+
mise exec -- go run ./cmd/viberun-server -- --help
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Test and vet
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
mise exec -- go test ./...
|
|
166
|
+
mise exec -- go vet ./...
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Container image
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
mise run build:image
|
|
173
|
+
# fallback: docker build -t viberun .
|
|
174
|
+
# proxy image (Caddy + auth):
|
|
175
|
+
docker build -f Dockerfile.proxy -t viberun-proxy .
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### E2E and integration
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
bin/viberun-e2e-local
|
|
182
|
+
bin/viberun-integration
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Bootstrap in development
|
|
186
|
+
|
|
187
|
+
When you run `viberun` via `go run` (or a dev build), bootstrap defaults to staging the local server binary and building the container image locally. This is equivalent to:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
viberun bootstrap --local --local-image myhost
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Under the hood, it builds a `viberun:dev` image for the host architecture, streams it over `ssh` with `docker save | docker load`, and tags it as `viberun:latest` on the host.
|
|
194
|
+
|
|
195
|
+
If you want to explicitly pass a local server binary:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
mise exec -- go build -o /tmp/viberun-server ./cmd/viberun-server
|
|
199
|
+
viberun bootstrap --local-path /tmp/viberun-server myhost
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
For the full build/test/E2E flow, see `DEVELOPMENT.md`.
|
|
203
|
+
|
|
204
|
+
## Architecture
|
|
205
|
+
|
|
206
|
+
### High-level flow
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
viberun <app>
|
|
210
|
+
-> ssh <host>
|
|
211
|
+
-> viberun-server <app>
|
|
212
|
+
-> docker container viberun-<app>
|
|
213
|
+
-> agent session (tmux)
|
|
214
|
+
|
|
215
|
+
container port 8080
|
|
216
|
+
-> host port (assigned per app)
|
|
217
|
+
-> ssh -L localhost:<port>
|
|
218
|
+
-> http://localhost:<port>
|
|
219
|
+
-> (optional) host proxy (Caddy)
|
|
220
|
+
-> https://<app>.<domain>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Core components
|
|
224
|
+
|
|
225
|
+
- Client: `viberun` CLI on your machine.
|
|
226
|
+
- Server: `viberun-server` executed on the host via SSH (no long-running daemon required).
|
|
227
|
+
- Container: `viberun-<app>` Docker container built from the `viberun:latest` image.
|
|
228
|
+
- Agent: runs inside the container in a tmux session (default provider: `codex`).
|
|
229
|
+
- Host RPC: local Unix socket used by the container to request snapshot/restore operations.
|
|
230
|
+
- Proxy (optional): `viberun-proxy` (Caddy + `viberun-auth`) for app URLs and login.
|
|
231
|
+
|
|
232
|
+
### Session lifecycle
|
|
233
|
+
|
|
234
|
+
1. `viberun <app>` resolves the host (from `@host` or your default config) and runs `viberun-server` over SSH.
|
|
235
|
+
2. The server creates the container if needed, or starts it if it already exists.
|
|
236
|
+
3. The agent process is attached via `docker exec` inside a tmux session so it persists across disconnects.
|
|
237
|
+
4. `viberun` sets up a local port forward so you can open the app on `http://localhost:<port>`.
|
|
238
|
+
|
|
239
|
+
### Bootstrap pipeline
|
|
240
|
+
|
|
241
|
+
The bootstrap script (run on the host) does the following:
|
|
242
|
+
|
|
243
|
+
- Verifies the host is Ubuntu.
|
|
244
|
+
- Installs Docker (if missing) and enables it.
|
|
245
|
+
- Installs Btrfs tools (`btrfs-progs`) for volume snapshots.
|
|
246
|
+
- Pulls the `viberun` container image from GHCR (unless using local image mode).
|
|
247
|
+
- Downloads and installs the `viberun-server` binary.
|
|
248
|
+
|
|
249
|
+
If bootstrap is run from a TTY, it will offer to set up a public domain name (same as `viberun proxy setup`).
|
|
250
|
+
|
|
251
|
+
Useful bootstrap overrides:
|
|
252
|
+
|
|
253
|
+
- `VIBERUN_SERVER_REPO`: GitHub repo for releases (default `shayne/viberun`).
|
|
254
|
+
- `VIBERUN_SERVER_VERSION`: release tag or `latest`.
|
|
255
|
+
- `VIBERUN_IMAGE`: container image override.
|
|
256
|
+
- `VIBERUN_PROXY_IMAGE`: proxy container image override (for app URLs).
|
|
257
|
+
- `VIBERUN_SERVER_INSTALL_DIR`: install directory on the host.
|
|
258
|
+
- `VIBERUN_SERVER_BIN`: server binary name on the host.
|
|
259
|
+
- `VIBERUN_SERVER_LOCAL_PATH`: use a local server binary staged over SSH.
|
|
260
|
+
- `VIBERUN_SKIP_IMAGE_PULL`: skip pulling from GHCR (used for local builds).
|
|
261
|
+
|
|
262
|
+
### Networking and ports
|
|
263
|
+
|
|
264
|
+
- Each app container exposes port `8080` internally.
|
|
265
|
+
- The host port is assigned per app (starting at `8080`) and stored in the host server state.
|
|
266
|
+
- `viberun` opens an SSH local forward so `http://localhost:<port>` connects to the host port.
|
|
267
|
+
- If the proxy is configured, apps can also be served over HTTPS at `https://<app>.<domain>` (or a custom domain). Access requires login by default and can be made public per app.
|
|
268
|
+
|
|
269
|
+
### App URLs and proxy
|
|
270
|
+
|
|
271
|
+
`viberun` can optionally expose apps via a host-side proxy (Caddy + `viberun-auth`).
|
|
272
|
+
Set it up once per host:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
viberun proxy setup [<host>]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
You'll be prompted for a base domain and public IP (for DNS), plus a primary username/password.
|
|
279
|
+
Create an A record (or wildcard) pointing to the host's public IP.
|
|
280
|
+
|
|
281
|
+
After setup:
|
|
282
|
+
|
|
283
|
+
- `viberun <app> url` shows the current URL and access status.
|
|
284
|
+
- `viberun <app> url --make-public` or `--require-login` toggles access (default requires login).
|
|
285
|
+
- `viberun <app> url --set-domain <domain>` or `--reset-domain` manages custom domains.
|
|
286
|
+
- `viberun <app> url --disable` or `--enable` turns the URL off/on.
|
|
287
|
+
- `viberun <app> url --open` opens the URL in your browser.
|
|
288
|
+
- `viberun users ...` manages login accounts; `viberun <app> users` controls who can access the app.
|
|
289
|
+
|
|
290
|
+
If URL settings change, run `viberun <app> update` to refresh `VIBERUN_PUBLIC_URL` and `VIBERUN_PUBLIC_DOMAIN` inside the container.
|
|
291
|
+
|
|
292
|
+
### Snapshots and restore
|
|
293
|
+
|
|
294
|
+
Snapshots are Btrfs subvolume snapshots of the app's `/home/viberun` volume (auto-incremented versions).
|
|
295
|
+
On the host, each app uses a loop-backed Btrfs file under `/var/lib/viberun/apps/<app>/home.btrfs`.
|
|
296
|
+
|
|
297
|
+
- `viberun <app> snapshot` creates the next `vN` snapshot.
|
|
298
|
+
- `viberun <app> snapshots` lists versions with timestamps.
|
|
299
|
+
- `viberun <app> restore <vN|latest>` restores from a snapshot (`latest` picks the highest `vN`).
|
|
300
|
+
- `viberun <app> --delete -y` removes the container, the app volume + snapshots, and the host RPC directory.
|
|
301
|
+
|
|
302
|
+
Restore details:
|
|
303
|
+
- The host stops the container (if running) to safely unmount the volume.
|
|
304
|
+
- The current `@home` subvolume is replaced by a new writable snapshot of `@snapshots/vN`.
|
|
305
|
+
- The container is started again, and s6 reloads services from `/home/viberun/.local/services`.
|
|
306
|
+
|
|
307
|
+
### Host RPC bridge
|
|
308
|
+
|
|
309
|
+
When you open a session, the server creates a Unix socket on the host and mounts it into the container at `/var/run/viberun-hostrpc`. The container uses it to request snapshot and restore operations. Access is protected by a per-session token file mounted alongside the socket.
|
|
310
|
+
|
|
311
|
+
### Configuration and state
|
|
312
|
+
|
|
313
|
+
Local config lives at `~/.config/viberun/config.toml` (or `$XDG_CONFIG_HOME/viberun/config.toml`) and stores:
|
|
314
|
+
|
|
315
|
+
- `default_host`
|
|
316
|
+
- `agent_provider`
|
|
317
|
+
- `hosts` (alias mapping)
|
|
318
|
+
|
|
319
|
+
Host server state lives at `~/.config/viberun/server-state.json` (or `$XDG_CONFIG_HOME/viberun/server-state.json`) and stores the port mapping for each app.
|
|
320
|
+
|
|
321
|
+
Proxy config (when enabled) lives at `/var/lib/viberun/proxy.toml` (or `$VIBERUN_PROXY_CONFIG_PATH`) and stores the base domain, access rules, and users.
|
|
322
|
+
When enabled, the server injects `VIBERUN_PUBLIC_URL` and `VIBERUN_PUBLIC_DOMAIN` into containers.
|
|
323
|
+
|
|
324
|
+
### Agents
|
|
325
|
+
|
|
326
|
+
Supported agent providers:
|
|
327
|
+
|
|
328
|
+
- `codex` (default)
|
|
329
|
+
- `claude`
|
|
330
|
+
- `gemini`
|
|
331
|
+
- `ampcode` (alias: `amp`)
|
|
332
|
+
- `opencode`
|
|
333
|
+
|
|
334
|
+
Custom agents can be run via `npx:<pkg>` or `uvx:<pkg>` (for example, `viberun --agent npx:@sourcegraph/amp@latest <app>`).
|
|
335
|
+
|
|
336
|
+
Set globally with `viberun config --agent <provider>` or per-run with `viberun --agent <provider> <app>`.
|
|
337
|
+
To forward your local SSH agent into the container, use `viberun --forward-agent <app>`. For existing apps, run `viberun <app> update` once to recreate the container with the agent socket mounted.
|
|
338
|
+
|
|
339
|
+
Base skills are shipped in `/opt/viberun/skills` and symlinked into each agent's skills directory. User skills can be added directly to the agent-specific skills directory under `/home/viberun`.
|
|
340
|
+
|
|
341
|
+
### Security model
|
|
342
|
+
|
|
343
|
+
- All control traffic goes over SSH; the server is invoked on demand and does not expose a network port.
|
|
344
|
+
- The host RPC socket is local-only and protected by filesystem permissions and a per-session token.
|
|
345
|
+
- Containers are isolated by Docker and only the app port is exposed.
|
|
346
|
+
- App URLs are optional: the proxy requires login by default and can be made public per app with `viberun <app> url --make-public`.
|
|
347
|
+
|
|
348
|
+
### Wipe (safety)
|
|
349
|
+
|
|
350
|
+
`viberun wipe [<host>]` deletes local config and wipes all viberun data on the host.
|
|
351
|
+
It requires a TTY and asks you to type `WIPE`.
|
|
352
|
+
|
|
353
|
+
On the host, wipe removes:
|
|
354
|
+
|
|
355
|
+
- All containers named `viberun-*`, any containers using `viberun` images, and the proxy container (default `viberun-proxy`).
|
|
356
|
+
- All `viberun` images (including the proxy image).
|
|
357
|
+
- App data and snapshots under `/var/lib/viberun` (including per-app Btrfs volumes).
|
|
358
|
+
- Host RPC sockets in `/tmp/viberun-hostrpc` and `/var/run/viberun-hostrpc`.
|
|
359
|
+
- `/etc/viberun`, `/etc/sudoers.d/viberun-server`, and `/usr/local/bin/viberun-server`.
|
|
360
|
+
|
|
361
|
+
Locally, it removes `~/.config/viberun/config.toml` (and legacy config if present).
|
|
362
|
+
|
|
363
|
+
### Repository layout
|
|
364
|
+
|
|
365
|
+
- `cmd/`: Go entrypoints (`viberun`, `viberun-server`, `viberun-auth`).
|
|
366
|
+
- `internal/`: Core packages (config, server state, SSH args, target parsing, TUI helpers).
|
|
367
|
+
- `bin/`: Helper scripts for installs, integration/E2E flows, and container utilities.
|
|
368
|
+
- `skills/`: Codex skill definitions used inside containers.
|
|
369
|
+
- `config/`: Shell/TMUX/Starship config, auth assets, and runtime configs.
|
|
370
|
+
- `Dockerfile`: Base container image definition.
|
|
371
|
+
- `Dockerfile.proxy`: Proxy image definition (Caddy + auth).
|
|
372
|
+
|
|
373
|
+
### Troubleshooting
|
|
374
|
+
|
|
375
|
+
- `unsupported OS: ... expected ubuntu`: bootstrap currently supports Ubuntu only.
|
|
376
|
+
- `docker is required but was not found in PATH`: install Docker on the host or re-run bootstrap.
|
|
377
|
+
- `missing btrfs on host`: rerun bootstrap to install `btrfs-progs` and ensure sudo access.
|
|
378
|
+
- `no host provided and no default host configured`: run `viberun config --host myhost` or use `myapp@host`.
|
|
379
|
+
- `container image architecture mismatch`: delete and recreate the app (`viberun <app> --delete -y`).
|
|
380
|
+
- `proxy is not configured`: run `viberun proxy setup` (then retry `viberun <app> url`).
|
package/bin/viberun.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Unified entry point for the viberun CLI.
|
|
3
|
+
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
// __dirname equivalent in ESM
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const { platform, arch } = process;
|
|
14
|
+
|
|
15
|
+
let targetTriple = null;
|
|
16
|
+
switch (platform) {
|
|
17
|
+
case "linux":
|
|
18
|
+
case "android":
|
|
19
|
+
switch (arch) {
|
|
20
|
+
case "x64":
|
|
21
|
+
targetTriple = "x86_64-unknown-linux-musl";
|
|
22
|
+
break;
|
|
23
|
+
case "arm64":
|
|
24
|
+
targetTriple = "aarch64-unknown-linux-musl";
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
case "darwin":
|
|
31
|
+
switch (arch) {
|
|
32
|
+
case "x64":
|
|
33
|
+
targetTriple = "x86_64-apple-darwin";
|
|
34
|
+
break;
|
|
35
|
+
case "arm64":
|
|
36
|
+
targetTriple = "aarch64-apple-darwin";
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
case "win32":
|
|
43
|
+
switch (arch) {
|
|
44
|
+
case "x64":
|
|
45
|
+
targetTriple = "x86_64-pc-windows-msvc";
|
|
46
|
+
break;
|
|
47
|
+
case "arm64":
|
|
48
|
+
targetTriple = "aarch64-pc-windows-msvc";
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!targetTriple) {
|
|
59
|
+
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const vendorRoot = path.join(__dirname, "..", "vendor");
|
|
63
|
+
const archRoot = path.join(vendorRoot, targetTriple);
|
|
64
|
+
const viberunBinaryName = process.platform === "win32" ? "viberun.exe" : "viberun";
|
|
65
|
+
const binaryPath = path.join(archRoot, "viberun", viberunBinaryName);
|
|
66
|
+
|
|
67
|
+
// Use an asynchronous spawn instead of spawnSync so that Node is able to
|
|
68
|
+
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
|
|
69
|
+
// executing. This allows us to forward those signals to the child process
|
|
70
|
+
// and guarantees that when either the child terminates or the parent
|
|
71
|
+
// receives a fatal signal, both processes exit in a predictable manner.
|
|
72
|
+
|
|
73
|
+
function getUpdatedPath(newDirs) {
|
|
74
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
75
|
+
const existingPath = process.env.PATH || "";
|
|
76
|
+
const updatedPath = [
|
|
77
|
+
...newDirs,
|
|
78
|
+
...existingPath.split(pathSep).filter(Boolean),
|
|
79
|
+
].join(pathSep);
|
|
80
|
+
return updatedPath;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Use heuristics to detect the package manager that was used to install viberun
|
|
85
|
+
* in order to give the user a hint about how to update it.
|
|
86
|
+
*/
|
|
87
|
+
function detectPackageManager() {
|
|
88
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
89
|
+
if (/\bbun\//.test(userAgent)) {
|
|
90
|
+
return "bun";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const execPath = process.env.npm_execpath || "";
|
|
94
|
+
if (execPath.includes("bun")) {
|
|
95
|
+
return "bun";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
__dirname.includes(".bun/install/global") ||
|
|
100
|
+
__dirname.includes(".bun\\install\\global")
|
|
101
|
+
) {
|
|
102
|
+
return "bun";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return userAgent ? "npm" : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const additionalDirs = [];
|
|
109
|
+
const pathDir = path.join(archRoot, "path");
|
|
110
|
+
if (existsSync(pathDir)) {
|
|
111
|
+
additionalDirs.push(pathDir);
|
|
112
|
+
}
|
|
113
|
+
const updatedPath = getUpdatedPath(additionalDirs);
|
|
114
|
+
|
|
115
|
+
const env = { ...process.env, PATH: updatedPath };
|
|
116
|
+
const packageManagerEnvVar =
|
|
117
|
+
detectPackageManager() === "bun"
|
|
118
|
+
? "VIBERUN_MANAGED_BY_BUN"
|
|
119
|
+
: "VIBERUN_MANAGED_BY_NPM";
|
|
120
|
+
env[packageManagerEnvVar] = "1";
|
|
121
|
+
|
|
122
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
123
|
+
stdio: "inherit",
|
|
124
|
+
env,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
child.on("error", (err) => {
|
|
128
|
+
// Typically triggered when the binary is missing or not executable.
|
|
129
|
+
// Re-throwing here will terminate the parent with a non-zero exit code
|
|
130
|
+
// while still printing a helpful stack trace.
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
console.error(err);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Forward common termination signals to the child so that it shuts down
|
|
137
|
+
// gracefully. In the handler we temporarily disable the default behavior of
|
|
138
|
+
// exiting immediately; once the child has been signaled we simply wait for
|
|
139
|
+
// its exit event which will in turn terminate the parent (see below).
|
|
140
|
+
const forwardSignal = (signal) => {
|
|
141
|
+
if (child.killed) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
child.kill(signal);
|
|
146
|
+
} catch {
|
|
147
|
+
/* ignore */
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
152
|
+
process.on(sig, () => forwardSignal(sig));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// When the child exits, mirror its termination reason in the parent so that
|
|
156
|
+
// shell scripts and other tooling observe the correct exit status.
|
|
157
|
+
// Wrap the lifetime of the child process in a Promise so that we can await
|
|
158
|
+
// its termination in a structured way. The Promise resolves with an object
|
|
159
|
+
// describing how the child exited: either via exit code or due to a signal.
|
|
160
|
+
const childResult = await new Promise((resolve) => {
|
|
161
|
+
child.on("exit", (code, signal) => {
|
|
162
|
+
if (signal) {
|
|
163
|
+
resolve({ type: "signal", signal });
|
|
164
|
+
} else {
|
|
165
|
+
resolve({ type: "code", exitCode: code ?? 1 });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (childResult.type === "signal") {
|
|
171
|
+
// Re-emit the same signal so that the parent terminates with the expected
|
|
172
|
+
// semantics (this also sets the correct exit code of 128 + n).
|
|
173
|
+
process.kill(process.pid, childResult.signal);
|
|
174
|
+
} else {
|
|
175
|
+
process.exit(childResult.exitCode);
|
|
176
|
+
}
|
package/package.json
CHANGED
|
@@ -1,95 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viberun",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"author": "Marvin Hagemeister <marvin@marvinhagemeister.de>",
|
|
7
|
-
"license": "MIT",
|
|
3
|
+
"version": "0.3.5",
|
|
4
|
+
"license": "BSD-3-Clause",
|
|
8
5
|
"bin": {
|
|
9
|
-
"viberun": "
|
|
6
|
+
"viberun": "bin/viberun.js"
|
|
10
7
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"watch": "tsc -w",
|
|
15
|
-
"build": "npm run cleanup && tsc",
|
|
16
|
-
"test": "mocha --compilers ts:ts-node/register 'src/**/__tests__/*.spec.ts'",
|
|
17
|
-
"test-watch": "npm t -- -w -R min",
|
|
18
|
-
"coverage": "nyc npm t",
|
|
19
|
-
"prepublish": "npm run lint && npm t && npm run build"
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=16"
|
|
20
11
|
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"awesome-typescript-loader": "^3.1.3",
|
|
31
|
-
"babel-core": "^6.24.1",
|
|
32
|
-
"babel-loader": "^7.0.0",
|
|
33
|
-
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
|
34
|
-
"babel-plugin-transform-react-jsx-self": "^6.22.0",
|
|
35
|
-
"babel-plugin-transform-react-jsx-source": "^6.22.0",
|
|
36
|
-
"babel-preset-env": "^1.5.1",
|
|
37
|
-
"css-loader": "^0.28.2",
|
|
38
|
-
"extract-text-webpack-plugin": "^2.1.0",
|
|
39
|
-
"file-loader": "^0.11.1",
|
|
40
|
-
"html-webpack-plugin": "^2.28.0",
|
|
41
|
-
"nicer-fs": "^1.0.0",
|
|
42
|
-
"node-sass": "^4.5.3",
|
|
43
|
-
"raw-loader": "^0.5.1",
|
|
44
|
-
"sass-loader": "^6.0.5",
|
|
45
|
-
"script-ext-html-webpack-plugin": "^1.8.0",
|
|
46
|
-
"style-loader": "^0.18.0",
|
|
47
|
-
"url-loader": "^0.5.8",
|
|
48
|
-
"webpack": "^2.6.0",
|
|
49
|
-
"webpack-bundle-analyzer": "^2.8.2",
|
|
50
|
-
"webpack-dev-server": "^2.4.5",
|
|
51
|
-
"webpack-merge": "^4.1.0",
|
|
52
|
-
"yargs": "^8.0.1"
|
|
53
|
-
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"@types/chai": "^3.5.1",
|
|
56
|
-
"@types/extract-text-webpack-plugin": "^2.1.0",
|
|
57
|
-
"@types/mocha": "^2.2.41",
|
|
58
|
-
"@types/node": "^7.0.22",
|
|
59
|
-
"chai": "^4.0.0",
|
|
60
|
-
"mocha": "^3.3.0",
|
|
61
|
-
"nyc": "^10.2.0",
|
|
62
|
-
"rimraf": "^2.6.1",
|
|
63
|
-
"ts-node": "^3.0.2",
|
|
64
|
-
"tslint": "^5.1.0",
|
|
65
|
-
"tslint-config-sevenval": "^0.6.0",
|
|
66
|
-
"typescript": "^2.3.1"
|
|
67
|
-
},
|
|
68
|
-
"nyc": {
|
|
69
|
-
"include": [
|
|
70
|
-
"src/*.ts",
|
|
71
|
-
"src/**/*.ts"
|
|
72
|
-
],
|
|
73
|
-
"exclude": [
|
|
74
|
-
"typings",
|
|
75
|
-
"node_modules/",
|
|
76
|
-
"**/__tests__/**",
|
|
77
|
-
"__tests__/**",
|
|
78
|
-
"**/*.d.ts"
|
|
79
|
-
],
|
|
80
|
-
"extension": [
|
|
81
|
-
".ts"
|
|
82
|
-
],
|
|
83
|
-
"require": [
|
|
84
|
-
"ts-node/register"
|
|
85
|
-
],
|
|
86
|
-
"reporter": [
|
|
87
|
-
"json",
|
|
88
|
-
"html",
|
|
89
|
-
"text",
|
|
90
|
-
"text-summary",
|
|
91
|
-
"lcovonly"
|
|
92
|
-
],
|
|
93
|
-
"all": true
|
|
12
|
+
"files": [
|
|
13
|
+
"bin",
|
|
14
|
+
"vendor",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/shayne/viberun.git"
|
|
94
21
|
}
|
|
95
22
|
}
|