bgrun 3.12.23 → 3.12.25
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/dist/api.js +107 -71
- package/dist/deploy.js +107 -71
- package/dist/deps.js +15 -2
- package/dist/index.js +199 -96
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -240,7 +240,7 @@ function ensureDir(dirPath) {
|
|
|
240
240
|
}
|
|
241
241
|
function getShellCommand(command) {
|
|
242
242
|
if (isWindows()) {
|
|
243
|
-
return ["cmd", "/c", command];
|
|
243
|
+
return [process.env.ComSpec || "cmd.exe", "/c", command];
|
|
244
244
|
} else {
|
|
245
245
|
return ["sh", "-c", command];
|
|
246
246
|
}
|
|
@@ -825,7 +825,7 @@ var init_db = __esm(() => {
|
|
|
825
825
|
// src/utils.ts
|
|
826
826
|
import * as fs2 from "fs";
|
|
827
827
|
import * as os2 from "os";
|
|
828
|
-
import { join as join3 } from "path";
|
|
828
|
+
import { delimiter, dirname, join as join3 } from "path";
|
|
829
829
|
function parseEnvString(envString) {
|
|
830
830
|
const env = {};
|
|
831
831
|
envString.split(",").forEach((pair) => {
|
|
@@ -841,6 +841,17 @@ function calculateRuntime(startTime) {
|
|
|
841
841
|
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
842
842
|
return `${diffInMinutes} minutes`;
|
|
843
843
|
}
|
|
844
|
+
function prependPathEntry(existingPath, entry) {
|
|
845
|
+
if (!existingPath)
|
|
846
|
+
return entry;
|
|
847
|
+
const parts = existingPath.split(delimiter).filter(Boolean);
|
|
848
|
+
const normalizedEntry = process.platform === "win32" ? entry.toLowerCase() : entry;
|
|
849
|
+
const deduped = parts.filter((part) => {
|
|
850
|
+
const normalizedPart = process.platform === "win32" ? part.toLowerCase() : part;
|
|
851
|
+
return normalizedPart !== normalizedEntry;
|
|
852
|
+
});
|
|
853
|
+
return [entry, ...deduped].join(delimiter);
|
|
854
|
+
}
|
|
844
855
|
function parseCommandEnv(command) {
|
|
845
856
|
const env = {};
|
|
846
857
|
const trimmed = command.trim();
|
|
@@ -882,6 +893,8 @@ function buildManagedProcessEnv(parentEnv, processEnv = {}) {
|
|
|
882
893
|
continue;
|
|
883
894
|
sanitizedParentEnv[key] = value;
|
|
884
895
|
}
|
|
896
|
+
const bunDir = dirname(process.execPath);
|
|
897
|
+
sanitizedParentEnv.PATH = prependPathEntry(sanitizedParentEnv.PATH, bunDir);
|
|
885
898
|
return { ...sanitizedParentEnv, ...processEnv };
|
|
886
899
|
}
|
|
887
900
|
function stringifyEnvString(env) {
|
|
@@ -1181,79 +1194,28 @@ import { join as join4 } from "path";
|
|
|
1181
1194
|
|
|
1182
1195
|
// src/cli-helpers.ts
|
|
1183
1196
|
init_db();
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
"
|
|
1187
|
-
"
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
"december"
|
|
1197
|
-
];
|
|
1198
|
-
var DAY_NAMES = [
|
|
1199
|
-
"",
|
|
1200
|
-
"first",
|
|
1201
|
-
"second",
|
|
1202
|
-
"third",
|
|
1203
|
-
"fourth",
|
|
1204
|
-
"fifth",
|
|
1205
|
-
"sixth",
|
|
1206
|
-
"seventh",
|
|
1207
|
-
"eighth",
|
|
1208
|
-
"ninth",
|
|
1209
|
-
"tenth",
|
|
1210
|
-
"eleventh",
|
|
1211
|
-
"twelfth",
|
|
1212
|
-
"thirteenth",
|
|
1213
|
-
"fourteenth",
|
|
1214
|
-
"fifteenth",
|
|
1215
|
-
"sixteenth",
|
|
1216
|
-
"seventeenth",
|
|
1217
|
-
"eighteenth",
|
|
1218
|
-
"nineteenth",
|
|
1219
|
-
"twentieth",
|
|
1220
|
-
"twenty-first",
|
|
1221
|
-
"twenty-second",
|
|
1222
|
-
"twenty-third",
|
|
1223
|
-
"twenty-fourth",
|
|
1224
|
-
"twenty-fifth",
|
|
1225
|
-
"twenty-sixth",
|
|
1226
|
-
"twenty-seventh",
|
|
1227
|
-
"twenty-eighth",
|
|
1228
|
-
"twenty-ninth",
|
|
1229
|
-
"thirtieth",
|
|
1230
|
-
"thirty-first"
|
|
1231
|
-
];
|
|
1232
|
-
function pad2(value) {
|
|
1233
|
-
return String(value).padStart(2, "0");
|
|
1234
|
-
}
|
|
1235
|
-
function buildDateProcessName(now = new Date) {
|
|
1236
|
-
const month = MONTH_NAMES[now.getMonth()] || "process";
|
|
1237
|
-
const day = DAY_NAMES[now.getDate()] || "day";
|
|
1238
|
-
return `${month}-${day}`;
|
|
1239
|
-
}
|
|
1240
|
-
function generateAutoProcessName(now = new Date) {
|
|
1241
|
-
const baseName = buildDateProcessName(now);
|
|
1197
|
+
import { basename, resolve } from "path";
|
|
1198
|
+
function sanitizeProcessName(value) {
|
|
1199
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
|
|
1200
|
+
return normalized || "process";
|
|
1201
|
+
}
|
|
1202
|
+
function buildDirectoryProcessName(directory) {
|
|
1203
|
+
const resolved = resolve(directory);
|
|
1204
|
+
const folderName = basename(resolved);
|
|
1205
|
+
return sanitizeProcessName(folderName);
|
|
1206
|
+
}
|
|
1207
|
+
function generateAutoProcessName(directory) {
|
|
1208
|
+
const baseName = buildDirectoryProcessName(directory);
|
|
1242
1209
|
if (!getProcess(baseName)) {
|
|
1243
1210
|
return baseName;
|
|
1244
1211
|
}
|
|
1245
|
-
const timeSuffix = `${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
|
|
1246
|
-
const timeName = `${baseName}-${timeSuffix}`;
|
|
1247
|
-
if (!getProcess(timeName)) {
|
|
1248
|
-
return timeName;
|
|
1249
|
-
}
|
|
1250
1212
|
for (let i = 1;i < 1000; i++) {
|
|
1251
|
-
const candidate = `${
|
|
1213
|
+
const candidate = `${baseName}-${i}`;
|
|
1252
1214
|
if (!getProcess(candidate)) {
|
|
1253
1215
|
return candidate;
|
|
1254
1216
|
}
|
|
1255
1217
|
}
|
|
1256
|
-
return `${
|
|
1218
|
+
return `${baseName}-${Date.now()}`;
|
|
1257
1219
|
}
|
|
1258
1220
|
function shellQuoteArg(arg) {
|
|
1259
1221
|
if (process.platform === "win32") {
|
|
@@ -1465,6 +1427,78 @@ function getRecentGuardEvents(limit = 100) {
|
|
|
1465
1427
|
var homePath2 = getHomeDir();
|
|
1466
1428
|
var run = createMeasure2("run");
|
|
1467
1429
|
var INTERNAL_BUNX_PREFIX = "bunx bgrun";
|
|
1430
|
+
async function resolveSpawnedProcessPid(parentPid, command, workdir) {
|
|
1431
|
+
if (parentPid <= 0)
|
|
1432
|
+
return 0;
|
|
1433
|
+
let candidatePid = parentPid;
|
|
1434
|
+
const spawnedAt = Date.now();
|
|
1435
|
+
for (let attempt = 0;attempt < 6; attempt++) {
|
|
1436
|
+
const descendantPid = await findChildPid(parentPid);
|
|
1437
|
+
if (descendantPid > 0) {
|
|
1438
|
+
candidatePid = descendantPid;
|
|
1439
|
+
}
|
|
1440
|
+
if (candidatePid > 0 && await isProcessRunning(candidatePid, command)) {
|
|
1441
|
+
return candidatePid;
|
|
1442
|
+
}
|
|
1443
|
+
await sleep2(250);
|
|
1444
|
+
}
|
|
1445
|
+
const reconciled = await reconcileProcessPids([{ name: "__spawn__", pid: parentPid, command, workdir }], new Set([parentPid]));
|
|
1446
|
+
const matchedPid = reconciled.get("__spawn__") ?? 0;
|
|
1447
|
+
if (matchedPid > 0 && await isProcessRunning(matchedPid, command)) {
|
|
1448
|
+
return matchedPid;
|
|
1449
|
+
}
|
|
1450
|
+
if (process.platform === "win32") {
|
|
1451
|
+
try {
|
|
1452
|
+
const commandParts = command.toLowerCase().split(/\s+/).map((part) => part.trim()).filter((part) => part.length > 2);
|
|
1453
|
+
const output = await psExec(`Get-CimInstance Win32_Process -Filter "Name='bun.exe'" | ` + `ForEach-Object { Write-Output "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate)|$($_.CommandLine)" }`, 5000);
|
|
1454
|
+
let bestPid = 0;
|
|
1455
|
+
let bestScore = -1;
|
|
1456
|
+
for (const line of output.split(`
|
|
1457
|
+
`)) {
|
|
1458
|
+
const parts = line.split("|");
|
|
1459
|
+
if (parts.length < 4)
|
|
1460
|
+
continue;
|
|
1461
|
+
const pid = parseInt(parts[0]?.trim(), 10);
|
|
1462
|
+
const candidateParentPid = parseInt(parts[1]?.trim(), 10);
|
|
1463
|
+
const creationDateRaw = parts[2]?.trim() || "";
|
|
1464
|
+
const candidateCommand = parts.slice(3).join("|").trim().toLowerCase();
|
|
1465
|
+
if (isNaN(pid) || pid <= 0 || pid === process.pid)
|
|
1466
|
+
continue;
|
|
1467
|
+
if (!candidateCommand)
|
|
1468
|
+
continue;
|
|
1469
|
+
let score = 0;
|
|
1470
|
+
if (candidateParentPid === parentPid)
|
|
1471
|
+
score += 10;
|
|
1472
|
+
for (const part of commandParts) {
|
|
1473
|
+
if (candidateCommand.includes(part))
|
|
1474
|
+
score += 2;
|
|
1475
|
+
}
|
|
1476
|
+
if (candidateCommand.includes("run server.ts"))
|
|
1477
|
+
score += 2;
|
|
1478
|
+
if (candidateCommand.includes(workdir.toLowerCase().replace(/\\/g, "/")))
|
|
1479
|
+
score += 4;
|
|
1480
|
+
if (candidateCommand.includes(workdir.toLowerCase()))
|
|
1481
|
+
score += 4;
|
|
1482
|
+
const createdAt = creationDateRaw ? Date.parse(creationDateRaw) : NaN;
|
|
1483
|
+
if (!isNaN(createdAt)) {
|
|
1484
|
+
const ageMs = Math.abs(createdAt - spawnedAt);
|
|
1485
|
+
if (ageMs <= 15000)
|
|
1486
|
+
score += 6;
|
|
1487
|
+
else if (ageMs <= 60000)
|
|
1488
|
+
score += 2;
|
|
1489
|
+
}
|
|
1490
|
+
if (score > bestScore && await isProcessRunning(pid, command)) {
|
|
1491
|
+
bestScore = score;
|
|
1492
|
+
bestPid = pid;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (bestScore >= 4) {
|
|
1496
|
+
return bestPid;
|
|
1497
|
+
}
|
|
1498
|
+
} catch {}
|
|
1499
|
+
}
|
|
1500
|
+
return 0;
|
|
1501
|
+
}
|
|
1468
1502
|
function resolveInternalBgrunCommand(command) {
|
|
1469
1503
|
const trimmed = command.trim();
|
|
1470
1504
|
if (!trimmed.startsWith("bgrun --_") && !trimmed.startsWith(`${INTERNAL_BUNX_PREFIX} --_`)) {
|
|
@@ -1675,11 +1709,13 @@ async function handleRun(options) {
|
|
|
1675
1709
|
stderr: Bun.file(stderrPath)
|
|
1676
1710
|
});
|
|
1677
1711
|
newProcess.unref();
|
|
1678
|
-
await
|
|
1679
|
-
const pid = await findChildPid(newProcess.pid);
|
|
1680
|
-
await sleep2(400);
|
|
1681
|
-
return pid;
|
|
1712
|
+
return await resolveSpawnedProcessPid(newProcess.pid, finalCommand, finalDirectory);
|
|
1682
1713
|
}) ?? 0;
|
|
1714
|
+
if (actualPid <= 0) {
|
|
1715
|
+
throw new Error(`Failed to resolve a live PID for "${name}" after launch. The child process likely exited immediately. Check logs:
|
|
1716
|
+
stdout: ${stdoutPath}
|
|
1717
|
+
stderr: ${stderrPath}`);
|
|
1718
|
+
}
|
|
1683
1719
|
await retryDatabaseOperation(() => insertProcess({
|
|
1684
1720
|
pid: actualPid,
|
|
1685
1721
|
workdir: finalDirectory,
|
package/dist/deploy.js
CHANGED
|
@@ -240,7 +240,7 @@ function ensureDir(dirPath) {
|
|
|
240
240
|
}
|
|
241
241
|
function getShellCommand(command) {
|
|
242
242
|
if (isWindows()) {
|
|
243
|
-
return ["cmd", "/c", command];
|
|
243
|
+
return [process.env.ComSpec || "cmd.exe", "/c", command];
|
|
244
244
|
} else {
|
|
245
245
|
return ["sh", "-c", command];
|
|
246
246
|
}
|
|
@@ -825,7 +825,7 @@ var init_db = __esm(() => {
|
|
|
825
825
|
// src/utils.ts
|
|
826
826
|
import * as fs2 from "fs";
|
|
827
827
|
import * as os2 from "os";
|
|
828
|
-
import { join as join3 } from "path";
|
|
828
|
+
import { delimiter, dirname, join as join3 } from "path";
|
|
829
829
|
function parseEnvString(envString) {
|
|
830
830
|
const env = {};
|
|
831
831
|
envString.split(",").forEach((pair) => {
|
|
@@ -841,6 +841,17 @@ function calculateRuntime(startTime) {
|
|
|
841
841
|
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
842
842
|
return `${diffInMinutes} minutes`;
|
|
843
843
|
}
|
|
844
|
+
function prependPathEntry(existingPath, entry) {
|
|
845
|
+
if (!existingPath)
|
|
846
|
+
return entry;
|
|
847
|
+
const parts = existingPath.split(delimiter).filter(Boolean);
|
|
848
|
+
const normalizedEntry = process.platform === "win32" ? entry.toLowerCase() : entry;
|
|
849
|
+
const deduped = parts.filter((part) => {
|
|
850
|
+
const normalizedPart = process.platform === "win32" ? part.toLowerCase() : part;
|
|
851
|
+
return normalizedPart !== normalizedEntry;
|
|
852
|
+
});
|
|
853
|
+
return [entry, ...deduped].join(delimiter);
|
|
854
|
+
}
|
|
844
855
|
function parseCommandEnv(command) {
|
|
845
856
|
const env = {};
|
|
846
857
|
const trimmed = command.trim();
|
|
@@ -882,6 +893,8 @@ function buildManagedProcessEnv(parentEnv, processEnv = {}) {
|
|
|
882
893
|
continue;
|
|
883
894
|
sanitizedParentEnv[key] = value;
|
|
884
895
|
}
|
|
896
|
+
const bunDir = dirname(process.execPath);
|
|
897
|
+
sanitizedParentEnv.PATH = prependPathEntry(sanitizedParentEnv.PATH, bunDir);
|
|
885
898
|
return { ...sanitizedParentEnv, ...processEnv };
|
|
886
899
|
}
|
|
887
900
|
function stringifyEnvString(env) {
|
|
@@ -1180,79 +1193,28 @@ import { join as join4 } from "path";
|
|
|
1180
1193
|
|
|
1181
1194
|
// src/cli-helpers.ts
|
|
1182
1195
|
init_db();
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
"
|
|
1186
|
-
"
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
"december"
|
|
1196
|
-
];
|
|
1197
|
-
var DAY_NAMES = [
|
|
1198
|
-
"",
|
|
1199
|
-
"first",
|
|
1200
|
-
"second",
|
|
1201
|
-
"third",
|
|
1202
|
-
"fourth",
|
|
1203
|
-
"fifth",
|
|
1204
|
-
"sixth",
|
|
1205
|
-
"seventh",
|
|
1206
|
-
"eighth",
|
|
1207
|
-
"ninth",
|
|
1208
|
-
"tenth",
|
|
1209
|
-
"eleventh",
|
|
1210
|
-
"twelfth",
|
|
1211
|
-
"thirteenth",
|
|
1212
|
-
"fourteenth",
|
|
1213
|
-
"fifteenth",
|
|
1214
|
-
"sixteenth",
|
|
1215
|
-
"seventeenth",
|
|
1216
|
-
"eighteenth",
|
|
1217
|
-
"nineteenth",
|
|
1218
|
-
"twentieth",
|
|
1219
|
-
"twenty-first",
|
|
1220
|
-
"twenty-second",
|
|
1221
|
-
"twenty-third",
|
|
1222
|
-
"twenty-fourth",
|
|
1223
|
-
"twenty-fifth",
|
|
1224
|
-
"twenty-sixth",
|
|
1225
|
-
"twenty-seventh",
|
|
1226
|
-
"twenty-eighth",
|
|
1227
|
-
"twenty-ninth",
|
|
1228
|
-
"thirtieth",
|
|
1229
|
-
"thirty-first"
|
|
1230
|
-
];
|
|
1231
|
-
function pad2(value) {
|
|
1232
|
-
return String(value).padStart(2, "0");
|
|
1233
|
-
}
|
|
1234
|
-
function buildDateProcessName(now = new Date) {
|
|
1235
|
-
const month = MONTH_NAMES[now.getMonth()] || "process";
|
|
1236
|
-
const day = DAY_NAMES[now.getDate()] || "day";
|
|
1237
|
-
return `${month}-${day}`;
|
|
1238
|
-
}
|
|
1239
|
-
function generateAutoProcessName(now = new Date) {
|
|
1240
|
-
const baseName = buildDateProcessName(now);
|
|
1196
|
+
import { basename, resolve } from "path";
|
|
1197
|
+
function sanitizeProcessName(value) {
|
|
1198
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
|
|
1199
|
+
return normalized || "process";
|
|
1200
|
+
}
|
|
1201
|
+
function buildDirectoryProcessName(directory) {
|
|
1202
|
+
const resolved = resolve(directory);
|
|
1203
|
+
const folderName = basename(resolved);
|
|
1204
|
+
return sanitizeProcessName(folderName);
|
|
1205
|
+
}
|
|
1206
|
+
function generateAutoProcessName(directory) {
|
|
1207
|
+
const baseName = buildDirectoryProcessName(directory);
|
|
1241
1208
|
if (!getProcess(baseName)) {
|
|
1242
1209
|
return baseName;
|
|
1243
1210
|
}
|
|
1244
|
-
const timeSuffix = `${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
|
|
1245
|
-
const timeName = `${baseName}-${timeSuffix}`;
|
|
1246
|
-
if (!getProcess(timeName)) {
|
|
1247
|
-
return timeName;
|
|
1248
|
-
}
|
|
1249
1211
|
for (let i = 1;i < 1000; i++) {
|
|
1250
|
-
const candidate = `${
|
|
1212
|
+
const candidate = `${baseName}-${i}`;
|
|
1251
1213
|
if (!getProcess(candidate)) {
|
|
1252
1214
|
return candidate;
|
|
1253
1215
|
}
|
|
1254
1216
|
}
|
|
1255
|
-
return `${
|
|
1217
|
+
return `${baseName}-${Date.now()}`;
|
|
1256
1218
|
}
|
|
1257
1219
|
function shellQuoteArg(arg) {
|
|
1258
1220
|
if (process.platform === "win32") {
|
|
@@ -1464,6 +1426,78 @@ function getRecentGuardEvents(limit = 100) {
|
|
|
1464
1426
|
var homePath2 = getHomeDir();
|
|
1465
1427
|
var run = createMeasure2("run");
|
|
1466
1428
|
var INTERNAL_BUNX_PREFIX = "bunx bgrun";
|
|
1429
|
+
async function resolveSpawnedProcessPid(parentPid, command, workdir) {
|
|
1430
|
+
if (parentPid <= 0)
|
|
1431
|
+
return 0;
|
|
1432
|
+
let candidatePid = parentPid;
|
|
1433
|
+
const spawnedAt = Date.now();
|
|
1434
|
+
for (let attempt = 0;attempt < 6; attempt++) {
|
|
1435
|
+
const descendantPid = await findChildPid(parentPid);
|
|
1436
|
+
if (descendantPid > 0) {
|
|
1437
|
+
candidatePid = descendantPid;
|
|
1438
|
+
}
|
|
1439
|
+
if (candidatePid > 0 && await isProcessRunning(candidatePid, command)) {
|
|
1440
|
+
return candidatePid;
|
|
1441
|
+
}
|
|
1442
|
+
await sleep2(250);
|
|
1443
|
+
}
|
|
1444
|
+
const reconciled = await reconcileProcessPids([{ name: "__spawn__", pid: parentPid, command, workdir }], new Set([parentPid]));
|
|
1445
|
+
const matchedPid = reconciled.get("__spawn__") ?? 0;
|
|
1446
|
+
if (matchedPid > 0 && await isProcessRunning(matchedPid, command)) {
|
|
1447
|
+
return matchedPid;
|
|
1448
|
+
}
|
|
1449
|
+
if (process.platform === "win32") {
|
|
1450
|
+
try {
|
|
1451
|
+
const commandParts = command.toLowerCase().split(/\s+/).map((part) => part.trim()).filter((part) => part.length > 2);
|
|
1452
|
+
const output = await psExec(`Get-CimInstance Win32_Process -Filter "Name='bun.exe'" | ` + `ForEach-Object { Write-Output "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate)|$($_.CommandLine)" }`, 5000);
|
|
1453
|
+
let bestPid = 0;
|
|
1454
|
+
let bestScore = -1;
|
|
1455
|
+
for (const line of output.split(`
|
|
1456
|
+
`)) {
|
|
1457
|
+
const parts = line.split("|");
|
|
1458
|
+
if (parts.length < 4)
|
|
1459
|
+
continue;
|
|
1460
|
+
const pid = parseInt(parts[0]?.trim(), 10);
|
|
1461
|
+
const candidateParentPid = parseInt(parts[1]?.trim(), 10);
|
|
1462
|
+
const creationDateRaw = parts[2]?.trim() || "";
|
|
1463
|
+
const candidateCommand = parts.slice(3).join("|").trim().toLowerCase();
|
|
1464
|
+
if (isNaN(pid) || pid <= 0 || pid === process.pid)
|
|
1465
|
+
continue;
|
|
1466
|
+
if (!candidateCommand)
|
|
1467
|
+
continue;
|
|
1468
|
+
let score = 0;
|
|
1469
|
+
if (candidateParentPid === parentPid)
|
|
1470
|
+
score += 10;
|
|
1471
|
+
for (const part of commandParts) {
|
|
1472
|
+
if (candidateCommand.includes(part))
|
|
1473
|
+
score += 2;
|
|
1474
|
+
}
|
|
1475
|
+
if (candidateCommand.includes("run server.ts"))
|
|
1476
|
+
score += 2;
|
|
1477
|
+
if (candidateCommand.includes(workdir.toLowerCase().replace(/\\/g, "/")))
|
|
1478
|
+
score += 4;
|
|
1479
|
+
if (candidateCommand.includes(workdir.toLowerCase()))
|
|
1480
|
+
score += 4;
|
|
1481
|
+
const createdAt = creationDateRaw ? Date.parse(creationDateRaw) : NaN;
|
|
1482
|
+
if (!isNaN(createdAt)) {
|
|
1483
|
+
const ageMs = Math.abs(createdAt - spawnedAt);
|
|
1484
|
+
if (ageMs <= 15000)
|
|
1485
|
+
score += 6;
|
|
1486
|
+
else if (ageMs <= 60000)
|
|
1487
|
+
score += 2;
|
|
1488
|
+
}
|
|
1489
|
+
if (score > bestScore && await isProcessRunning(pid, command)) {
|
|
1490
|
+
bestScore = score;
|
|
1491
|
+
bestPid = pid;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (bestScore >= 4) {
|
|
1495
|
+
return bestPid;
|
|
1496
|
+
}
|
|
1497
|
+
} catch {}
|
|
1498
|
+
}
|
|
1499
|
+
return 0;
|
|
1500
|
+
}
|
|
1467
1501
|
function resolveInternalBgrunCommand(command) {
|
|
1468
1502
|
const trimmed = command.trim();
|
|
1469
1503
|
if (!trimmed.startsWith("bgrun --_") && !trimmed.startsWith(`${INTERNAL_BUNX_PREFIX} --_`)) {
|
|
@@ -1674,11 +1708,13 @@ async function handleRun(options) {
|
|
|
1674
1708
|
stderr: Bun.file(stderrPath)
|
|
1675
1709
|
});
|
|
1676
1710
|
newProcess.unref();
|
|
1677
|
-
await
|
|
1678
|
-
const pid = await findChildPid(newProcess.pid);
|
|
1679
|
-
await sleep2(400);
|
|
1680
|
-
return pid;
|
|
1711
|
+
return await resolveSpawnedProcessPid(newProcess.pid, finalCommand, finalDirectory);
|
|
1681
1712
|
}) ?? 0;
|
|
1713
|
+
if (actualPid <= 0) {
|
|
1714
|
+
throw new Error(`Failed to resolve a live PID for "${name}" after launch. The child process likely exited immediately. Check logs:
|
|
1715
|
+
stdout: ${stdoutPath}
|
|
1716
|
+
stderr: ${stderrPath}`);
|
|
1717
|
+
}
|
|
1682
1718
|
await retryDatabaseOperation(() => insertProcess({
|
|
1683
1719
|
pid: actualPid,
|
|
1684
1720
|
workdir: finalDirectory,
|
package/dist/deps.js
CHANGED
|
@@ -240,7 +240,7 @@ function ensureDir(dirPath) {
|
|
|
240
240
|
}
|
|
241
241
|
function getShellCommand(command) {
|
|
242
242
|
if (isWindows()) {
|
|
243
|
-
return ["cmd", "/c", command];
|
|
243
|
+
return [process.env.ComSpec || "cmd.exe", "/c", command];
|
|
244
244
|
} else {
|
|
245
245
|
return ["sh", "-c", command];
|
|
246
246
|
}
|
|
@@ -825,7 +825,7 @@ var init_db = __esm(() => {
|
|
|
825
825
|
// src/utils.ts
|
|
826
826
|
import * as fs2 from "fs";
|
|
827
827
|
import * as os2 from "os";
|
|
828
|
-
import { join as join3 } from "path";
|
|
828
|
+
import { delimiter, dirname, join as join3 } from "path";
|
|
829
829
|
function parseEnvString(envString) {
|
|
830
830
|
const env = {};
|
|
831
831
|
envString.split(",").forEach((pair) => {
|
|
@@ -841,6 +841,17 @@ function calculateRuntime(startTime) {
|
|
|
841
841
|
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
842
842
|
return `${diffInMinutes} minutes`;
|
|
843
843
|
}
|
|
844
|
+
function prependPathEntry(existingPath, entry) {
|
|
845
|
+
if (!existingPath)
|
|
846
|
+
return entry;
|
|
847
|
+
const parts = existingPath.split(delimiter).filter(Boolean);
|
|
848
|
+
const normalizedEntry = process.platform === "win32" ? entry.toLowerCase() : entry;
|
|
849
|
+
const deduped = parts.filter((part) => {
|
|
850
|
+
const normalizedPart = process.platform === "win32" ? part.toLowerCase() : part;
|
|
851
|
+
return normalizedPart !== normalizedEntry;
|
|
852
|
+
});
|
|
853
|
+
return [entry, ...deduped].join(delimiter);
|
|
854
|
+
}
|
|
844
855
|
function parseCommandEnv(command) {
|
|
845
856
|
const env = {};
|
|
846
857
|
const trimmed = command.trim();
|
|
@@ -882,6 +893,8 @@ function buildManagedProcessEnv(parentEnv, processEnv = {}) {
|
|
|
882
893
|
continue;
|
|
883
894
|
sanitizedParentEnv[key] = value;
|
|
884
895
|
}
|
|
896
|
+
const bunDir = dirname(process.execPath);
|
|
897
|
+
sanitizedParentEnv.PATH = prependPathEntry(sanitizedParentEnv.PATH, bunDir);
|
|
885
898
|
return { ...sanitizedParentEnv, ...processEnv };
|
|
886
899
|
}
|
|
887
900
|
function stringifyEnvString(env) {
|
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ function ensureDir(dirPath) {
|
|
|
241
241
|
}
|
|
242
242
|
function getShellCommand(command) {
|
|
243
243
|
if (isWindows()) {
|
|
244
|
-
return ["cmd", "/c", command];
|
|
244
|
+
return [process.env.ComSpec || "cmd.exe", "/c", command];
|
|
245
245
|
} else {
|
|
246
246
|
return ["sh", "-c", command];
|
|
247
247
|
}
|
|
@@ -826,7 +826,7 @@ var init_db = __esm(() => {
|
|
|
826
826
|
// src/utils.ts
|
|
827
827
|
import * as fs2 from "fs";
|
|
828
828
|
import * as os2 from "os";
|
|
829
|
-
import { join as join3 } from "path";
|
|
829
|
+
import { delimiter, dirname, join as join3 } from "path";
|
|
830
830
|
function parseEnvString(envString) {
|
|
831
831
|
const env = {};
|
|
832
832
|
envString.split(",").forEach((pair) => {
|
|
@@ -842,6 +842,17 @@ function calculateRuntime(startTime) {
|
|
|
842
842
|
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
843
843
|
return `${diffInMinutes} minutes`;
|
|
844
844
|
}
|
|
845
|
+
function prependPathEntry(existingPath, entry) {
|
|
846
|
+
if (!existingPath)
|
|
847
|
+
return entry;
|
|
848
|
+
const parts = existingPath.split(delimiter).filter(Boolean);
|
|
849
|
+
const normalizedEntry = process.platform === "win32" ? entry.toLowerCase() : entry;
|
|
850
|
+
const deduped = parts.filter((part) => {
|
|
851
|
+
const normalizedPart = process.platform === "win32" ? part.toLowerCase() : part;
|
|
852
|
+
return normalizedPart !== normalizedEntry;
|
|
853
|
+
});
|
|
854
|
+
return [entry, ...deduped].join(delimiter);
|
|
855
|
+
}
|
|
845
856
|
function parseCommandEnv(command) {
|
|
846
857
|
const env = {};
|
|
847
858
|
const trimmed = command.trim();
|
|
@@ -883,6 +894,8 @@ function buildManagedProcessEnv(parentEnv, processEnv = {}) {
|
|
|
883
894
|
continue;
|
|
884
895
|
sanitizedParentEnv[key] = value;
|
|
885
896
|
}
|
|
897
|
+
const bunDir = dirname(process.execPath);
|
|
898
|
+
sanitizedParentEnv.PATH = prependPathEntry(sanitizedParentEnv.PATH, bunDir);
|
|
886
899
|
return { ...sanitizedParentEnv, ...processEnv };
|
|
887
900
|
}
|
|
888
901
|
function stringifyEnvString(env) {
|
|
@@ -1341,79 +1354,28 @@ import { join as join4 } from "path";
|
|
|
1341
1354
|
|
|
1342
1355
|
// src/cli-helpers.ts
|
|
1343
1356
|
init_db();
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
"
|
|
1347
|
-
"
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
"december"
|
|
1357
|
-
];
|
|
1358
|
-
var DAY_NAMES = [
|
|
1359
|
-
"",
|
|
1360
|
-
"first",
|
|
1361
|
-
"second",
|
|
1362
|
-
"third",
|
|
1363
|
-
"fourth",
|
|
1364
|
-
"fifth",
|
|
1365
|
-
"sixth",
|
|
1366
|
-
"seventh",
|
|
1367
|
-
"eighth",
|
|
1368
|
-
"ninth",
|
|
1369
|
-
"tenth",
|
|
1370
|
-
"eleventh",
|
|
1371
|
-
"twelfth",
|
|
1372
|
-
"thirteenth",
|
|
1373
|
-
"fourteenth",
|
|
1374
|
-
"fifteenth",
|
|
1375
|
-
"sixteenth",
|
|
1376
|
-
"seventeenth",
|
|
1377
|
-
"eighteenth",
|
|
1378
|
-
"nineteenth",
|
|
1379
|
-
"twentieth",
|
|
1380
|
-
"twenty-first",
|
|
1381
|
-
"twenty-second",
|
|
1382
|
-
"twenty-third",
|
|
1383
|
-
"twenty-fourth",
|
|
1384
|
-
"twenty-fifth",
|
|
1385
|
-
"twenty-sixth",
|
|
1386
|
-
"twenty-seventh",
|
|
1387
|
-
"twenty-eighth",
|
|
1388
|
-
"twenty-ninth",
|
|
1389
|
-
"thirtieth",
|
|
1390
|
-
"thirty-first"
|
|
1391
|
-
];
|
|
1392
|
-
function pad2(value) {
|
|
1393
|
-
return String(value).padStart(2, "0");
|
|
1394
|
-
}
|
|
1395
|
-
function buildDateProcessName(now = new Date) {
|
|
1396
|
-
const month = MONTH_NAMES[now.getMonth()] || "process";
|
|
1397
|
-
const day = DAY_NAMES[now.getDate()] || "day";
|
|
1398
|
-
return `${month}-${day}`;
|
|
1399
|
-
}
|
|
1400
|
-
function generateAutoProcessName(now = new Date) {
|
|
1401
|
-
const baseName = buildDateProcessName(now);
|
|
1357
|
+
import { basename, resolve } from "path";
|
|
1358
|
+
function sanitizeProcessName(value) {
|
|
1359
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
|
|
1360
|
+
return normalized || "process";
|
|
1361
|
+
}
|
|
1362
|
+
function buildDirectoryProcessName(directory) {
|
|
1363
|
+
const resolved = resolve(directory);
|
|
1364
|
+
const folderName = basename(resolved);
|
|
1365
|
+
return sanitizeProcessName(folderName);
|
|
1366
|
+
}
|
|
1367
|
+
function generateAutoProcessName(directory) {
|
|
1368
|
+
const baseName = buildDirectoryProcessName(directory);
|
|
1402
1369
|
if (!getProcess(baseName)) {
|
|
1403
1370
|
return baseName;
|
|
1404
1371
|
}
|
|
1405
|
-
const timeSuffix = `${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
|
|
1406
|
-
const timeName = `${baseName}-${timeSuffix}`;
|
|
1407
|
-
if (!getProcess(timeName)) {
|
|
1408
|
-
return timeName;
|
|
1409
|
-
}
|
|
1410
1372
|
for (let i = 1;i < 1000; i++) {
|
|
1411
|
-
const candidate = `${
|
|
1373
|
+
const candidate = `${baseName}-${i}`;
|
|
1412
1374
|
if (!getProcess(candidate)) {
|
|
1413
1375
|
return candidate;
|
|
1414
1376
|
}
|
|
1415
1377
|
}
|
|
1416
|
-
return `${
|
|
1378
|
+
return `${baseName}-${Date.now()}`;
|
|
1417
1379
|
}
|
|
1418
1380
|
function shellQuoteArg(arg) {
|
|
1419
1381
|
if (process.platform === "win32") {
|
|
@@ -1625,6 +1587,78 @@ function getRecentGuardEvents(limit = 100) {
|
|
|
1625
1587
|
var homePath2 = getHomeDir();
|
|
1626
1588
|
var run = createMeasure2("run");
|
|
1627
1589
|
var INTERNAL_BUNX_PREFIX = "bunx bgrun";
|
|
1590
|
+
async function resolveSpawnedProcessPid(parentPid, command, workdir) {
|
|
1591
|
+
if (parentPid <= 0)
|
|
1592
|
+
return 0;
|
|
1593
|
+
let candidatePid = parentPid;
|
|
1594
|
+
const spawnedAt = Date.now();
|
|
1595
|
+
for (let attempt = 0;attempt < 6; attempt++) {
|
|
1596
|
+
const descendantPid = await findChildPid(parentPid);
|
|
1597
|
+
if (descendantPid > 0) {
|
|
1598
|
+
candidatePid = descendantPid;
|
|
1599
|
+
}
|
|
1600
|
+
if (candidatePid > 0 && await isProcessRunning(candidatePid, command)) {
|
|
1601
|
+
return candidatePid;
|
|
1602
|
+
}
|
|
1603
|
+
await sleep2(250);
|
|
1604
|
+
}
|
|
1605
|
+
const reconciled = await reconcileProcessPids([{ name: "__spawn__", pid: parentPid, command, workdir }], new Set([parentPid]));
|
|
1606
|
+
const matchedPid = reconciled.get("__spawn__") ?? 0;
|
|
1607
|
+
if (matchedPid > 0 && await isProcessRunning(matchedPid, command)) {
|
|
1608
|
+
return matchedPid;
|
|
1609
|
+
}
|
|
1610
|
+
if (process.platform === "win32") {
|
|
1611
|
+
try {
|
|
1612
|
+
const commandParts = command.toLowerCase().split(/\s+/).map((part) => part.trim()).filter((part) => part.length > 2);
|
|
1613
|
+
const output = await psExec(`Get-CimInstance Win32_Process -Filter "Name='bun.exe'" | ` + `ForEach-Object { Write-Output "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate)|$($_.CommandLine)" }`, 5000);
|
|
1614
|
+
let bestPid = 0;
|
|
1615
|
+
let bestScore = -1;
|
|
1616
|
+
for (const line of output.split(`
|
|
1617
|
+
`)) {
|
|
1618
|
+
const parts = line.split("|");
|
|
1619
|
+
if (parts.length < 4)
|
|
1620
|
+
continue;
|
|
1621
|
+
const pid = parseInt(parts[0]?.trim(), 10);
|
|
1622
|
+
const candidateParentPid = parseInt(parts[1]?.trim(), 10);
|
|
1623
|
+
const creationDateRaw = parts[2]?.trim() || "";
|
|
1624
|
+
const candidateCommand = parts.slice(3).join("|").trim().toLowerCase();
|
|
1625
|
+
if (isNaN(pid) || pid <= 0 || pid === process.pid)
|
|
1626
|
+
continue;
|
|
1627
|
+
if (!candidateCommand)
|
|
1628
|
+
continue;
|
|
1629
|
+
let score = 0;
|
|
1630
|
+
if (candidateParentPid === parentPid)
|
|
1631
|
+
score += 10;
|
|
1632
|
+
for (const part of commandParts) {
|
|
1633
|
+
if (candidateCommand.includes(part))
|
|
1634
|
+
score += 2;
|
|
1635
|
+
}
|
|
1636
|
+
if (candidateCommand.includes("run server.ts"))
|
|
1637
|
+
score += 2;
|
|
1638
|
+
if (candidateCommand.includes(workdir.toLowerCase().replace(/\\/g, "/")))
|
|
1639
|
+
score += 4;
|
|
1640
|
+
if (candidateCommand.includes(workdir.toLowerCase()))
|
|
1641
|
+
score += 4;
|
|
1642
|
+
const createdAt = creationDateRaw ? Date.parse(creationDateRaw) : NaN;
|
|
1643
|
+
if (!isNaN(createdAt)) {
|
|
1644
|
+
const ageMs = Math.abs(createdAt - spawnedAt);
|
|
1645
|
+
if (ageMs <= 15000)
|
|
1646
|
+
score += 6;
|
|
1647
|
+
else if (ageMs <= 60000)
|
|
1648
|
+
score += 2;
|
|
1649
|
+
}
|
|
1650
|
+
if (score > bestScore && await isProcessRunning(pid, command)) {
|
|
1651
|
+
bestScore = score;
|
|
1652
|
+
bestPid = pid;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (bestScore >= 4) {
|
|
1656
|
+
return bestPid;
|
|
1657
|
+
}
|
|
1658
|
+
} catch {}
|
|
1659
|
+
}
|
|
1660
|
+
return 0;
|
|
1661
|
+
}
|
|
1628
1662
|
function resolveInternalBgrunCommand(command) {
|
|
1629
1663
|
const trimmed = command.trim();
|
|
1630
1664
|
if (!trimmed.startsWith("bgrun --_") && !trimmed.startsWith(`${INTERNAL_BUNX_PREFIX} --_`)) {
|
|
@@ -1835,11 +1869,13 @@ async function handleRun(options) {
|
|
|
1835
1869
|
stderr: Bun.file(stderrPath)
|
|
1836
1870
|
});
|
|
1837
1871
|
newProcess.unref();
|
|
1838
|
-
await
|
|
1839
|
-
const pid = await findChildPid(newProcess.pid);
|
|
1840
|
-
await sleep2(400);
|
|
1841
|
-
return pid;
|
|
1872
|
+
return await resolveSpawnedProcessPid(newProcess.pid, finalCommand, finalDirectory);
|
|
1842
1873
|
}) ?? 0;
|
|
1874
|
+
if (actualPid <= 0) {
|
|
1875
|
+
throw new Error(`Failed to resolve a live PID for "${name}" after launch. The child process likely exited immediately. Check logs:
|
|
1876
|
+
stdout: ${stdoutPath}
|
|
1877
|
+
stderr: ${stderrPath}`);
|
|
1878
|
+
}
|
|
1843
1879
|
await retryDatabaseOperation(() => insertProcess({
|
|
1844
1880
|
pid: actualPid,
|
|
1845
1881
|
workdir: finalDirectory,
|
|
@@ -2039,7 +2075,18 @@ function formatMemory(bytes) {
|
|
|
2039
2075
|
}
|
|
2040
2076
|
async function showAll(opts) {
|
|
2041
2077
|
const processes = getAllProcesses();
|
|
2042
|
-
const
|
|
2078
|
+
const latestByName = new Map;
|
|
2079
|
+
for (const proc of processes) {
|
|
2080
|
+
const existing = latestByName.get(proc.name);
|
|
2081
|
+
if (!existing) {
|
|
2082
|
+
latestByName.set(proc.name, proc);
|
|
2083
|
+
continue;
|
|
2084
|
+
}
|
|
2085
|
+
if (proc.timestamp > existing.timestamp || proc.timestamp === existing.timestamp && proc.id > existing.id) {
|
|
2086
|
+
latestByName.set(proc.name, proc);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
const filtered = Array.from(latestByName.values()).filter((proc) => {
|
|
2043
2090
|
if (isInternalProcessName(proc.name))
|
|
2044
2091
|
return false;
|
|
2045
2092
|
if (!opts?.filter)
|
|
@@ -2071,9 +2118,19 @@ async function showAll(opts) {
|
|
|
2071
2118
|
for (const proc of filtered) {
|
|
2072
2119
|
const isRunning = aliveCache.get(proc.pid) ?? await isProcessRunning(proc.pid, proc.command);
|
|
2073
2120
|
const envVars = parseEnvString(proc.env);
|
|
2074
|
-
|
|
2121
|
+
let displayPid = proc.pid;
|
|
2122
|
+
let ports = [];
|
|
2123
|
+
if (isRunning) {
|
|
2124
|
+
const resolved = await resolvePidWithPorts(proc.pid);
|
|
2125
|
+
displayPid = resolved.pid;
|
|
2126
|
+
ports = resolved.ports;
|
|
2127
|
+
if (displayPid !== proc.pid) {
|
|
2128
|
+
updateProcessPid(proc.name, displayPid);
|
|
2129
|
+
proc.pid = displayPid;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2075
2132
|
jsonData.push({
|
|
2076
|
-
pid:
|
|
2133
|
+
pid: displayPid,
|
|
2077
2134
|
name: proc.name,
|
|
2078
2135
|
ports: ports.length > 0 ? ports : undefined,
|
|
2079
2136
|
status: isRunning ? "running" : "stopped",
|
|
@@ -2334,7 +2391,7 @@ ${color(content)}
|
|
|
2334
2391
|
return true;
|
|
2335
2392
|
};
|
|
2336
2393
|
const waitForLogReady = (logPath, timeoutMs = 5000) => {
|
|
2337
|
-
return new Promise((
|
|
2394
|
+
return new Promise((resolve2, reject) => {
|
|
2338
2395
|
const checkReady = () => {
|
|
2339
2396
|
try {
|
|
2340
2397
|
if (fs4.existsSync(logPath)) {
|
|
@@ -2347,7 +2404,7 @@ ${color(content)}
|
|
|
2347
2404
|
return false;
|
|
2348
2405
|
};
|
|
2349
2406
|
if (checkReady()) {
|
|
2350
|
-
|
|
2407
|
+
resolve2();
|
|
2351
2408
|
return;
|
|
2352
2409
|
}
|
|
2353
2410
|
const dir = path2.dirname(logPath);
|
|
@@ -2356,7 +2413,7 @@ ${color(content)}
|
|
|
2356
2413
|
if (changedFilename === filename && eventType === "change") {
|
|
2357
2414
|
if (checkReady()) {
|
|
2358
2415
|
watcher2.close();
|
|
2359
|
-
|
|
2416
|
+
resolve2();
|
|
2360
2417
|
}
|
|
2361
2418
|
}
|
|
2362
2419
|
});
|
|
@@ -2874,7 +2931,7 @@ async function showHelp() {
|
|
|
2874
2931
|
${chalk7.yellow("Commands:")}
|
|
2875
2932
|
bunx bgrun List all processes
|
|
2876
2933
|
bunx bgrun [name] Show details for a process
|
|
2877
|
-
bunx bgrun -- <cmd> Start a managed process
|
|
2934
|
+
bunx bgrun -- <cmd> Start a managed process named from the working directory
|
|
2878
2935
|
bunx bgrun inline -- <cmd> Run a command in this terminal with config env loaded
|
|
2879
2936
|
bunx bgrun --env Print shell commands to export config env vars
|
|
2880
2937
|
bunx bgrun --dashboard Launch web dashboard (managed by bgrun)
|
|
@@ -2887,6 +2944,7 @@ async function showHelp() {
|
|
|
2887
2944
|
bunx bgrun --delete [name] Delete a process
|
|
2888
2945
|
bunx bgrun --clean Remove all stopped processes
|
|
2889
2946
|
bunx bgrun --nuke Delete ALL processes
|
|
2947
|
+
bunx bgrun --kill-port <n> Kill whatever is currently listening on a port
|
|
2890
2948
|
|
|
2891
2949
|
${chalk7.yellow("Options:")}
|
|
2892
2950
|
--name <string> Process name (required for new)
|
|
@@ -2897,6 +2955,7 @@ async function showHelp() {
|
|
|
2897
2955
|
--env Print shell export commands from config and exit
|
|
2898
2956
|
--shell <type> Shell for --env: powershell | cmd | sh | json
|
|
2899
2957
|
--watch Watch for file changes and auto-restart
|
|
2958
|
+
--hot Restart the managed process when files change
|
|
2900
2959
|
--force Force restart existing process
|
|
2901
2960
|
--fetch Fetch latest git changes before running
|
|
2902
2961
|
--json Output in JSON format
|
|
@@ -2910,17 +2969,21 @@ async function showHelp() {
|
|
|
2910
2969
|
--dashboard Launch web dashboard as bgrun-managed process
|
|
2911
2970
|
--guard Enable per-process crash watcher
|
|
2912
2971
|
--guard-off Disable per-process crash watcher
|
|
2972
|
+
--kill-port <number> Kill the process currently using a port
|
|
2913
2973
|
--port <number> Port for dashboard (default: 3000)
|
|
2914
2974
|
--help Show this help message
|
|
2915
2975
|
|
|
2916
2976
|
${chalk7.yellow("Examples:")}
|
|
2917
2977
|
bunx bgrun -- bun run dev
|
|
2978
|
+
bunx bgrun --hot -- bun run index.ts
|
|
2979
|
+
bunx bgrun -hl -- bun run server.ts
|
|
2918
2980
|
bunx bgrun --no-config -- bun run script.ts
|
|
2919
2981
|
bunx bgrun --force -- bun run server.ts
|
|
2920
2982
|
bunx bgrun inline -- bun run dev
|
|
2921
2983
|
Invoke-Expression (bunx bgrun --env)
|
|
2922
2984
|
eval "$(bunx bgrun --env --shell sh)"
|
|
2923
2985
|
bunx bgrun --dashboard
|
|
2986
|
+
bunx bgrun --kill-port 3000
|
|
2924
2987
|
bunx bgrun myapp --guard
|
|
2925
2988
|
bunx bgrun myapp --guard-off
|
|
2926
2989
|
bunx bgrun --name myapp --command "bun run dev" --directory . --watch
|
|
@@ -2929,15 +2992,16 @@ async function showHelp() {
|
|
|
2929
2992
|
console.log(usage);
|
|
2930
2993
|
}
|
|
2931
2994
|
var cliArgOptions = {
|
|
2932
|
-
name: { type: "string" },
|
|
2933
|
-
command: { type: "string" },
|
|
2934
|
-
directory: { type: "string" },
|
|
2995
|
+
name: { type: "string", short: "n" },
|
|
2996
|
+
command: { type: "string", short: "c" },
|
|
2997
|
+
directory: { type: "string", short: "d" },
|
|
2935
2998
|
config: { type: "string" },
|
|
2936
2999
|
"no-config": { type: "boolean" },
|
|
2937
3000
|
env: { type: "boolean" },
|
|
2938
3001
|
shell: { type: "string" },
|
|
2939
|
-
watch: { type: "boolean" },
|
|
2940
|
-
|
|
3002
|
+
watch: { type: "boolean", short: "w" },
|
|
3003
|
+
hot: { type: "boolean", short: "h" },
|
|
3004
|
+
force: { type: "boolean", short: "f" },
|
|
2941
3005
|
fetch: { type: "boolean" },
|
|
2942
3006
|
delete: { type: "boolean" },
|
|
2943
3007
|
nuke: { type: "boolean" },
|
|
@@ -2946,13 +3010,13 @@ var cliArgOptions = {
|
|
|
2946
3010
|
stop: { type: "boolean" },
|
|
2947
3011
|
"stop-all": { type: "boolean" },
|
|
2948
3012
|
clean: { type: "boolean" },
|
|
2949
|
-
json: { type: "boolean" },
|
|
2950
|
-
logs: { type: "boolean" },
|
|
3013
|
+
json: { type: "boolean", short: "j" },
|
|
3014
|
+
logs: { type: "boolean", short: "l" },
|
|
2951
3015
|
"log-stdout": { type: "boolean" },
|
|
2952
3016
|
"log-stderr": { type: "boolean" },
|
|
2953
3017
|
lines: { type: "string" },
|
|
2954
3018
|
filter: { type: "string" },
|
|
2955
|
-
version: { type: "boolean" },
|
|
3019
|
+
version: { type: "boolean", short: "v" },
|
|
2956
3020
|
help: { type: "boolean" },
|
|
2957
3021
|
db: { type: "string" },
|
|
2958
3022
|
stdout: { type: "string" },
|
|
@@ -2961,6 +3025,7 @@ var cliArgOptions = {
|
|
|
2961
3025
|
guard: { type: "boolean" },
|
|
2962
3026
|
"guard-off": { type: "boolean" },
|
|
2963
3027
|
debug: { type: "boolean" },
|
|
3028
|
+
"kill-port": { type: "string" },
|
|
2964
3029
|
_serve: { type: "boolean" },
|
|
2965
3030
|
"_watch-process": { type: "string" },
|
|
2966
3031
|
port: { type: "string" }
|
|
@@ -2968,7 +3033,7 @@ var cliArgOptions = {
|
|
|
2968
3033
|
async function run2() {
|
|
2969
3034
|
const rawArgs = Bun.argv.slice(2);
|
|
2970
3035
|
const isActionInvocation = (values2) => {
|
|
2971
|
-
return Boolean(values2.dashboard || values2.guard || values2["guard-off"] || values2.version || values2.help || values2.debug || values2.nuke || values2.clean || values2["restart-all"] || values2["stop-all"] || values2.delete || values2.restart || values2.stop || values2.logs || values2["log-stdout"] || values2["log-stderr"] || values2.watch || values2.json || values2.filter);
|
|
3036
|
+
return Boolean(values2.dashboard || values2.guard || values2["guard-off"] || values2.version || values2.help || values2.debug || values2["kill-port"] || values2.nuke || values2.clean || values2["restart-all"] || values2["stop-all"] || values2.delete || values2.restart || values2.stop || values2.logs || values2["log-stdout"] || values2["log-stderr"] || values2.watch || values2.hot || values2.json || values2.filter);
|
|
2972
3037
|
};
|
|
2973
3038
|
if (rawArgs[0] === "inline") {
|
|
2974
3039
|
const parsed = parseInlineArgs(rawArgs.slice(1));
|
|
@@ -3001,11 +3066,12 @@ async function run2() {
|
|
|
3001
3066
|
strict: false,
|
|
3002
3067
|
allowPositionals: true
|
|
3003
3068
|
});
|
|
3004
|
-
const autoName = values2.name || generateAutoProcessName();
|
|
3005
3069
|
const inlineCommand = joinCommandArgs(commandArgs);
|
|
3006
3070
|
const directory = values2.directory || process.cwd();
|
|
3007
|
-
|
|
3008
|
-
|
|
3071
|
+
const autoName = values2.name || generateAutoProcessName(directory);
|
|
3072
|
+
const watchLike = Boolean(values2.watch || values2.hot);
|
|
3073
|
+
const runOptions = {
|
|
3074
|
+
action: watchLike ? "watch" : "run",
|
|
3009
3075
|
name: autoName,
|
|
3010
3076
|
command: inlineCommand,
|
|
3011
3077
|
directory,
|
|
@@ -3016,7 +3082,16 @@ async function run2() {
|
|
|
3016
3082
|
dbPath: values2.db,
|
|
3017
3083
|
stdout: values2.stdout,
|
|
3018
3084
|
stderr: values2.stderr
|
|
3019
|
-
}
|
|
3085
|
+
};
|
|
3086
|
+
if (watchLike) {
|
|
3087
|
+
await handleWatch(runOptions, {
|
|
3088
|
+
showLogs: values2.logs || false,
|
|
3089
|
+
logType: values2["log-stdout"] ? "stdout" : values2["log-stderr"] ? "stderr" : "both",
|
|
3090
|
+
lines: values2.lines ? parseInt(values2.lines) : undefined
|
|
3091
|
+
});
|
|
3092
|
+
} else {
|
|
3093
|
+
await handleRun(runOptions);
|
|
3094
|
+
}
|
|
3020
3095
|
return;
|
|
3021
3096
|
}
|
|
3022
3097
|
const { values, positionals } = parseArgs({
|
|
@@ -3037,11 +3112,12 @@ async function run2() {
|
|
|
3037
3112
|
return;
|
|
3038
3113
|
}
|
|
3039
3114
|
if (positionals.length > 1 && !values.command && !isActionInvocation(values)) {
|
|
3040
|
-
const autoName = values.name || generateAutoProcessName();
|
|
3041
3115
|
const implicitCommand = joinCommandArgs(positionals);
|
|
3042
3116
|
const directory = values.directory || process.cwd();
|
|
3043
|
-
|
|
3044
|
-
|
|
3117
|
+
const autoName = values.name || generateAutoProcessName(directory);
|
|
3118
|
+
const watchLike = Boolean(values.watch || values.hot);
|
|
3119
|
+
const runOptions = {
|
|
3120
|
+
action: watchLike ? "watch" : "run",
|
|
3045
3121
|
name: autoName,
|
|
3046
3122
|
command: implicitCommand,
|
|
3047
3123
|
directory,
|
|
@@ -3052,7 +3128,16 @@ async function run2() {
|
|
|
3052
3128
|
dbPath: values.db,
|
|
3053
3129
|
stdout: values.stdout,
|
|
3054
3130
|
stderr: values.stderr
|
|
3055
|
-
}
|
|
3131
|
+
};
|
|
3132
|
+
if (watchLike) {
|
|
3133
|
+
await handleWatch(runOptions, {
|
|
3134
|
+
showLogs: values.logs || false,
|
|
3135
|
+
logType: values["log-stdout"] ? "stdout" : values["log-stderr"] ? "stderr" : "both",
|
|
3136
|
+
lines: values.lines ? parseInt(values.lines) : undefined
|
|
3137
|
+
});
|
|
3138
|
+
} else {
|
|
3139
|
+
await handleRun(runOptions);
|
|
3140
|
+
}
|
|
3056
3141
|
return;
|
|
3057
3142
|
}
|
|
3058
3143
|
if (values["_serve"]) {
|
|
@@ -3221,6 +3306,24 @@ async function run2() {
|
|
|
3221
3306
|
`);
|
|
3222
3307
|
return;
|
|
3223
3308
|
}
|
|
3309
|
+
if (values["kill-port"]) {
|
|
3310
|
+
const port = parseInt(String(values["kill-port"]), 10);
|
|
3311
|
+
if (isNaN(port) || port <= 0) {
|
|
3312
|
+
error("Please provide a valid port number for --kill-port.");
|
|
3313
|
+
}
|
|
3314
|
+
const wasFree = await isPortFree(port);
|
|
3315
|
+
if (wasFree) {
|
|
3316
|
+
announce(`Port ${port} is already free.`, "Port Free");
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
await killProcessOnPort(port);
|
|
3320
|
+
const freed = await waitForPortFree(port, 5000);
|
|
3321
|
+
if (!freed) {
|
|
3322
|
+
error(`Port ${port} is still busy after attempted cleanup.`);
|
|
3323
|
+
}
|
|
3324
|
+
announce(`Freed port ${port}.`, "Port Cleanup");
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3224
3327
|
if (values.nuke) {
|
|
3225
3328
|
await handleDeleteAll();
|
|
3226
3329
|
return;
|
|
@@ -3321,7 +3424,7 @@ async function run2() {
|
|
|
3321
3424
|
await showLogs(name, logType, lines);
|
|
3322
3425
|
return;
|
|
3323
3426
|
}
|
|
3324
|
-
if (values.watch) {
|
|
3427
|
+
if (values.watch || values.hot) {
|
|
3325
3428
|
await handleWatch({
|
|
3326
3429
|
action: "watch",
|
|
3327
3430
|
name,
|
package/dist/server.js
CHANGED