bgrun 3.12.16 → 3.12.22
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 +54 -15
- package/dashboard/app/api/guard/route.ts +5 -13
- package/dashboard/app/api/guard-all/route.ts +6 -14
- package/dashboard/app/api/guard-events/route.ts +3 -3
- package/dashboard/app/api/next-port/route.ts +27 -7
- package/dashboard/app/api/processes/route.ts +31 -5
- package/dashboard/app/page.client.tsx +2 -19
- package/dashboard/app/page.tsx +1 -5
- package/dashboard/lib/runtime.ts +58 -49
- package/dist/api.js +912 -234
- package/dist/deploy.js +691 -231
- package/dist/deps.js +151 -70
- package/dist/index.js +1276 -717
- package/dist/server.js +151 -639
- package/package.json +3 -2
package/dist/deps.js
CHANGED
|
@@ -17,48 +17,59 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
17
17
|
var __require = import.meta.require;
|
|
18
18
|
|
|
19
19
|
// src/platform.ts
|
|
20
|
-
var exports_platform = {};
|
|
21
|
-
__export(exports_platform, {
|
|
22
|
-
waitForPortFree: () => waitForPortFree,
|
|
23
|
-
terminateProcess: () => terminateProcess,
|
|
24
|
-
reconcileProcessPids: () => reconcileProcessPids,
|
|
25
|
-
readFileTail: () => readFileTail,
|
|
26
|
-
psExec: () => psExec,
|
|
27
|
-
parseUnixListeningPorts: () => parseUnixListeningPorts,
|
|
28
|
-
killProcessOnPort: () => killProcessOnPort,
|
|
29
|
-
isWindows: () => isWindows,
|
|
30
|
-
isProcessRunning: () => isProcessRunning,
|
|
31
|
-
isPortFree: () => isPortFree,
|
|
32
|
-
getShellCommand: () => getShellCommand,
|
|
33
|
-
getProcessPorts: () => getProcessPorts,
|
|
34
|
-
getProcessMemory: () => getProcessMemory,
|
|
35
|
-
getProcessBatchResources: () => getProcessBatchResources,
|
|
36
|
-
getPortInfo: () => getPortInfo,
|
|
37
|
-
getHomeDir: () => getHomeDir,
|
|
38
|
-
findPidByPort: () => findPidByPort,
|
|
39
|
-
findChildPid: () => findChildPid,
|
|
40
|
-
ensureDir: () => ensureDir,
|
|
41
|
-
copyFile: () => copyFile
|
|
42
|
-
});
|
|
43
20
|
import * as fs from "fs";
|
|
44
21
|
import * as os from "os";
|
|
45
22
|
import { join } from "path";
|
|
46
23
|
var {$ } = globalThis.Bun;
|
|
47
24
|
import { createMeasure } from "measure-fn";
|
|
48
|
-
function psExec(command,
|
|
49
|
-
const tmpFile = join(os.tmpdir(), `bgr-ps-${Date.now()}.ps1`);
|
|
25
|
+
async function psExec(command, timeoutMs = 3000) {
|
|
26
|
+
const tmpFile = join(os.tmpdir(), `bgr-ps-${Date.now()}-${Math.random().toString(36).substr(2, 9)}.ps1`);
|
|
50
27
|
try {
|
|
51
|
-
|
|
52
|
-
const
|
|
28
|
+
await Bun.write(tmpFile, command);
|
|
29
|
+
const proc = Bun.spawn([
|
|
30
|
+
"powershell",
|
|
31
|
+
"-NoProfile",
|
|
32
|
+
"-ExecutionPolicy",
|
|
33
|
+
"Bypass",
|
|
34
|
+
"-File",
|
|
35
|
+
tmpFile
|
|
36
|
+
], {
|
|
37
|
+
stdout: "pipe",
|
|
38
|
+
stderr: "pipe"
|
|
39
|
+
});
|
|
40
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
41
|
+
setTimeout(() => reject(new Error("PowerShell command timed out")), timeoutMs);
|
|
42
|
+
});
|
|
43
|
+
const resultPromise = new Promise(async (resolve, reject) => {
|
|
44
|
+
try {
|
|
45
|
+
const stdoutPromise = proc.stdout ? new Response(proc.stdout).text() : Promise.resolve("");
|
|
46
|
+
const stderrPromise = proc.stderr ? new Response(proc.stderr).text() : Promise.resolve("");
|
|
47
|
+
const exitCode = await proc.exited;
|
|
48
|
+
const stdout = await stdoutPromise;
|
|
49
|
+
const stderr = await stderrPromise;
|
|
50
|
+
if (exitCode === 0) {
|
|
51
|
+
resolve(stdout);
|
|
52
|
+
} else {
|
|
53
|
+
resolve(stderr || "");
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
reject(error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
53
59
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
const result = await Promise.race([resultPromise, timeoutPromise]);
|
|
61
|
+
return result.trim();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return "";
|
|
64
|
+
} finally {
|
|
65
|
+
try {
|
|
66
|
+
await Bun.sleep(100);
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
58
70
|
try {
|
|
59
|
-
fs.
|
|
71
|
+
fs.rmSync(tmpFile, { force: true });
|
|
60
72
|
} catch {}
|
|
61
|
-
return "";
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
function isWindows() {
|
|
@@ -80,7 +91,7 @@ async function isProcessRunning(pid, command) {
|
|
|
80
91
|
process.kill(pid, 0);
|
|
81
92
|
return true;
|
|
82
93
|
} catch {
|
|
83
|
-
const output = psExec(`Get-Process -Id ${pid} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id`)
|
|
94
|
+
const output = await psExec(`Get-Process -Id ${pid} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id`);
|
|
84
95
|
return output === String(pid);
|
|
85
96
|
}
|
|
86
97
|
} else {
|
|
@@ -173,35 +184,6 @@ async function isPortFree(port) {
|
|
|
173
184
|
return true;
|
|
174
185
|
}
|
|
175
186
|
}
|
|
176
|
-
async function getPortInfo(port) {
|
|
177
|
-
try {
|
|
178
|
-
if (isWindows()) {
|
|
179
|
-
const result = await $`netstat -ano | findstr :${port}`.nothrow().quiet().text();
|
|
180
|
-
for (const line of result.split(`
|
|
181
|
-
`)) {
|
|
182
|
-
const match = line.match(new RegExp(`:(${port})\\s+.*LISTENING\\s+(\\d+)`));
|
|
183
|
-
if (match) {
|
|
184
|
-
const pid = parseInt(match[2]);
|
|
185
|
-
if (pid > 0 && await isProcessRunning(pid)) {
|
|
186
|
-
const nameResult = await $`powershell -NoProfile -Command "(Get-Process -Id ${pid} -ErrorAction SilentlyContinue).ProcessName"`.nothrow().quiet().text();
|
|
187
|
-
return { inUse: true, pid, processName: nameResult.trim() || "unknown" };
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return { inUse: false };
|
|
192
|
-
} else {
|
|
193
|
-
const result = await $`ss -tln sport = :${port}`.nothrow().quiet().text();
|
|
194
|
-
const lines = result.trim().split(`
|
|
195
|
-
`).filter((l) => l.trim());
|
|
196
|
-
if (lines.length > 1) {
|
|
197
|
-
return { inUse: true };
|
|
198
|
-
}
|
|
199
|
-
return { inUse: false };
|
|
200
|
-
}
|
|
201
|
-
} catch {
|
|
202
|
-
return { inUse: false };
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
187
|
async function waitForPortFree(port, timeoutMs = 5000) {
|
|
206
188
|
const startTime = Date.now();
|
|
207
189
|
const pollInterval = 300;
|
|
@@ -409,9 +391,6 @@ async function readFileTail(filePath, lines) {
|
|
|
409
391
|
}
|
|
410
392
|
}) ?? "";
|
|
411
393
|
}
|
|
412
|
-
function copyFile(src, dest) {
|
|
413
|
-
fs.copyFileSync(src, dest);
|
|
414
|
-
}
|
|
415
394
|
async function getProcessMemory(pid) {
|
|
416
395
|
const map = await getProcessBatchResources([pid]);
|
|
417
396
|
return map.get(pid)?.memory || 0;
|
|
@@ -424,7 +403,7 @@ async function getProcessBatchResources(pids) {
|
|
|
424
403
|
const pidSet = new Set(pids);
|
|
425
404
|
try {
|
|
426
405
|
if (isWindows()) {
|
|
427
|
-
const output = psExec(`Get-Process -Id ${pids.join(",")} -ErrorAction SilentlyContinue | Select-Object Id, WorkingSet64 | ForEach-Object { Write-Output "$($_.Id)|$($_.WorkingSet64)" }`);
|
|
406
|
+
const output = await psExec(`Get-Process -Id ${pids.join(",")} -ErrorAction SilentlyContinue | Select-Object Id, WorkingSet64 | ForEach-Object { Write-Output "$($_.Id)|$($_.WorkingSet64)" }`);
|
|
428
407
|
for (const line of output.split(`
|
|
429
408
|
`)) {
|
|
430
409
|
const sepIdx = line.indexOf("|");
|
|
@@ -504,6 +483,21 @@ async function getProcessPorts(pid) {
|
|
|
504
483
|
return [];
|
|
505
484
|
}
|
|
506
485
|
}
|
|
486
|
+
async function resolvePidWithPorts(pid) {
|
|
487
|
+
const ports = await getProcessPorts(pid);
|
|
488
|
+
if (ports.length > 0 || !isWindows() || pid <= 0) {
|
|
489
|
+
return { pid, ports };
|
|
490
|
+
}
|
|
491
|
+
const childPid = await findChildPid(pid);
|
|
492
|
+
if (childPid === pid || childPid <= 0) {
|
|
493
|
+
return { pid, ports };
|
|
494
|
+
}
|
|
495
|
+
const childPorts = await getProcessPorts(childPid);
|
|
496
|
+
if (childPorts.length > 0) {
|
|
497
|
+
return { pid: childPid, ports: childPorts };
|
|
498
|
+
}
|
|
499
|
+
return { pid, ports };
|
|
500
|
+
}
|
|
507
501
|
var plat;
|
|
508
502
|
var init_platform = __esm(() => {
|
|
509
503
|
plat = createMeasure("platform");
|
|
@@ -580,7 +574,7 @@ function removeProcessByName(name) {
|
|
|
580
574
|
function updateProcessPid(name, newPid) {
|
|
581
575
|
const proc = db.process.select().where({ name }).limit(1).get();
|
|
582
576
|
if (proc) {
|
|
583
|
-
|
|
577
|
+
proc.update({ pid: newPid });
|
|
584
578
|
}
|
|
585
579
|
}
|
|
586
580
|
function removeAllProcesses() {
|
|
@@ -592,7 +586,7 @@ function removeAllProcesses() {
|
|
|
592
586
|
function updateProcessEnv(name, envJson) {
|
|
593
587
|
const proc = db.process.select().where({ name }).limit(1).get();
|
|
594
588
|
if (proc) {
|
|
595
|
-
|
|
589
|
+
proc.update({ env: envJson });
|
|
596
590
|
}
|
|
597
591
|
}
|
|
598
592
|
function getAllTemplates() {
|
|
@@ -830,6 +824,8 @@ var init_db = __esm(() => {
|
|
|
830
824
|
|
|
831
825
|
// src/utils.ts
|
|
832
826
|
import * as fs2 from "fs";
|
|
827
|
+
import * as os2 from "os";
|
|
828
|
+
import { join as join3 } from "path";
|
|
833
829
|
function parseEnvString(envString) {
|
|
834
830
|
const env = {};
|
|
835
831
|
envString.split(",").forEach((pair) => {
|
|
@@ -845,9 +841,92 @@ function calculateRuntime(startTime) {
|
|
|
845
841
|
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
846
842
|
return `${diffInMinutes} minutes`;
|
|
847
843
|
}
|
|
844
|
+
function parseCommandEnv(command) {
|
|
845
|
+
const env = {};
|
|
846
|
+
const trimmed = command.trim();
|
|
847
|
+
const windowsSegments = trimmed.split(/&&/).map((segment) => segment.trim());
|
|
848
|
+
for (const segment of windowsSegments) {
|
|
849
|
+
const match = segment.match(/^set\s+([A-Za-z_][A-Za-z0-9_]*)=(.*)$/i);
|
|
850
|
+
if (!match)
|
|
851
|
+
break;
|
|
852
|
+
env[match[1]] = match[2].trim();
|
|
853
|
+
}
|
|
854
|
+
const unixPrefixRegex = /^(?:([A-Za-z_][A-Za-z0-9_]*)=([^\s]+)\s+)+/;
|
|
855
|
+
const unixPrefix = trimmed.match(unixPrefixRegex);
|
|
856
|
+
if (unixPrefix) {
|
|
857
|
+
const pairs = unixPrefix[0].trim().split(/\s+/);
|
|
858
|
+
for (const pair of pairs) {
|
|
859
|
+
const eqIdx = pair.indexOf("=");
|
|
860
|
+
if (eqIdx <= 0)
|
|
861
|
+
continue;
|
|
862
|
+
const key = pair.slice(0, eqIdx);
|
|
863
|
+
const value = pair.slice(eqIdx + 1);
|
|
864
|
+
if (key)
|
|
865
|
+
env[key] = value;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return env;
|
|
869
|
+
}
|
|
870
|
+
function getDeclaredPort(processEnv, command) {
|
|
871
|
+
const mergedEnv = { ...command ? parseCommandEnv(command) : {}, ...processEnv };
|
|
872
|
+
const raw = mergedEnv.PORT || mergedEnv.BUN_PORT || "";
|
|
873
|
+
const parsed = parseInt(raw, 10);
|
|
874
|
+
return !isNaN(parsed) && parsed > 0 ? parsed : null;
|
|
875
|
+
}
|
|
876
|
+
function buildManagedProcessEnv(parentEnv, processEnv = {}) {
|
|
877
|
+
const sanitizedParentEnv = {};
|
|
878
|
+
for (const [key, value] of Object.entries(parentEnv)) {
|
|
879
|
+
if (value === undefined)
|
|
880
|
+
continue;
|
|
881
|
+
if (INTERNAL_MANAGED_ENV_KEYS.includes(key))
|
|
882
|
+
continue;
|
|
883
|
+
sanitizedParentEnv[key] = value;
|
|
884
|
+
}
|
|
885
|
+
return { ...sanitizedParentEnv, ...processEnv };
|
|
886
|
+
}
|
|
887
|
+
function stringifyEnvString(env) {
|
|
888
|
+
return Object.entries(env).map(([key, value]) => `${key}=${value}`).join(",");
|
|
889
|
+
}
|
|
890
|
+
function getWatcherProcessName(targetName) {
|
|
891
|
+
return `${WATCHER_PREFIX}${encodeURIComponent(targetName)}`;
|
|
892
|
+
}
|
|
893
|
+
function getWatchedProcessName(watcherName) {
|
|
894
|
+
if (!watcherName.startsWith(WATCHER_PREFIX))
|
|
895
|
+
return null;
|
|
896
|
+
try {
|
|
897
|
+
return decodeURIComponent(watcherName.slice(WATCHER_PREFIX.length));
|
|
898
|
+
} catch {
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function isWatcherProcessName(name) {
|
|
903
|
+
return getWatchedProcessName(name) !== null;
|
|
904
|
+
}
|
|
905
|
+
function isInternalProcessName(name) {
|
|
906
|
+
return name === "bgr-dashboard" || name === "bgr-guard" || isWatcherProcessName(name);
|
|
907
|
+
}
|
|
908
|
+
function getOperationLockPath(name) {
|
|
909
|
+
return join3(os2.homedir(), ".bgr", `${name}.operation.lock`);
|
|
910
|
+
}
|
|
911
|
+
function acquireProcessOperationLock(name) {
|
|
912
|
+
const lockPath = getOperationLockPath(name);
|
|
913
|
+
fs2.mkdirSync(join3(os2.homedir(), ".bgr"), { recursive: true });
|
|
914
|
+
fs2.writeFileSync(lockPath, JSON.stringify({ pid: process.pid, time: Date.now() }));
|
|
915
|
+
let released = false;
|
|
916
|
+
return () => {
|
|
917
|
+
if (released)
|
|
918
|
+
return;
|
|
919
|
+
released = true;
|
|
920
|
+
try {
|
|
921
|
+
fs2.unlinkSync(lockPath);
|
|
922
|
+
} catch {}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function isProcessOperationLocked(name) {
|
|
926
|
+
return fs2.existsSync(getOperationLockPath(name));
|
|
927
|
+
}
|
|
848
928
|
async function getVersion() {
|
|
849
929
|
try {
|
|
850
|
-
const { join: join3 } = await import("path");
|
|
851
930
|
const pkgPath = join3(import.meta.dir, "../package.json");
|
|
852
931
|
const pkg = await Bun.file(pkgPath).json();
|
|
853
932
|
return pkg.version || "0.0.0";
|
|
@@ -903,8 +982,10 @@ function tailFile(path, prefix, colorFn, lines) {
|
|
|
903
982
|
} catch {}
|
|
904
983
|
};
|
|
905
984
|
}
|
|
985
|
+
var INTERNAL_MANAGED_ENV_KEYS, WATCHER_PREFIX = "bgr-watch-";
|
|
906
986
|
var init_utils = __esm(() => {
|
|
907
987
|
init_platform();
|
|
988
|
+
INTERNAL_MANAGED_ENV_KEYS = ["BUN_PORT", "BGR_STDOUT", "BGR_STDERR"];
|
|
908
989
|
});
|
|
909
990
|
|
|
910
991
|
// src/deps.ts
|