bgrun 3.4.0 → 3.7.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 +26 -3
- package/dashboard/app/api/config/[name]/route.ts +55 -0
- package/dashboard/app/api/debug/route.ts +3 -2
- package/dashboard/app/api/events/route.ts +60 -0
- package/dashboard/app/api/logs/[name]/route.ts +55 -5
- package/dashboard/app/api/processes/[name]/route.ts +5 -2
- package/dashboard/app/api/processes/route.ts +88 -44
- package/dashboard/app/api/restart/[name]/route.ts +4 -3
- package/dashboard/app/api/start/route.ts +4 -3
- package/dashboard/app/api/stop/[name]/route.ts +11 -4
- package/dashboard/app/api/version/route.ts +4 -2
- package/dashboard/app/globals.css +657 -87
- package/dashboard/app/layout.tsx +2 -23
- package/dashboard/app/page.client.tsx +703 -116
- package/dashboard/app/page.tsx +97 -33
- package/dist/index.js +394 -224
- package/image.png +0 -0
- package/package.json +60 -56
- package/src/api.ts +13 -0
- package/src/commands/list.ts +1 -0
- package/src/commands/run.ts +60 -47
- package/src/db.ts +27 -5
- package/src/index.ts +75 -3
- package/src/platform.ts +202 -96
- package/src/server.ts +2 -1
- package/src/types.ts +2 -2
package/image.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,56 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "bgrun",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "bgrun — A lightweight process manager for Bun",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./src/api.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./src/api.ts"
|
|
9
|
-
},
|
|
10
|
-
"bin": {
|
|
11
|
-
"bgrun": "./dist/index.js"
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "bun run ./src/build.ts",
|
|
15
|
-
"test": "bun test",
|
|
16
|
-
"prepublishOnly": "bun run build"
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"src",
|
|
21
|
-
"dashboard/app",
|
|
22
|
-
"README.md",
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "bgrun",
|
|
3
|
+
"version": "3.7.1",
|
|
4
|
+
"description": "bgrun — A lightweight process manager for Bun",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/api.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/api.ts"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"bgrun": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "bun run ./src/build.ts",
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"prepublishOnly": "bun run build"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"dashboard/app",
|
|
22
|
+
"README.md",
|
|
23
|
+
"image.png",
|
|
24
|
+
"examples/bgr-startup.sh"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"process-manager",
|
|
28
|
+
"bun",
|
|
29
|
+
"monitoring",
|
|
30
|
+
"devops",
|
|
31
|
+
"deployment",
|
|
32
|
+
"background",
|
|
33
|
+
"daemon"
|
|
34
|
+
],
|
|
35
|
+
"author": "7flash",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/7flash/bgrun.git"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"bun-types": "latest"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"typescript": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"boxen": "^8.0.1",
|
|
49
|
+
"chalk": "^5.4.1",
|
|
50
|
+
"dedent": "^1.5.3",
|
|
51
|
+
"measure-fn": "^3.2.1",
|
|
52
|
+
"melina": "^2.2.1",
|
|
53
|
+
"react": "^19.2.4",
|
|
54
|
+
"react-dom": "^19.2.4",
|
|
55
|
+
"sqlite-zod-orm": "^3.8.0"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"bun": ">=1.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/api.ts
CHANGED
|
@@ -48,3 +48,16 @@ export { handleRun } from './commands/run'
|
|
|
48
48
|
|
|
49
49
|
// --- Utilities ---
|
|
50
50
|
export { getVersion, calculateRuntime, parseEnvString, validateDirectory } from './utils'
|
|
51
|
+
|
|
52
|
+
// --- Default Export (namespace style) ---
|
|
53
|
+
import { getAllProcesses, getProcess, insertProcess, removeProcess, removeProcessByName, removeAllProcesses, retryDatabaseOperation, getDbInfo, dbPath, bgrHome } from './db'
|
|
54
|
+
import { isProcessRunning, terminateProcess, readFileTail, getProcessPorts, findChildPid, findPidByPort, getShellCommand, killProcessOnPort, waitForPortFree, ensureDir, getHomeDir, isWindows, getProcessBatchMemory, getProcessMemory } from './platform'
|
|
55
|
+
import { handleRun } from './commands/run'
|
|
56
|
+
import { getVersion, calculateRuntime, parseEnvString, validateDirectory } from './utils'
|
|
57
|
+
|
|
58
|
+
export default {
|
|
59
|
+
getAllProcesses, getProcess, insertProcess, removeProcess, removeProcessByName, removeAllProcesses, retryDatabaseOperation, getDbInfo, dbPath, bgrHome,
|
|
60
|
+
isProcessRunning, terminateProcess, readFileTail, getProcessPorts, findChildPid, findPidByPort, getShellCommand, killProcessOnPort, waitForPortFree, ensureDir, getHomeDir, isWindows, getProcessBatchMemory, getProcessMemory,
|
|
61
|
+
handleRun,
|
|
62
|
+
getVersion, calculateRuntime, parseEnvString, validateDirectory,
|
|
63
|
+
}
|
package/src/commands/list.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { getAllProcesses } from "../db";
|
|
|
5
5
|
import { announce } from "../logger";
|
|
6
6
|
import { isProcessRunning, calculateRuntime, parseEnvString } from "../utils";
|
|
7
7
|
import { getProcessPorts, getProcessBatchMemory } from "../platform";
|
|
8
|
+
import { measure } from "measure-fn";
|
|
8
9
|
|
|
9
10
|
function formatMemory(bytes: number): string {
|
|
10
11
|
if (bytes === 0) return '-';
|
package/src/commands/run.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandOptions } from "../types";
|
|
1
|
+
import type { CommandOptions } from "../types";
|
|
2
2
|
import { getProcess, removeProcessByName, retryDatabaseOperation, insertProcess } from "../db";
|
|
3
3
|
import { isProcessRunning, terminateProcess, getHomeDir, getShellCommand, killProcessOnPort, findChildPid, getProcessPorts, waitForPortFree } from "../platform";
|
|
4
4
|
import { error, announce } from "../logger";
|
|
@@ -7,8 +7,10 @@ import { parseConfigFile } from "../config";
|
|
|
7
7
|
import { $ } from "bun";
|
|
8
8
|
import { sleep } from "bun";
|
|
9
9
|
import { join } from "path";
|
|
10
|
+
import { createMeasure } from "measure-fn";
|
|
10
11
|
|
|
11
12
|
const homePath = getHomeDir();
|
|
13
|
+
const run = createMeasure('run');
|
|
12
14
|
|
|
13
15
|
export async function handleRun(options: CommandOptions) {
|
|
14
16
|
const { command, directory, env, name, configPath, force, fetch, stdout, stderr } = options;
|
|
@@ -24,18 +26,20 @@ export async function handleRun(options: CommandOptions) {
|
|
|
24
26
|
if (!require('fs').existsSync(require('path').join(finalDirectory, '.git'))) {
|
|
25
27
|
error(`Cannot --fetch: '${finalDirectory}' is not a Git repository.`);
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
await run.measure(`Git fetch "${name}"`, async () => {
|
|
30
|
+
try {
|
|
31
|
+
await $`git fetch origin`;
|
|
32
|
+
const localHash = (await $`git rev-parse HEAD`.text()).trim();
|
|
33
|
+
const remoteHash = (await $`git rev-parse origin/$(git rev-parse --abbrev-ref HEAD)`.text()).trim();
|
|
34
|
+
|
|
35
|
+
if (localHash !== remoteHash) {
|
|
36
|
+
await $`git pull origin $(git rev-parse --abbrev-ref HEAD)`;
|
|
37
|
+
announce("📥 Pulled latest changes", "Git Update");
|
|
38
|
+
}
|
|
39
|
+
} catch (err) {
|
|
40
|
+
error(`Failed to pull latest changes: ${err}`);
|
|
35
41
|
}
|
|
36
|
-
}
|
|
37
|
-
error(`Failed to pull latest changes: ${err}`);
|
|
38
|
-
}
|
|
42
|
+
});
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
const isRunning = await isProcessRunning(existingProcess.pid);
|
|
@@ -50,23 +54,26 @@ export async function handleRun(options: CommandOptions) {
|
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
if (isRunning) {
|
|
53
|
-
await
|
|
54
|
-
|
|
57
|
+
await run.measure(`Terminate "${name}" (PID ${existingProcess.pid})`, async () => {
|
|
58
|
+
await terminateProcess(existingProcess.pid);
|
|
59
|
+
announce(`🔥 Terminated existing process '${name}'`, "Process Terminated");
|
|
60
|
+
});
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
// Kill anything still on the ports the old process was using
|
|
58
|
-
|
|
59
|
-
await
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
if (detectedPorts.length > 0) {
|
|
65
|
+
await run.measure(`Port cleanup [${detectedPorts.join(', ')}]`, async () => {
|
|
66
|
+
for (const port of detectedPorts) {
|
|
67
|
+
await killProcessOnPort(port);
|
|
68
|
+
}
|
|
69
|
+
for (const port of detectedPorts) {
|
|
70
|
+
const freed = await waitForPortFree(port, 5000);
|
|
71
|
+
if (!freed) {
|
|
72
|
+
await killProcessOnPort(port);
|
|
73
|
+
await waitForPortFree(port, 3000);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
await retryDatabaseOperation(() =>
|
|
@@ -97,12 +104,17 @@ export async function handleRun(options: CommandOptions) {
|
|
|
97
104
|
const fullConfigPath = join(finalDirectory, finalConfigPath);
|
|
98
105
|
|
|
99
106
|
if (await Bun.file(fullConfigPath).exists()) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
const configEnv = await run.measure(`Parse config "${finalConfigPath}"`, async () => {
|
|
108
|
+
try {
|
|
109
|
+
return await parseConfigFile(fullConfigPath);
|
|
110
|
+
} catch (err: any) {
|
|
111
|
+
console.warn(`Warning: Failed to parse config file ${finalConfigPath}: ${err.message}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (configEnv) {
|
|
116
|
+
finalEnv = { ...finalEnv, ...configEnv };
|
|
103
117
|
console.log(`Loaded config from ${finalConfigPath}`);
|
|
104
|
-
} catch (err: any) {
|
|
105
|
-
console.warn(`Warning: Failed to parse config file ${finalConfigPath}: ${err.message}`);
|
|
106
118
|
}
|
|
107
119
|
} else {
|
|
108
120
|
console.log(`Config file '${finalConfigPath}' not found, continuing without it.`);
|
|
@@ -114,20 +126,23 @@ export async function handleRun(options: CommandOptions) {
|
|
|
114
126
|
const stderrPath = stderr || existingProcess?.stderr_path || join(homePath, ".bgr", `${name}-err.txt`);
|
|
115
127
|
Bun.write(stderrPath, '');
|
|
116
128
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
const actualPid = await run.measure(`Spawn "${name}" → ${finalCommand}`, async () => {
|
|
130
|
+
const newProcess = Bun.spawn(getShellCommand(finalCommand!), {
|
|
131
|
+
env: { ...Bun.env, ...finalEnv },
|
|
132
|
+
cwd: finalDirectory,
|
|
133
|
+
stdout: Bun.file(stdoutPath),
|
|
134
|
+
stderr: Bun.file(stderrPath),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
newProcess.unref();
|
|
138
|
+
// Give shell a moment to spawn child, then find PID before shell exits
|
|
139
|
+
await sleep(100);
|
|
140
|
+
// Find the actual child PID (shell wrapper exits immediately after spawning)
|
|
141
|
+
const pid = await findChildPid(newProcess.pid);
|
|
142
|
+
// Wait more for subprocess to initialize
|
|
143
|
+
await sleep(400);
|
|
144
|
+
return pid;
|
|
145
|
+
}) ?? 0;
|
|
131
146
|
|
|
132
147
|
await retryDatabaseOperation(() =>
|
|
133
148
|
insertProcess({
|
|
@@ -147,5 +162,3 @@ export async function handleRun(options: CommandOptions) {
|
|
|
147
162
|
"Process Started"
|
|
148
163
|
);
|
|
149
164
|
}
|
|
150
|
-
|
|
151
|
-
|
package/src/db.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Database, z } from "sqlite-zod-orm";
|
|
|
2
2
|
import { getHomeDir, ensureDir } from "./platform";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { sleep } from "bun";
|
|
5
|
+
import { existsSync, copyFileSync } from "fs";
|
|
5
6
|
|
|
6
7
|
// =============================================================================
|
|
7
8
|
// SCHEMA (inline — single table, no need for a separate file)
|
|
@@ -27,11 +28,24 @@ export type Process = z.infer<typeof ProcessSchema> & { id: number };
|
|
|
27
28
|
|
|
28
29
|
const homePath = getHomeDir();
|
|
29
30
|
const bgrDir = join(homePath, ".bgr");
|
|
30
|
-
const dbName = process.env.DB_NAME ?? "bgr";
|
|
31
|
-
export const dbPath = join(bgrDir, `${dbName}_v2.sqlite`);
|
|
32
|
-
export const bgrHome = bgrDir;
|
|
33
31
|
ensureDir(bgrDir);
|
|
34
32
|
|
|
33
|
+
// DB filename: configurable via BGRUN_DB env, default "bgrun.sqlite"
|
|
34
|
+
const dbFilename = process.env.BGRUN_DB ?? "bgrun.sqlite";
|
|
35
|
+
export const dbPath = join(bgrDir, dbFilename);
|
|
36
|
+
export const bgrHome = bgrDir;
|
|
37
|
+
|
|
38
|
+
// Auto-migration: if new DB doesn't exist but old one does, copy it over
|
|
39
|
+
const legacyDbPath = join(bgrDir, "bgr_v2.sqlite");
|
|
40
|
+
if (!existsSync(dbPath) && existsSync(legacyDbPath)) {
|
|
41
|
+
try {
|
|
42
|
+
copyFileSync(legacyDbPath, dbPath);
|
|
43
|
+
console.log(`[bgrun] Migrated database: ${legacyDbPath} → ${dbPath}`);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Migration failed — start fresh
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
export const db = new Database(dbPath, {
|
|
36
50
|
process: ProcessSchema,
|
|
37
51
|
}, {
|
|
@@ -90,6 +104,14 @@ export function removeProcessByName(name: string) {
|
|
|
90
104
|
}
|
|
91
105
|
}
|
|
92
106
|
|
|
107
|
+
/** Update the stored PID for a process (used by PID reconciliation) */
|
|
108
|
+
export function updateProcessPid(name: string, newPid: number) {
|
|
109
|
+
const proc = db.process.select().where({ name }).limit(1).get();
|
|
110
|
+
if (proc) {
|
|
111
|
+
db.process.update(proc.id, { pid: newPid });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
93
115
|
export function removeAllProcesses() {
|
|
94
116
|
const all = db.process.select().all();
|
|
95
117
|
for (const p of all) {
|
|
@@ -105,8 +127,8 @@ export function getDbInfo() {
|
|
|
105
127
|
return {
|
|
106
128
|
dbPath,
|
|
107
129
|
bgrHome,
|
|
108
|
-
|
|
109
|
-
exists:
|
|
130
|
+
dbFilename,
|
|
131
|
+
exists: existsSync(dbPath),
|
|
110
132
|
};
|
|
111
133
|
}
|
|
112
134
|
|
package/src/index.ts
CHANGED
|
@@ -10,13 +10,21 @@ import { showLogs } from "./commands/logs";
|
|
|
10
10
|
import { showDetails } from "./commands/details";
|
|
11
11
|
import type { CommandOptions } from "./types";
|
|
12
12
|
import { error, announce } from "./logger";
|
|
13
|
-
|
|
13
|
+
// startServer is dynamically imported only when --_serve is used
|
|
14
|
+
// to avoid loading melina (which has side-effects) on every bgrun command
|
|
14
15
|
import { getHomeDir, getShellCommand, findChildPid, isProcessRunning, terminateProcess, getProcessPorts, killProcessOnPort, waitForPortFree } from "./platform";
|
|
15
16
|
import { insertProcess, removeProcessByName, getProcess, retryDatabaseOperation, getDbInfo } from "./db";
|
|
16
17
|
import dedent from "dedent";
|
|
17
18
|
import chalk from "chalk";
|
|
18
19
|
import { join } from "path";
|
|
19
20
|
import { sleep } from "bun";
|
|
21
|
+
import { configure } from "measure-fn";
|
|
22
|
+
|
|
23
|
+
if (!Bun.argv.includes("--_serve")) {
|
|
24
|
+
if (!Bun.env.MEASURE_SILENT) {
|
|
25
|
+
configure({ silent: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
20
28
|
|
|
21
29
|
async function showHelp() {
|
|
22
30
|
const usage = dedent`
|
|
@@ -31,7 +39,9 @@ async function showHelp() {
|
|
|
31
39
|
bgrun [name] Show details for a process
|
|
32
40
|
bgrun --dashboard Launch web dashboard (managed by bgrun)
|
|
33
41
|
bgrun --restart [name] Restart a process
|
|
42
|
+
bgrun --restart-all Restart ALL registered processes
|
|
34
43
|
bgrun --stop [name] Stop a process (keep in registry)
|
|
44
|
+
bgrun --stop-all Stop ALL running processes
|
|
35
45
|
bgrun --delete [name] Delete a process
|
|
36
46
|
bgrun --clean Remove all stopped processes
|
|
37
47
|
bgrun --nuke Delete ALL processes
|
|
@@ -79,7 +89,9 @@ async function run() {
|
|
|
79
89
|
delete: { type: 'boolean' },
|
|
80
90
|
nuke: { type: 'boolean' },
|
|
81
91
|
restart: { type: 'boolean' },
|
|
92
|
+
"restart-all": { type: 'boolean' },
|
|
82
93
|
stop: { type: 'boolean' },
|
|
94
|
+
"stop-all": { type: 'boolean' },
|
|
83
95
|
clean: { type: 'boolean' },
|
|
84
96
|
json: { type: 'boolean' },
|
|
85
97
|
logs: { type: 'boolean' },
|
|
@@ -105,6 +117,7 @@ async function run() {
|
|
|
105
117
|
// Port is NOT passed explicitly — Melina auto-detects from BUN_PORT env
|
|
106
118
|
// or defaults to 3000 with fallback to next available port.
|
|
107
119
|
if (values['_serve']) {
|
|
120
|
+
const { startServer } = await import("./server");
|
|
108
121
|
await startServer();
|
|
109
122
|
return;
|
|
110
123
|
}
|
|
@@ -121,7 +134,15 @@ async function run() {
|
|
|
121
134
|
// Check if dashboard is already running
|
|
122
135
|
const existing = getProcess(dashboardName);
|
|
123
136
|
if (existing && await isProcessRunning(existing.pid)) {
|
|
124
|
-
|
|
137
|
+
// The stored PID may be the shell wrapper (cmd.exe), not the actual bun process
|
|
138
|
+
// Try the stored PID first, then traverse the process tree to find the real one
|
|
139
|
+
let existingPorts = await getProcessPorts(existing.pid);
|
|
140
|
+
if (existingPorts.length === 0) {
|
|
141
|
+
const childPid = await findChildPid(existing.pid);
|
|
142
|
+
if (childPid !== existing.pid) {
|
|
143
|
+
existingPorts = await getProcessPorts(childPid);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
125
146
|
const portStr = existingPorts.length > 0 ? `:${existingPorts[0]}` : '(detecting...)';
|
|
126
147
|
announce(
|
|
127
148
|
`Dashboard is already running (PID ${existing.pid})\n\n` +
|
|
@@ -244,7 +265,7 @@ async function run() {
|
|
|
244
265
|
Version: ${chalk.cyan(version)}
|
|
245
266
|
BGR Home: ${chalk.yellow(info.bgrHome)}
|
|
246
267
|
DB Path: ${chalk.yellow(info.dbPath)}
|
|
247
|
-
DB
|
|
268
|
+
DB File: ${info.dbFilename}
|
|
248
269
|
DB Exists: ${info.exists ? chalk.green('✓') : chalk.red('✗')}
|
|
249
270
|
Platform: ${process.platform}
|
|
250
271
|
Bun: ${Bun.version}
|
|
@@ -263,6 +284,57 @@ async function run() {
|
|
|
263
284
|
return;
|
|
264
285
|
}
|
|
265
286
|
|
|
287
|
+
// Restart all registered processes
|
|
288
|
+
if (values['restart-all']) {
|
|
289
|
+
const { getAllProcesses } = await import('./db');
|
|
290
|
+
const all = getAllProcesses();
|
|
291
|
+
if (all.length === 0) {
|
|
292
|
+
error('No processes registered.');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log(chalk.bold(`\n Restarting ${all.length} processes...\n`));
|
|
296
|
+
for (const proc of all) {
|
|
297
|
+
try {
|
|
298
|
+
console.log(chalk.yellow(` ↻ Restarting ${proc.name}...`));
|
|
299
|
+
await handleRun({
|
|
300
|
+
action: 'run',
|
|
301
|
+
name: proc.name,
|
|
302
|
+
force: true,
|
|
303
|
+
remoteName: '',
|
|
304
|
+
});
|
|
305
|
+
} catch (err: any) {
|
|
306
|
+
console.error(chalk.red(` ✗ Failed to restart ${proc.name}: ${err.message}`));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
console.log(chalk.green(`\n ✓ All processes restarted.\n`));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Stop all running processes
|
|
314
|
+
if (values['stop-all']) {
|
|
315
|
+
const { getAllProcesses } = await import('./db');
|
|
316
|
+
const all = getAllProcesses();
|
|
317
|
+
if (all.length === 0) {
|
|
318
|
+
error('No processes registered.');
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
console.log(chalk.bold(`\n Stopping ${all.length} processes...\n`));
|
|
322
|
+
for (const proc of all) {
|
|
323
|
+
try {
|
|
324
|
+
if (await isProcessRunning(proc.pid)) {
|
|
325
|
+
console.log(chalk.yellow(` ■ Stopping ${proc.name} (PID ${proc.pid})...`));
|
|
326
|
+
await handleStop(proc.name);
|
|
327
|
+
} else {
|
|
328
|
+
console.log(chalk.gray(` ○ ${proc.name} already stopped`));
|
|
329
|
+
}
|
|
330
|
+
} catch (err: any) {
|
|
331
|
+
console.error(chalk.red(` ✗ Failed to stop ${proc.name}: ${err.message}`));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
console.log(chalk.green(`\n ✓ All processes stopped.\n`));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
266
338
|
const name = (values.name as string) || positionals[0];
|
|
267
339
|
|
|
268
340
|
// Delete
|