bgrun 3.12.12 → 3.12.14
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 +2 -2
- package/dashboard/lib/runtime.ts +49 -0
- package/package.json +2 -17
- package/src/api.ts +0 -116
- package/src/build.ts +0 -31
- package/src/commands/cleanup.ts +0 -141
- package/src/commands/details.ts +0 -60
- package/src/commands/list.ts +0 -133
- package/src/commands/logs.ts +0 -49
- package/src/commands/run.ts +0 -217
- package/src/commands/watch.ts +0 -223
- package/src/config.ts +0 -37
- package/src/db.ts +0 -422
- package/src/deploy.ts +0 -163
- package/src/deps.ts +0 -126
- package/src/guard.ts +0 -208
- package/src/index.ts +0 -623
- package/src/log-rotation.ts +0 -93
- package/src/logger.ts +0 -40
- package/src/platform.ts +0 -665
- package/src/server.ts +0 -217
- package/src/table.ts +0 -232
- package/src/types.ts +0 -14
- package/src/utils.ts +0 -96
package/README.md
CHANGED
|
@@ -409,8 +409,8 @@ If no `--config` is specified, bgrun looks for `.config.toml` in the working dir
|
|
|
409
409
|
|
|
410
410
|
bgrun exposes its internals as importable TypeScript functions:
|
|
411
411
|
|
|
412
|
-
> **Packaging note:** the CLI ships from `dist/index.js`,
|
|
413
|
-
>
|
|
412
|
+
> **Packaging note:** the CLI ships from `dist/index.js`, the Bun programmatic API resolves through `dist/api.js`, and the dashboard backend uses built `dist/*` runtime artifacts via `dashboard/lib/runtime.ts`.
|
|
413
|
+
> Published packages are now `dist`-first; repository `src/` files remain the development/build source of truth and are not part of the runtime package surface.
|
|
414
414
|
|
|
415
415
|
```bash
|
|
416
416
|
bun add bgrun
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export {
|
|
2
|
+
db,
|
|
3
|
+
getAllProcesses,
|
|
4
|
+
getProcess,
|
|
5
|
+
insertProcess,
|
|
6
|
+
removeProcess,
|
|
7
|
+
removeProcessByName,
|
|
8
|
+
removeAllProcesses,
|
|
9
|
+
updateProcessPid,
|
|
10
|
+
updateProcessEnv,
|
|
11
|
+
getAllTemplates,
|
|
12
|
+
saveTemplate,
|
|
13
|
+
deleteTemplate,
|
|
14
|
+
getProcessHistory,
|
|
15
|
+
getRecentHistory,
|
|
16
|
+
addHistoryEntry,
|
|
17
|
+
getDependencyGraph,
|
|
18
|
+
addDependency,
|
|
19
|
+
removeDependency,
|
|
20
|
+
getStartOrder,
|
|
21
|
+
getDbInfo,
|
|
22
|
+
dbPath,
|
|
23
|
+
bgrHome,
|
|
24
|
+
isProcessRunning,
|
|
25
|
+
terminateProcess,
|
|
26
|
+
readFileTail,
|
|
27
|
+
getProcessPorts,
|
|
28
|
+
findChildPid,
|
|
29
|
+
findPidByPort,
|
|
30
|
+
getShellCommand,
|
|
31
|
+
killProcessOnPort,
|
|
32
|
+
waitForPortFree,
|
|
33
|
+
ensureDir,
|
|
34
|
+
getHomeDir,
|
|
35
|
+
isWindows,
|
|
36
|
+
getProcessBatchResources,
|
|
37
|
+
getProcessMemory,
|
|
38
|
+
reconcileProcessPids,
|
|
39
|
+
handleRun,
|
|
40
|
+
getVersion,
|
|
41
|
+
calculateRuntime,
|
|
42
|
+
parseEnvString,
|
|
43
|
+
validateDirectory,
|
|
44
|
+
} from '../../dist/api.js'
|
|
45
|
+
|
|
46
|
+
export { deployProcess, deployAllProcesses } from '../../dist/deploy.js'
|
|
47
|
+
export { buildDepGraph } from '../../dist/deps.js'
|
|
48
|
+
export { rotateAllLogs } from '../../dist/log-rotation.js'
|
|
49
|
+
export { guardEvents, guardRestartCounts } from '../../dist/server.js'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bgrun",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.14",
|
|
4
4
|
"description": "bgrun — A lightweight process manager for Bun",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api.js",
|
|
@@ -17,23 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
|
-
"src/api.ts",
|
|
21
|
-
"src/build.ts",
|
|
22
|
-
"src/config.ts",
|
|
23
|
-
"src/db.ts",
|
|
24
|
-
"src/deploy.ts",
|
|
25
|
-
"src/deps.ts",
|
|
26
|
-
"src/guard.ts",
|
|
27
|
-
"src/index.ts",
|
|
28
|
-
"src/log-rotation.ts",
|
|
29
|
-
"src/logger.ts",
|
|
30
|
-
"src/platform.ts",
|
|
31
|
-
"src/server.ts",
|
|
32
|
-
"src/table.ts",
|
|
33
|
-
"src/types.ts",
|
|
34
|
-
"src/utils.ts",
|
|
35
|
-
"src/commands",
|
|
36
20
|
"dashboard/app",
|
|
21
|
+
"dashboard/lib",
|
|
37
22
|
"scripts",
|
|
38
23
|
"README.md",
|
|
39
24
|
"image.png",
|
package/src/api.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BGR Public API (package: bgrun)
|
|
3
|
-
*
|
|
4
|
-
* Import from 'bgrun' to use these functions in your own process-managing apps.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { getAllProcesses, isProcessRunning, handleRun } from 'bgrun'
|
|
9
|
-
*
|
|
10
|
-
* // List all managed processes
|
|
11
|
-
* const processes = getAllProcesses()
|
|
12
|
-
*
|
|
13
|
-
* // Check if a process is running
|
|
14
|
-
* const alive = await isProcessRunning(process.pid)
|
|
15
|
-
*
|
|
16
|
-
* // Start a new managed process
|
|
17
|
-
* await handleRun({ name: 'my-app', command: 'bun run dev', directory: './my-app', action: 'run', remoteName: '' })
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
// --- Types ---
|
|
22
|
-
export type { Process } from './db'
|
|
23
|
-
export type { CommandOptions } from './types'
|
|
24
|
-
|
|
25
|
-
// --- Database Operations ---
|
|
26
|
-
export {
|
|
27
|
-
db,
|
|
28
|
-
getAllProcesses,
|
|
29
|
-
getProcess,
|
|
30
|
-
insertProcess,
|
|
31
|
-
removeProcess,
|
|
32
|
-
removeProcessByName,
|
|
33
|
-
removeAllProcesses,
|
|
34
|
-
updateProcessPid,
|
|
35
|
-
updateProcessEnv,
|
|
36
|
-
getAllTemplates,
|
|
37
|
-
saveTemplate,
|
|
38
|
-
deleteTemplate,
|
|
39
|
-
getProcessHistory,
|
|
40
|
-
getRecentHistory,
|
|
41
|
-
addHistoryEntry,
|
|
42
|
-
getDependencyGraph,
|
|
43
|
-
addDependency,
|
|
44
|
-
removeDependency,
|
|
45
|
-
getStartOrder,
|
|
46
|
-
retryDatabaseOperation,
|
|
47
|
-
getDbInfo,
|
|
48
|
-
dbPath,
|
|
49
|
-
bgrHome,
|
|
50
|
-
} from './db'
|
|
51
|
-
|
|
52
|
-
// --- Process Operations ---
|
|
53
|
-
export {
|
|
54
|
-
isProcessRunning,
|
|
55
|
-
terminateProcess,
|
|
56
|
-
readFileTail,
|
|
57
|
-
getProcessPorts,
|
|
58
|
-
findChildPid,
|
|
59
|
-
findPidByPort,
|
|
60
|
-
getShellCommand,
|
|
61
|
-
killProcessOnPort,
|
|
62
|
-
waitForPortFree,
|
|
63
|
-
ensureDir,
|
|
64
|
-
getHomeDir,
|
|
65
|
-
isWindows,
|
|
66
|
-
getProcessBatchResources,
|
|
67
|
-
getProcessMemory,
|
|
68
|
-
reconcileProcessPids,
|
|
69
|
-
} from './platform'
|
|
70
|
-
|
|
71
|
-
// --- High-Level Commands ---
|
|
72
|
-
export { handleRun } from './commands/run'
|
|
73
|
-
|
|
74
|
-
// --- Utilities ---
|
|
75
|
-
export { getVersion, calculateRuntime, parseEnvString, validateDirectory } from './utils'
|
|
76
|
-
|
|
77
|
-
// --- Default Export (namespace style) ---
|
|
78
|
-
import {
|
|
79
|
-
db,
|
|
80
|
-
getAllProcesses,
|
|
81
|
-
getProcess,
|
|
82
|
-
insertProcess,
|
|
83
|
-
removeProcess,
|
|
84
|
-
removeProcessByName,
|
|
85
|
-
removeAllProcesses,
|
|
86
|
-
updateProcessPid,
|
|
87
|
-
updateProcessEnv,
|
|
88
|
-
getAllTemplates,
|
|
89
|
-
saveTemplate,
|
|
90
|
-
deleteTemplate,
|
|
91
|
-
getProcessHistory,
|
|
92
|
-
getRecentHistory,
|
|
93
|
-
addHistoryEntry,
|
|
94
|
-
getDependencyGraph,
|
|
95
|
-
addDependency,
|
|
96
|
-
removeDependency,
|
|
97
|
-
getStartOrder,
|
|
98
|
-
retryDatabaseOperation,
|
|
99
|
-
getDbInfo,
|
|
100
|
-
dbPath,
|
|
101
|
-
bgrHome,
|
|
102
|
-
} from './db'
|
|
103
|
-
import { isProcessRunning, terminateProcess, readFileTail, getProcessPorts, findChildPid, findPidByPort, getShellCommand, killProcessOnPort, waitForPortFree, ensureDir, getHomeDir, isWindows, getProcessBatchResources, getProcessMemory, reconcileProcessPids } from './platform'
|
|
104
|
-
import { handleRun } from './commands/run'
|
|
105
|
-
import { getVersion, calculateRuntime, parseEnvString, validateDirectory } from './utils'
|
|
106
|
-
|
|
107
|
-
export default {
|
|
108
|
-
db, getAllProcesses, getProcess, insertProcess, removeProcess, removeProcessByName, removeAllProcesses,
|
|
109
|
-
updateProcessPid, updateProcessEnv, getAllTemplates, saveTemplate, deleteTemplate,
|
|
110
|
-
getProcessHistory, getRecentHistory, addHistoryEntry,
|
|
111
|
-
getDependencyGraph, addDependency, removeDependency, getStartOrder,
|
|
112
|
-
retryDatabaseOperation, getDbInfo, dbPath, bgrHome,
|
|
113
|
-
isProcessRunning, terminateProcess, readFileTail, getProcessPorts, findChildPid, findPidByPort, getShellCommand, killProcessOnPort, waitForPortFree, ensureDir, getHomeDir, isWindows, getProcessBatchResources, getProcessMemory, reconcileProcessPids,
|
|
114
|
-
handleRun,
|
|
115
|
-
getVersion, calculateRuntime, parseEnvString, validateDirectory,
|
|
116
|
-
}
|
package/src/build.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
console.log("Starting build process for bgrun...");
|
|
2
|
-
|
|
3
|
-
const entrypoints = [
|
|
4
|
-
'./src/index.ts',
|
|
5
|
-
'./src/api.ts',
|
|
6
|
-
'./src/server.ts',
|
|
7
|
-
'./src/deploy.ts',
|
|
8
|
-
'./src/deps.ts',
|
|
9
|
-
'./src/log-rotation.ts',
|
|
10
|
-
];
|
|
11
|
-
const result = await Bun.build({
|
|
12
|
-
entrypoints,
|
|
13
|
-
outdir: './dist',
|
|
14
|
-
target: 'bun',
|
|
15
|
-
format: 'esm',
|
|
16
|
-
minify: false,
|
|
17
|
-
// Mark all packages as external to rely on node_modules
|
|
18
|
-
// This avoids bundling native modules or mismatched React versions
|
|
19
|
-
packages: "external",
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (!result.success) {
|
|
23
|
-
console.error("Build failed");
|
|
24
|
-
for (const message of result.logs) {
|
|
25
|
-
console.error(message);
|
|
26
|
-
}
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const builtFiles = result.outputs.map((output) => output.path.split(/[\\/]/).pop()).filter(Boolean);
|
|
31
|
-
console.log(`Build successful! Artifacts: ${builtFiles.join(', ')}`);
|
package/src/commands/cleanup.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { getProcess, removeProcessByName, removeProcess, getAllProcesses, removeAllProcesses, updateProcessPid } from "../db";
|
|
3
|
-
import { isProcessRunning, terminateProcess, getProcessPorts, killProcessOnPort, waitForPortFree } from "../platform";
|
|
4
|
-
import { announce, error } from "../logger";
|
|
5
|
-
import * as fs from "fs";
|
|
6
|
-
|
|
7
|
-
export async function handleDelete(name: string) {
|
|
8
|
-
const process = getProcess(name);
|
|
9
|
-
|
|
10
|
-
if (!process) {
|
|
11
|
-
error(`No process found named '${name}'`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const isRunning = await isProcessRunning(process.pid);
|
|
16
|
-
if (isRunning) {
|
|
17
|
-
await terminateProcess(process.pid);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (fs.existsSync(process.stdout_path)) {
|
|
21
|
-
try { fs.unlinkSync(process.stdout_path); } catch { }
|
|
22
|
-
}
|
|
23
|
-
if (fs.existsSync(process.stderr_path)) {
|
|
24
|
-
try { fs.unlinkSync(process.stderr_path); } catch { }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
removeProcessByName(name);
|
|
28
|
-
announce(`Process '${name}' has been ${isRunning ? 'stopped and ' : ''}deleted`, "Process Deleted");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function handleClean() {
|
|
32
|
-
const processes = getAllProcesses();
|
|
33
|
-
let cleanedCount = 0;
|
|
34
|
-
let deletedLogs = 0;
|
|
35
|
-
|
|
36
|
-
for (const proc of processes) {
|
|
37
|
-
const running = await isProcessRunning(proc.pid);
|
|
38
|
-
if (!running) {
|
|
39
|
-
removeProcess(proc.pid);
|
|
40
|
-
cleanedCount++;
|
|
41
|
-
|
|
42
|
-
if (fs.existsSync(proc.stdout_path)) {
|
|
43
|
-
try { fs.unlinkSync(proc.stdout_path); deletedLogs++; } catch { }
|
|
44
|
-
}
|
|
45
|
-
if (fs.existsSync(proc.stderr_path)) {
|
|
46
|
-
try { fs.unlinkSync(proc.stderr_path); deletedLogs++; } catch { }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (cleanedCount === 0) {
|
|
52
|
-
announce("No stopped processes found to clean.", "Clean Complete");
|
|
53
|
-
} else {
|
|
54
|
-
announce(
|
|
55
|
-
`Cleaned ${cleanedCount} stopped ${cleanedCount === 1 ? 'process' : 'processes'} and removed ${deletedLogs} log ${deletedLogs === 1 ? 'file' : 'files'}.`,
|
|
56
|
-
"Clean Complete"
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function handleStop(name: string) {
|
|
62
|
-
const proc = getProcess(name);
|
|
63
|
-
|
|
64
|
-
if (!proc) {
|
|
65
|
-
error(`No process found named '${name}'`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const isRunning = await isProcessRunning(proc.pid);
|
|
70
|
-
if (!isRunning) {
|
|
71
|
-
announce(`Process '${name}' is already stopped.`, "Process Stop");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Detect ports the process is using BEFORE killing it
|
|
76
|
-
const ports = await getProcessPorts(proc.pid);
|
|
77
|
-
|
|
78
|
-
await terminateProcess(proc.pid);
|
|
79
|
-
|
|
80
|
-
// Also kill by detected ports as safety net
|
|
81
|
-
for (const port of ports) {
|
|
82
|
-
await killProcessOnPort(port);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Mark PID as 0 — prevents reconcileProcessPids from re-attaching
|
|
86
|
-
// a random matching process as this one
|
|
87
|
-
updateProcessPid(name, 0);
|
|
88
|
-
|
|
89
|
-
announce(`Process '${name}' has been stopped (kept in registry).`, "Process Stopped");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function handleDeleteAll() {
|
|
93
|
-
const processes = getAllProcesses();
|
|
94
|
-
if (processes.length === 0) {
|
|
95
|
-
announce("There are no processes to delete.", "Delete All");
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let killedCount = 0;
|
|
100
|
-
let portsFreed = 0;
|
|
101
|
-
|
|
102
|
-
for (const proc of processes) {
|
|
103
|
-
const running = await isProcessRunning(proc.pid);
|
|
104
|
-
|
|
105
|
-
if (running) {
|
|
106
|
-
// Detect ports BEFORE killing so we can clean them up
|
|
107
|
-
const ports = await getProcessPorts(proc.pid);
|
|
108
|
-
|
|
109
|
-
// Force-kill the process tree
|
|
110
|
-
await terminateProcess(proc.pid, true);
|
|
111
|
-
killedCount++;
|
|
112
|
-
|
|
113
|
-
// Kill anything still holding the ports
|
|
114
|
-
for (const port of ports) {
|
|
115
|
-
await killProcessOnPort(port);
|
|
116
|
-
const freed = await waitForPortFree(port, 3000);
|
|
117
|
-
if (!freed) {
|
|
118
|
-
await killProcessOnPort(port);
|
|
119
|
-
await waitForPortFree(port, 2000);
|
|
120
|
-
}
|
|
121
|
-
portsFreed++;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Clean up log files
|
|
126
|
-
if (fs.existsSync(proc.stdout_path)) {
|
|
127
|
-
try { fs.unlinkSync(proc.stdout_path); } catch { }
|
|
128
|
-
}
|
|
129
|
-
if (fs.existsSync(proc.stderr_path)) {
|
|
130
|
-
try { fs.unlinkSync(proc.stderr_path); } catch { }
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
removeAllProcesses();
|
|
135
|
-
|
|
136
|
-
const parts = [`${processes.length} ${processes.length === 1 ? 'process' : 'processes'} deleted`];
|
|
137
|
-
if (killedCount > 0) parts.push(`${killedCount} force-killed`);
|
|
138
|
-
if (portsFreed > 0) parts.push(`${portsFreed} ${portsFreed === 1 ? 'port' : 'ports'} freed`);
|
|
139
|
-
|
|
140
|
-
announce(parts.join(', ') + '.', "Nuke Complete");
|
|
141
|
-
}
|
package/src/commands/details.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { error, announce } from "../logger";
|
|
3
|
-
import { getProcess, updateProcessPid } from "../db";
|
|
4
|
-
import { isProcessRunning, calculateRuntime, parseEnvString } from "../utils";
|
|
5
|
-
import { getProcessPorts, reconcileProcessPids } from "../platform";
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
|
|
8
|
-
export async function showDetails(name: string) {
|
|
9
|
-
const proc = getProcess(name);
|
|
10
|
-
if (!proc) {
|
|
11
|
-
error(`No process found named '${name}'`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let isRunning = await isProcessRunning(proc.pid, proc.command);
|
|
16
|
-
|
|
17
|
-
// Reconcile stale PID: cmd.exe wrapper may have exited while bun.exe child lives
|
|
18
|
-
if (!isRunning && proc.pid > 0) {
|
|
19
|
-
const reconciled = await reconcileProcessPids(
|
|
20
|
-
[{ name: proc.name, pid: proc.pid, command: proc.command, workdir: proc.workdir }],
|
|
21
|
-
new Set([proc.pid]),
|
|
22
|
-
);
|
|
23
|
-
const newPid = reconciled.get(proc.name);
|
|
24
|
-
if (newPid) {
|
|
25
|
-
updateProcessPid(proc.name, newPid);
|
|
26
|
-
(proc as any).pid = newPid;
|
|
27
|
-
isRunning = true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
const runtime = calculateRuntime(proc.timestamp);
|
|
31
|
-
const envVars = parseEnvString(proc.env);
|
|
32
|
-
|
|
33
|
-
// Detect actual ports via OS
|
|
34
|
-
const ports = isRunning ? await getProcessPorts(proc.pid) : [];
|
|
35
|
-
|
|
36
|
-
const portDisplay = ports.length > 0
|
|
37
|
-
? ports.map(p => chalk.hex('#FF6B6B')(`:${p}`)).join(', ')
|
|
38
|
-
: null;
|
|
39
|
-
|
|
40
|
-
const details = `
|
|
41
|
-
${chalk.bold('Process Details:')}
|
|
42
|
-
${chalk.gray('═'.repeat(50))}
|
|
43
|
-
${chalk.cyan.bold('Name:')} ${proc.name}
|
|
44
|
-
${chalk.yellow.bold('PID:')} ${proc.pid}${portDisplay ? `\n${chalk.hex('#FF6B6B').bold('Port:')} ${portDisplay}` : ''}
|
|
45
|
-
${chalk.bold('Status:')} ${isRunning ? chalk.green.bold("● Running") : chalk.red.bold("○ Stopped")}
|
|
46
|
-
${chalk.magenta.bold('Runtime:')} ${runtime}
|
|
47
|
-
${chalk.blue.bold('Working Directory:')} ${proc.workdir}
|
|
48
|
-
${chalk.white.bold('Command:')} ${proc.command}
|
|
49
|
-
${chalk.gray.bold('Config Path:')} ${proc.configPath}
|
|
50
|
-
${chalk.green.bold('Stdout Path:')} ${proc.stdout_path}
|
|
51
|
-
${chalk.red.bold('Stderr Path:')} ${proc.stderr_path}
|
|
52
|
-
|
|
53
|
-
${chalk.bold('🔧 Environment Variables:')}
|
|
54
|
-
${chalk.gray('═'.repeat(50))}
|
|
55
|
-
${Object.entries(envVars)
|
|
56
|
-
.map(([key, value]) => `${chalk.cyan.bold(key)} = ${chalk.yellow(value)}`)
|
|
57
|
-
.join('\n')}
|
|
58
|
-
`;
|
|
59
|
-
announce(details, `Process Details: ${name}`);
|
|
60
|
-
}
|
package/src/commands/list.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { renderProcessTable } from "../table";
|
|
3
|
-
import type { ProcessTableRow } from "../table";
|
|
4
|
-
import { getAllProcesses, updateProcessPid } from "../db";
|
|
5
|
-
import { announce } from "../logger";
|
|
6
|
-
import { isProcessRunning, calculateRuntime, parseEnvString } from "../utils";
|
|
7
|
-
import { getProcessPorts, getProcessBatchResources, reconcileProcessPids } from "../platform";
|
|
8
|
-
import { measure } from "measure-fn";
|
|
9
|
-
|
|
10
|
-
function formatMemory(bytes: number): string {
|
|
11
|
-
if (bytes === 0) return '-';
|
|
12
|
-
const mb = bytes / (1024 * 1024);
|
|
13
|
-
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`;
|
|
14
|
-
return `${Math.round(mb)} MB`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function showAll(opts?: { json?: boolean; filter?: string }) {
|
|
18
|
-
const processes = getAllProcesses();
|
|
19
|
-
|
|
20
|
-
// Apply filter by env.BGR_GROUP if provided
|
|
21
|
-
const filtered = processes.filter((proc) => {
|
|
22
|
-
if (!opts?.filter) return true;
|
|
23
|
-
const envVars = parseEnvString(proc.env);
|
|
24
|
-
return envVars["BGR_GROUP"] === opts.filter;
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// ─── PID Reconciliation ──────────────────────────────────────────
|
|
28
|
-
// On Windows, the stored PID may be a dead cmd.exe wrapper while the
|
|
29
|
-
// actual bun.exe child is still running. Detect dead PIDs up-front,
|
|
30
|
-
// reconcile them in one batch PowerShell call, and patch the DB so
|
|
31
|
-
// subsequent invocations are stable (no flicker).
|
|
32
|
-
const deadPids = new Set<number>();
|
|
33
|
-
const aliveCache = new Map<number, boolean>();
|
|
34
|
-
|
|
35
|
-
for (const proc of filtered) {
|
|
36
|
-
const alive = await isProcessRunning(proc.pid, proc.command);
|
|
37
|
-
aliveCache.set(proc.pid, alive);
|
|
38
|
-
if (!alive && proc.pid > 0) deadPids.add(proc.pid);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (deadPids.size > 0) {
|
|
42
|
-
const reconciled = await reconcileProcessPids(
|
|
43
|
-
filtered.map(p => ({ name: p.name, pid: p.pid, command: p.command, workdir: p.workdir })),
|
|
44
|
-
deadPids,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
for (const [name, newPid] of reconciled) {
|
|
48
|
-
updateProcessPid(name, newPid);
|
|
49
|
-
// Patch the in-memory record so the rest of this function sees
|
|
50
|
-
// the corrected PID without re-querying the DB.
|
|
51
|
-
const proc = filtered.find(p => p.name === name);
|
|
52
|
-
if (proc) {
|
|
53
|
-
(proc as any).pid = newPid;
|
|
54
|
-
aliveCache.set(newPid, true);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// ─────────────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
if (opts?.json) {
|
|
61
|
-
// JSON output with filtered env variables
|
|
62
|
-
const jsonData: any[] = [];
|
|
63
|
-
|
|
64
|
-
for (const proc of filtered) {
|
|
65
|
-
const isRunning = aliveCache.get(proc.pid) ?? await isProcessRunning(proc.pid, proc.command);
|
|
66
|
-
const envVars = parseEnvString(proc.env);
|
|
67
|
-
|
|
68
|
-
const ports = isRunning ? await getProcessPorts(proc.pid) : [];
|
|
69
|
-
jsonData.push({
|
|
70
|
-
pid: proc.pid,
|
|
71
|
-
name: proc.name,
|
|
72
|
-
ports: ports.length > 0 ? ports : undefined,
|
|
73
|
-
status: isRunning ? "running" : "stopped",
|
|
74
|
-
env: envVars,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
console.log(JSON.stringify(jsonData, null, 2));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Table output
|
|
83
|
-
const tableData: ProcessTableRow[] = [];
|
|
84
|
-
|
|
85
|
-
// Batch fetch memory for all PIDs
|
|
86
|
-
const allPids = filtered.map(p => p.pid);
|
|
87
|
-
const resourceMap = await getProcessBatchResources(allPids);
|
|
88
|
-
|
|
89
|
-
for (const proc of filtered) {
|
|
90
|
-
const isRunning = aliveCache.get(proc.pid) ?? await isProcessRunning(proc.pid, proc.command);
|
|
91
|
-
const runtime = calculateRuntime(proc.timestamp);
|
|
92
|
-
const mem = isRunning ? (resourceMap.get(proc.pid)?.memory || 0) : 0;
|
|
93
|
-
|
|
94
|
-
const ports = isRunning ? await getProcessPorts(proc.pid) : [];
|
|
95
|
-
tableData.push({
|
|
96
|
-
id: proc.id,
|
|
97
|
-
pid: proc.pid,
|
|
98
|
-
name: proc.name,
|
|
99
|
-
port: ports.length > 0 ? ports.map(p => `:${p}`).join(',') : '-',
|
|
100
|
-
memory: formatMemory(mem),
|
|
101
|
-
command: proc.command,
|
|
102
|
-
workdir: proc.workdir,
|
|
103
|
-
status: isRunning
|
|
104
|
-
? chalk.green.bold("● Running")
|
|
105
|
-
: chalk.red.bold("○ Stopped"),
|
|
106
|
-
runtime: runtime,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (tableData.length === 0) {
|
|
111
|
-
if (opts?.filter) {
|
|
112
|
-
announce(`No processes matched filter BGR_GROUP='${opts.filter}'.`, "No Matches");
|
|
113
|
-
} else {
|
|
114
|
-
announce("No processes found.", "Empty");
|
|
115
|
-
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const tableOutput = renderProcessTable(tableData, {
|
|
120
|
-
padding: 1,
|
|
121
|
-
borderStyle: "rounded",
|
|
122
|
-
showHeaders: true,
|
|
123
|
-
});
|
|
124
|
-
console.log(tableOutput);
|
|
125
|
-
|
|
126
|
-
const runningCount = tableData.filter((p) => p.status.includes("Running")).length;
|
|
127
|
-
const stoppedCount = tableData.filter((p) => p.status.includes("Stopped")).length;
|
|
128
|
-
console.log(
|
|
129
|
-
chalk.cyan(
|
|
130
|
-
`Total: ${tableData.length} processes (${chalk.green(`${runningCount} running`)}, ${chalk.red(`${stoppedCount} stopped`)})`
|
|
131
|
-
)
|
|
132
|
-
);
|
|
133
|
-
}
|
package/src/commands/logs.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { getProcess } from "../db";
|
|
2
|
-
import { error } from "../logger";
|
|
3
|
-
import { readFileTail } from "../platform";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
import * as fs from "fs";
|
|
6
|
-
|
|
7
|
-
export async function showLogs(name: string, logType: 'stdout' | 'stderr' | 'both' = 'both', lines?: number) {
|
|
8
|
-
const proc = getProcess(name);
|
|
9
|
-
if (!proc) {
|
|
10
|
-
error(`No process found named '${name}'`);
|
|
11
|
-
return; // TS shut up
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (logType === 'both' || logType === 'stdout') {
|
|
15
|
-
console.log(chalk.green.bold(`📄 Stdout logs for ${name}:`));
|
|
16
|
-
console.log(chalk.gray('═'.repeat(50)));
|
|
17
|
-
|
|
18
|
-
if (fs.existsSync(proc.stdout_path)) {
|
|
19
|
-
try {
|
|
20
|
-
const output = await readFileTail(proc.stdout_path, lines);
|
|
21
|
-
console.log(output || chalk.gray('(no output)'));
|
|
22
|
-
} catch (err) {
|
|
23
|
-
console.log(chalk.red(`Error reading stdout: ${err}`));
|
|
24
|
-
}
|
|
25
|
-
} else {
|
|
26
|
-
console.log(chalk.gray('(log file not found)'));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (logType === 'both') {
|
|
30
|
-
console.log('\n');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (logType === 'both' || logType === 'stderr') {
|
|
35
|
-
console.log(chalk.red.bold(`📄 Stderr logs for ${name}:`));
|
|
36
|
-
console.log(chalk.gray('═'.repeat(50)));
|
|
37
|
-
|
|
38
|
-
if (fs.existsSync(proc.stderr_path)) {
|
|
39
|
-
try {
|
|
40
|
-
const output = await readFileTail(proc.stderr_path, lines);
|
|
41
|
-
console.log(output || chalk.gray('(no errors)'));
|
|
42
|
-
} catch (err) {
|
|
43
|
-
console.log(chalk.red(`Error reading stderr: ${err}`));
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
console.log(chalk.gray('(log file not found)'));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|