bgrun 3.3.1
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/README.md +720 -0
- package/dashboard/app/api/logs/[name]/route.ts +17 -0
- package/dashboard/app/api/processes/[name]/route.ts +19 -0
- package/dashboard/app/api/processes/route.ts +150 -0
- package/dashboard/app/api/restart/[name]/route.ts +20 -0
- package/dashboard/app/api/start/route.ts +22 -0
- package/dashboard/app/api/stop/[name]/route.ts +16 -0
- package/dashboard/app/api/version/route.ts +8 -0
- package/dashboard/app/globals.css +1135 -0
- package/dashboard/app/layout.tsx +47 -0
- package/dashboard/app/page.client.tsx +554 -0
- package/dashboard/app/page.tsx +130 -0
- package/dist/index.js +1580 -0
- package/examples/bgr-startup.sh +40 -0
- package/package.json +60 -0
- package/src/api.ts +31 -0
- package/src/build.ts +26 -0
- package/src/commands/cleanup.ts +142 -0
- package/src/commands/details.ts +46 -0
- package/src/commands/list.ts +86 -0
- package/src/commands/logs.ts +49 -0
- package/src/commands/run.ts +151 -0
- package/src/commands/watch.ts +223 -0
- package/src/config.ts +37 -0
- package/src/db.ts +115 -0
- package/src/index.ts +349 -0
- package/src/logger.ts +29 -0
- package/src/platform.ts +440 -0
- package/src/schema.ts +2 -0
- package/src/server.ts +24 -0
- package/src/table.ts +230 -0
- package/src/types.ts +27 -0
- package/src/utils.ts +99 -0
- package/src/version.macro.ts +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
<](https://www.npmjs.com/package/bgr)
|
|
8
|
+
[](https://bun.sh/)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
|
|
11
|
+
Start, stop, restart, and monitor any process — from dev servers to Docker containers.
|
|
12
|
+
Zero config. One command. Beautiful dashboard included.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
bun install -g bgr
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why BGR?
|
|
23
|
+
|
|
24
|
+
| Feature | PM2 | bgr |
|
|
25
|
+
|---------|-----|-----|
|
|
26
|
+
| Runtime | Node.js | Bun (5× faster startup) |
|
|
27
|
+
| Install | `npm i -g pm2` (50+ deps) | `bun i -g bgr` (minimal deps) |
|
|
28
|
+
| Config format | JSON / JS / YAML | TOML (or none at all) |
|
|
29
|
+
| Dashboard | `pm2 monit` (TUI) | `bgr --dashboard` (full web UI) |
|
|
30
|
+
| Language support | Any | Any |
|
|
31
|
+
| Docker-aware | ❌ | ✅ detects container status |
|
|
32
|
+
| Port management | Manual | Auto-detect & cleanup |
|
|
33
|
+
| File watching | Built-in | Built-in |
|
|
34
|
+
| Programmatic API | ✅ | ✅ (first-class TypeScript) |
|
|
35
|
+
| Process persistence | ✅ | ✅ (SQLite) |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install globally
|
|
43
|
+
bun install -g bgr
|
|
44
|
+
|
|
45
|
+
# Start a process
|
|
46
|
+
bgr --name my-api --directory ./my-project --command "bun run server.ts"
|
|
47
|
+
|
|
48
|
+
# List all processes
|
|
49
|
+
bgr
|
|
50
|
+
|
|
51
|
+
# Open the web dashboard
|
|
52
|
+
bgr --dashboard
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That's it. BGR tracks the PID, captures stdout/stderr, detects the port, and survives terminal close.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Table of Contents
|
|
60
|
+
|
|
61
|
+
- [Core Commands](#core-commands)
|
|
62
|
+
- [Dashboard](#dashboard)
|
|
63
|
+
- [File Watching](#file-watching)
|
|
64
|
+
- [Port Handling](#port-handling)
|
|
65
|
+
- [Docker Integration](#docker-integration)
|
|
66
|
+
- [Caddy Reverse Proxy](#caddy-reverse-proxy)
|
|
67
|
+
- [TOML Configuration](#toml-configuration)
|
|
68
|
+
- [Programmatic API](#programmatic-api)
|
|
69
|
+
- [Migrating from PM2](#migrating-from-pm2)
|
|
70
|
+
- [Edge Cases & Behaviors](#edge-cases--behaviors)
|
|
71
|
+
- [Full CLI Reference](#full-cli-reference)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Core Commands
|
|
76
|
+
|
|
77
|
+
### Starting a process
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bgr --name my-api \
|
|
81
|
+
--directory ~/projects/my-api \
|
|
82
|
+
--command "bun run server.ts"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Short form — if you're already *in* the project directory:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bgr --name my-api --command "bun run server.ts"
|
|
89
|
+
# bgr uses current directory by default
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Listing processes
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
bgr # Pretty table
|
|
96
|
+
bgr --json # Machine-readable JSON
|
|
97
|
+
bgr --filter api # Filter by group (BGR_GROUP env)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Viewing a process
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
bgr my-api # Show status, PID, port, runtime, command
|
|
104
|
+
bgr my-api --logs # Show stdout + stderr interleaved
|
|
105
|
+
bgr my-api --logs --log-stdout --lines 50 # Last 50 stdout lines only
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Stopping, restarting, deleting
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bgr --stop my-api # Graceful stop (SIGTERM → SIGKILL)
|
|
112
|
+
bgr --restart my-api # Stop then start again with same command
|
|
113
|
+
bgr --delete my-api # Stop and remove from database
|
|
114
|
+
bgr --clean # Remove all stopped processes
|
|
115
|
+
bgr --nuke # ☠️ Delete everything
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Force restart
|
|
119
|
+
|
|
120
|
+
When a process is stuck or its port is orphaned:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
bgr --name my-api --command "bun run server.ts" --force
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--force` will:
|
|
127
|
+
1. Kill the existing process by PID
|
|
128
|
+
2. Detect all ports it was using (via OS `netstat`)
|
|
129
|
+
3. Kill any zombie processes still holding those ports
|
|
130
|
+
4. Wait for ports to free up
|
|
131
|
+
5. Start fresh
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Dashboard
|
|
136
|
+
|
|
137
|
+
BGR ships with a built-in web dashboard for managing all your processes visually.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
bgr --dashboard
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The dashboard provides:
|
|
144
|
+
- **Real-time process table** with status, PID, port, runtime
|
|
145
|
+
- **Start/stop/restart/delete** actions with one click
|
|
146
|
+
- **Log viewer** with monospace display and auto-scroll
|
|
147
|
+
- **Process detail drawer** with stdout/stderr tabs
|
|
148
|
+
- **Auto-refresh** every 3 seconds
|
|
149
|
+
|
|
150
|
+
### Dashboard port selection
|
|
151
|
+
|
|
152
|
+
The dashboard uses [Melina.js](https://github.com/7flash/melina.js) for serving and follows smart port selection:
|
|
153
|
+
|
|
154
|
+
| Scenario | Behavior |
|
|
155
|
+
|----------|----------|
|
|
156
|
+
| `bgr --dashboard` | Starts on port 3000. If busy, auto-falls back to 3001, 3002, etc. |
|
|
157
|
+
| `BUN_PORT=4000 bgr --dashboard` | Starts on port 4000. Fails with error if port is busy. |
|
|
158
|
+
| `bgr --dashboard --port 5000` | Same as `BUN_PORT=5000` — explicit, no fallback. |
|
|
159
|
+
| Dashboard already running | Prints current URL and PID instead of starting a second instance. |
|
|
160
|
+
|
|
161
|
+
The actual port is always detected from the running process and displayed correctly in `bgr` output.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## File Watching
|
|
166
|
+
|
|
167
|
+
For development, BGR can watch for file changes and auto-restart:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
bgr --name frontend \
|
|
171
|
+
--directory ~/projects/frontend \
|
|
172
|
+
--command "bun run dev" \
|
|
173
|
+
--watch
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This monitors the working directory for changes and restarts the process when files are modified. Combine with `--force` to ensure clean restarts:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
bgr --name api \
|
|
180
|
+
--command "bun run server.ts" \
|
|
181
|
+
--watch \
|
|
182
|
+
--force \
|
|
183
|
+
--config .dev.toml
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Port Handling
|
|
189
|
+
|
|
190
|
+
BGR automatically detects which TCP ports a process is listening on by querying the OS. This means:
|
|
191
|
+
|
|
192
|
+
- **No port configuration needed** — BGR discovers ports from `netstat`
|
|
193
|
+
- **No environment variable assumptions** — BGR doesn't guess `PORT` or `BUN_PORT`
|
|
194
|
+
- **Clean restarts** — `--force` kills all orphaned port bindings before restarting
|
|
195
|
+
- **Accurate display** — the port shown in `bgr` output is the *actual* bound port
|
|
196
|
+
|
|
197
|
+
### How it works
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
1. bgr spawns your process
|
|
201
|
+
2. Process starts and binds to a port (however it wants)
|
|
202
|
+
3. bgr queries `netstat -ano` (Windows) or `ss -tlnp` (Linux)
|
|
203
|
+
4. bgr finds all TCP LISTEN ports for the process PID
|
|
204
|
+
5. These ports are displayed in the table and used for cleanup
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Port conflict resolution
|
|
208
|
+
|
|
209
|
+
If you `--force` restart a process and its old port is still held by a zombie:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
1. bgr detects ports held by the old PID
|
|
213
|
+
2. Sends SIGTERM to the old process
|
|
214
|
+
3. Kills any remaining processes on those ports
|
|
215
|
+
4. Waits for ports to become free (up to 5 seconds)
|
|
216
|
+
5. Starts the new process
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Docker Integration
|
|
222
|
+
|
|
223
|
+
BGR can manage Docker containers alongside regular processes:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Start a Postgres container
|
|
227
|
+
bgr --name postgres \
|
|
228
|
+
--command "docker run --name bgr-postgres -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:16"
|
|
229
|
+
|
|
230
|
+
# Start a Redis container
|
|
231
|
+
bgr --name redis \
|
|
232
|
+
--command "docker run --name bgr-redis -p 6379:6379 redis:7-alpine"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### How BGR handles Docker
|
|
236
|
+
|
|
237
|
+
BGR is **Docker-aware** — when it detects a `docker run` command, it:
|
|
238
|
+
|
|
239
|
+
1. **Checks container status** via `docker inspect` instead of checking the PID
|
|
240
|
+
2. **Handles container lifecycle** — stops containers with `docker stop` on `bgr --stop`
|
|
241
|
+
3. **Reports correct status** — shows Running/Stopped based on container state, not process state
|
|
242
|
+
|
|
243
|
+
### Docker Compose alternative
|
|
244
|
+
|
|
245
|
+
Instead of `docker-compose.yml`, use BGR to orchestrate containers alongside your app:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
#!/bin/bash
|
|
249
|
+
# start-stack.sh
|
|
250
|
+
|
|
251
|
+
# Database
|
|
252
|
+
bgr --name db \
|
|
253
|
+
--command "docker run --name bgr-db -p 5432:5432 \
|
|
254
|
+
-v pgdata:/var/lib/postgresql/data \
|
|
255
|
+
-e POSTGRES_DB=myapp \
|
|
256
|
+
-e POSTGRES_PASSWORD=secret \
|
|
257
|
+
postgres:16" \
|
|
258
|
+
--force
|
|
259
|
+
|
|
260
|
+
# Cache
|
|
261
|
+
bgr --name cache \
|
|
262
|
+
--command "docker run --name bgr-cache -p 6379:6379 redis:7-alpine" \
|
|
263
|
+
--force
|
|
264
|
+
|
|
265
|
+
# Your app (not Docker, just a regular process)
|
|
266
|
+
bgr --name api \
|
|
267
|
+
--directory ~/projects/my-api \
|
|
268
|
+
--command "bun run server.ts" \
|
|
269
|
+
--config production.toml \
|
|
270
|
+
--force
|
|
271
|
+
|
|
272
|
+
# See everything
|
|
273
|
+
bgr
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The advantage over Docker Compose: your app processes and Docker containers are managed in the **same place** with the **same commands**.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Caddy Reverse Proxy
|
|
281
|
+
|
|
282
|
+
BGR pairs naturally with [Caddy](https://caddyserver.com/) for production deployments with automatic HTTPS.
|
|
283
|
+
|
|
284
|
+
### Basic setup
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Start your app on any port (BGR detects it)
|
|
288
|
+
bgr --name my-api --command "bun run server.ts" --force
|
|
289
|
+
|
|
290
|
+
# Check which port it got
|
|
291
|
+
bgr
|
|
292
|
+
# → my-api ● Running :3000 bun run server.ts
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Caddyfile:**
|
|
296
|
+
|
|
297
|
+
```caddy
|
|
298
|
+
api.example.com {
|
|
299
|
+
reverse_proxy localhost:3000
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
dashboard.example.com {
|
|
303
|
+
reverse_proxy localhost:3001
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Multi-service setup
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Start services
|
|
311
|
+
bgr --name api --command "bun run api/server.ts" --force
|
|
312
|
+
bgr --name frontend --command "bun run frontend/server.ts" --force
|
|
313
|
+
bgr --name admin --command "bun run admin/server.ts" --force
|
|
314
|
+
|
|
315
|
+
# Start dashboard
|
|
316
|
+
bgr --dashboard
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Caddyfile:**
|
|
320
|
+
|
|
321
|
+
```caddy
|
|
322
|
+
api.example.com {
|
|
323
|
+
reverse_proxy localhost:3000
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
app.example.com {
|
|
327
|
+
reverse_proxy localhost:3001
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
admin.example.com {
|
|
331
|
+
reverse_proxy localhost:3002
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
status.example.com {
|
|
335
|
+
reverse_proxy localhost:3003 # BGR dashboard
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Managing Caddy with BGR
|
|
340
|
+
|
|
341
|
+
You can even manage Caddy itself as a BGR process:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
bgr --name caddy \
|
|
345
|
+
--directory /etc/caddy \
|
|
346
|
+
--command "caddy run --config Caddyfile" \
|
|
347
|
+
--force
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Now `bgr` shows your entire stack — app servers, databases, and reverse proxy — in one place.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## TOML Configuration
|
|
355
|
+
|
|
356
|
+
BGR loads TOML config files and flattens them into environment variables:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
bgr --name api --command "bun run server.ts" --config production.toml
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```toml
|
|
363
|
+
# production.toml
|
|
364
|
+
[server]
|
|
365
|
+
port = 3000
|
|
366
|
+
host = "0.0.0.0"
|
|
367
|
+
|
|
368
|
+
[database]
|
|
369
|
+
url = "postgresql://localhost/myapp"
|
|
370
|
+
pool_size = 10
|
|
371
|
+
|
|
372
|
+
[auth]
|
|
373
|
+
jwt_secret = "your-secret-here"
|
|
374
|
+
session_ttl = 3600
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Becomes:**
|
|
378
|
+
```
|
|
379
|
+
SERVER_PORT=3000
|
|
380
|
+
SERVER_HOST=0.0.0.0
|
|
381
|
+
DATABASE_URL=postgresql://localhost/myapp
|
|
382
|
+
DATABASE_POOL_SIZE=10
|
|
383
|
+
AUTH_JWT_SECRET=your-secret-here
|
|
384
|
+
AUTH_SESSION_TTL=3600
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
The convention: `[section]` becomes the prefix, `key` becomes the suffix, joined with `_`, uppercased.
|
|
388
|
+
|
|
389
|
+
If no `--config` is specified, BGR looks for `.config.toml` in the working directory automatically.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Programmatic API
|
|
394
|
+
|
|
395
|
+
BGR exposes its internals as importable TypeScript functions:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
bun add bgr
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Process management
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
import {
|
|
405
|
+
getAllProcesses,
|
|
406
|
+
getProcess,
|
|
407
|
+
isProcessRunning,
|
|
408
|
+
terminateProcess,
|
|
409
|
+
handleRun,
|
|
410
|
+
getProcessPorts,
|
|
411
|
+
readFileTail,
|
|
412
|
+
calculateRuntime,
|
|
413
|
+
} from 'bgr'
|
|
414
|
+
|
|
415
|
+
// List all processes
|
|
416
|
+
const procs = getAllProcesses()
|
|
417
|
+
|
|
418
|
+
// Start a process programmatically
|
|
419
|
+
await handleRun({
|
|
420
|
+
action: 'run',
|
|
421
|
+
name: 'my-api',
|
|
422
|
+
command: 'bun run server.ts',
|
|
423
|
+
directory: '/path/to/project',
|
|
424
|
+
force: true,
|
|
425
|
+
remoteName: '',
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
// Check status
|
|
429
|
+
const proc = getProcess('my-api')
|
|
430
|
+
if (proc) {
|
|
431
|
+
const alive = await isProcessRunning(proc.pid)
|
|
432
|
+
const ports = await getProcessPorts(proc.pid)
|
|
433
|
+
const runtime = calculateRuntime(proc.timestamp)
|
|
434
|
+
console.log({ alive, ports, runtime })
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Read logs
|
|
438
|
+
const proc = getProcess('my-api')
|
|
439
|
+
if (proc) {
|
|
440
|
+
const stdout = await readFileTail(proc.stdout_path, 100) // last 100 lines
|
|
441
|
+
const stderr = await readFileTail(proc.stderr_path, 100)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Stop a process
|
|
445
|
+
await terminateProcess(proc.pid)
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Build a custom dashboard
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import { getAllProcesses, isProcessRunning, calculateRuntime } from 'bgr'
|
|
452
|
+
|
|
453
|
+
// Express/Hono/Elysia endpoint
|
|
454
|
+
export async function GET() {
|
|
455
|
+
const procs = getAllProcesses()
|
|
456
|
+
const enriched = await Promise.all(
|
|
457
|
+
procs.map(async (p) => ({
|
|
458
|
+
name: p.name,
|
|
459
|
+
pid: p.pid,
|
|
460
|
+
running: await isProcessRunning(p.pid),
|
|
461
|
+
runtime: calculateRuntime(p.timestamp),
|
|
462
|
+
}))
|
|
463
|
+
)
|
|
464
|
+
return Response.json(enriched)
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Migrating from PM2
|
|
471
|
+
|
|
472
|
+
If you're coming from PM2, here's a direct mapping of commands:
|
|
473
|
+
|
|
474
|
+
### Command mapping
|
|
475
|
+
|
|
476
|
+
| PM2 | BGR |
|
|
477
|
+
|-----|-----|
|
|
478
|
+
| `pm2 start app.js --name api` | `bgr --name api --command "node app.js"` |
|
|
479
|
+
| `pm2 start app.js -i max` | *(cluster mode not supported — use multiple named processes)* |
|
|
480
|
+
| `pm2 list` | `bgr` |
|
|
481
|
+
| `pm2 show api` | `bgr api` |
|
|
482
|
+
| `pm2 logs api` | `bgr api --logs` |
|
|
483
|
+
| `pm2 logs api --lines 50` | `bgr api --logs --lines 50` |
|
|
484
|
+
| `pm2 stop api` | `bgr --stop api` |
|
|
485
|
+
| `pm2 restart api` | `bgr --restart api` |
|
|
486
|
+
| `pm2 delete api` | `bgr --delete api` |
|
|
487
|
+
| `pm2 flush` | `bgr --clean` |
|
|
488
|
+
| `pm2 kill` | `bgr --nuke` |
|
|
489
|
+
| `pm2 monit` | `bgr --dashboard` |
|
|
490
|
+
| `pm2 save` / `pm2 resurrect` | *(automatic — processes persist in SQLite)* |
|
|
491
|
+
|
|
492
|
+
### ecosystem.config.js → TOML + shell script
|
|
493
|
+
|
|
494
|
+
**PM2 ecosystem file:**
|
|
495
|
+
|
|
496
|
+
```javascript
|
|
497
|
+
// ecosystem.config.js
|
|
498
|
+
module.exports = {
|
|
499
|
+
apps: [
|
|
500
|
+
{
|
|
501
|
+
name: 'api',
|
|
502
|
+
script: 'server.js',
|
|
503
|
+
cwd: './api',
|
|
504
|
+
env: { PORT: 3000, NODE_ENV: 'production' },
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: 'worker',
|
|
508
|
+
script: 'worker.js',
|
|
509
|
+
cwd: './workers',
|
|
510
|
+
env: { QUEUE: 'default' },
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**BGR equivalent:**
|
|
517
|
+
|
|
518
|
+
```toml
|
|
519
|
+
# api.toml
|
|
520
|
+
[server]
|
|
521
|
+
port = 3000
|
|
522
|
+
|
|
523
|
+
[node]
|
|
524
|
+
env = "production"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
#!/bin/bash
|
|
529
|
+
# start.sh
|
|
530
|
+
bgr --name api --directory ./api --command "node server.js" --config api.toml --force
|
|
531
|
+
bgr --name worker --directory ./workers --command "node worker.js" --force
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Key differences
|
|
535
|
+
|
|
536
|
+
1. **No cluster mode** — BGR manages independent processes. For multi-core, run multiple named instances (`api-1`, `api-2`) behind a load balancer.
|
|
537
|
+
|
|
538
|
+
2. **No `pm2 startup`** — BGR doesn't install itself as a system service. Use your OS init system (systemd, launchd, Windows Task Scheduler) to run BGR at boot:
|
|
539
|
+
|
|
540
|
+
```ini
|
|
541
|
+
# /etc/systemd/system/bgr-api.service
|
|
542
|
+
[Unit]
|
|
543
|
+
Description=My API via BGR
|
|
544
|
+
|
|
545
|
+
[Service]
|
|
546
|
+
ExecStart=/usr/local/bin/bgr --name api --directory /var/www/api --command "bun run server.ts" --force
|
|
547
|
+
Restart=always
|
|
548
|
+
|
|
549
|
+
[Install]
|
|
550
|
+
WantedBy=multi-user.target
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
3. **No log rotation** — BGR writes to plain text files in `~/.bgr/`. Use `logrotate` or similar tools, or specify custom log paths with `--stdout` and `--stderr`.
|
|
554
|
+
|
|
555
|
+
4. **Bun required** — BGR runs on Bun, but the *processes it manages* can be anything: Node.js, Python, Ruby, Go, Docker, shell scripts.
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Edge Cases & Behaviors
|
|
560
|
+
|
|
561
|
+
### What happens when a process crashes?
|
|
562
|
+
|
|
563
|
+
BGR records the process as **Stopped**. The PID and log files are preserved so you can inspect what happened:
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
bgr my-api --logs --log-stderr
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
For auto-restart on crash, use the guard script:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
bun run guard.ts my-api 30 # Check every 30 seconds, restart if dead
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### What happens on `bgr --force` if the port is stuck?
|
|
576
|
+
|
|
577
|
+
BGR queries the OS for all TCP ports held by the old PID, kills them, and waits up to 5 seconds for cleanup. If ports are still held after that, the new process starts anyway (and will likely pick a different port).
|
|
578
|
+
|
|
579
|
+
### What happens if I start two processes with the same name?
|
|
580
|
+
|
|
581
|
+
The new process replaces the old one. If the old one is still running, use `--force` to kill it first. Without `--force`, BGR will refuse to start if a process with that name is already running.
|
|
582
|
+
|
|
583
|
+
### What happens if BGR itself is killed?
|
|
584
|
+
|
|
585
|
+
The managed processes keep running — they're independent OS processes. When you run `bgr` again, it reconnects to the SQLite database and checks which PIDs are still alive. Dead processes are marked as **Stopped**.
|
|
586
|
+
|
|
587
|
+
### What about Windows?
|
|
588
|
+
|
|
589
|
+
BGR works on Windows. Process management uses `taskkill` and `wmic` instead of Unix signals. Port detection uses `netstat -ano`. The dashboard runs in your browser, so it works everywhere.
|
|
590
|
+
|
|
591
|
+
### Can I manage processes on a remote server?
|
|
592
|
+
|
|
593
|
+
Not directly — BGR manages processes on the local machine. For remote management, run BGR on the remote server and expose the dashboard behind a reverse proxy (see [Caddy section](#caddy-reverse-proxy)).
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Custom Log Paths
|
|
598
|
+
|
|
599
|
+
By default, logs go to `~/.bgr/<name>-out.txt` and `~/.bgr/<name>-err.txt`. Override with:
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
bgr --name api \
|
|
603
|
+
--command "bun run server.ts" \
|
|
604
|
+
--stdout /var/log/api/stdout.log \
|
|
605
|
+
--stderr /var/log/api/stderr.log
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Process Groups
|
|
611
|
+
|
|
612
|
+
Tag processes with `BGR_GROUP` to organize and filter them:
|
|
613
|
+
|
|
614
|
+
```bash
|
|
615
|
+
BGR_GROUP=prod bgr --name api --command "bun run server.ts" --force
|
|
616
|
+
BGR_GROUP=prod bgr --name worker --command "bun run worker.ts" --force
|
|
617
|
+
BGR_GROUP=dev bgr --name dev-server --command "bun run dev" --force
|
|
618
|
+
|
|
619
|
+
# Show only production processes
|
|
620
|
+
bgr --filter prod
|
|
621
|
+
|
|
622
|
+
# Show only dev processes
|
|
623
|
+
bgr --filter dev
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## Git Integration
|
|
629
|
+
|
|
630
|
+
Pull the latest changes before starting:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
bgr --name api \
|
|
634
|
+
--directory ~/projects/api \
|
|
635
|
+
--command "bun run server.ts" \
|
|
636
|
+
--fetch \
|
|
637
|
+
--force
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
`--fetch` runs `git pull` in the working directory before starting the process. Combine with `--force` for a clean deploy workflow:
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
# Deploy script
|
|
644
|
+
bgr --name api --directory /var/www/api --command "bun run server.ts" --fetch --force
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## File Structure
|
|
650
|
+
|
|
651
|
+
```
|
|
652
|
+
~/.bgr/
|
|
653
|
+
├── bgr.sqlite # Process database (SQLite)
|
|
654
|
+
├── myapp-out.txt # stdout logs
|
|
655
|
+
├── myapp-err.txt # stderr logs
|
|
656
|
+
├── bgr-dashboard-out.txt # Dashboard stdout
|
|
657
|
+
└── bgr-dashboard-err.txt # Dashboard stderr
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
All state lives in `~/.bgr/`. To reset everything, delete this directory.
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## Full CLI Reference
|
|
665
|
+
|
|
666
|
+
| Option | Description | Default |
|
|
667
|
+
|--------|-------------|---------|
|
|
668
|
+
| `--name <name>` | Process name | *(required for start)* |
|
|
669
|
+
| `--directory <path>` | Working directory | Current directory |
|
|
670
|
+
| `--command <cmd>` | Command to execute | *(required for start)* |
|
|
671
|
+
| `--config <path>` | TOML config file for env vars | `.config.toml` |
|
|
672
|
+
| `--force` | Kill existing process and ports before starting | `false` |
|
|
673
|
+
| `--fetch` | Git pull before starting | `false` |
|
|
674
|
+
| `--watch` | Auto-restart on file changes | `false` |
|
|
675
|
+
| `--stdout <path>` | Custom stdout log path | `~/.bgr/<name>-out.txt` |
|
|
676
|
+
| `--stderr <path>` | Custom stderr log path | `~/.bgr/<name>-err.txt` |
|
|
677
|
+
| `--db <path>` | Custom SQLite database path | `~/.bgr/bgr.sqlite` |
|
|
678
|
+
| `--json` | Output process list as JSON | `false` |
|
|
679
|
+
| `--filter <group>` | Filter by `BGR_GROUP` | *(show all)* |
|
|
680
|
+
| `--logs` | Show process logs | `false` |
|
|
681
|
+
| `--log-stdout` | Show only stdout | `false` |
|
|
682
|
+
| `--log-stderr` | Show only stderr | `false` |
|
|
683
|
+
| `--lines <n>` | Number of log lines | All |
|
|
684
|
+
| `--stop <name>` | Stop a process | - |
|
|
685
|
+
| `--restart <name>` | Restart a process | - |
|
|
686
|
+
| `--delete <name>` | Delete a process | - |
|
|
687
|
+
| `--clean` | Remove stopped processes | - |
|
|
688
|
+
| `--nuke` | Delete ALL processes | - |
|
|
689
|
+
| `--dashboard` | Launch web dashboard | - |
|
|
690
|
+
| `--version` | Show version | - |
|
|
691
|
+
| `--help` | Show help | - |
|
|
692
|
+
|
|
693
|
+
### Environment Variables
|
|
694
|
+
|
|
695
|
+
| Variable | Description | Default |
|
|
696
|
+
|----------|-------------|---------|
|
|
697
|
+
| `DB_NAME` | Custom database file name | `bgr` |
|
|
698
|
+
| `BGR_GROUP` | Assign process to a group | *(none)* |
|
|
699
|
+
| `BUN_PORT` | Dashboard port (explicit, no fallback) | *(auto: 3000+)* |
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## Requirements
|
|
704
|
+
|
|
705
|
+
- [Bun](https://bun.sh) v1.0.0+
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## License
|
|
710
|
+
|
|
711
|
+
MIT
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
<div align="center">
|
|
716
|
+
|
|
717
|
+
Built by [Mements](https://github.com/Mements) with ⚡ Bun
|
|
718
|
+
|
|
719
|
+
</div>
|
|
720
|
+
]]>
|