bgrun 3.12.11 → 3.12.13
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/app/api/config/[name]/route.ts +1 -1
- package/dashboard/app/api/debug/route.ts +1 -1
- package/dashboard/app/api/dependencies/route.ts +40 -40
- package/dashboard/app/api/deploy/[name]/route.ts +1 -1
- package/dashboard/app/api/deploy-all/route.ts +25 -25
- package/dashboard/app/api/deps/route.ts +3 -3
- package/dashboard/app/api/guard/route.ts +1 -1
- package/dashboard/app/api/guard-all/route.ts +1 -1
- package/dashboard/app/api/guard-events/route.ts +4 -4
- package/dashboard/app/api/history/route.ts +105 -105
- package/dashboard/app/api/logs/[name]/route.ts +100 -100
- package/dashboard/app/api/logs/rotate/route.ts +2 -2
- package/dashboard/app/api/next-port/route.ts +32 -32
- package/dashboard/app/api/processes/[name]/route.ts +2 -2
- package/dashboard/app/api/processes/route.ts +4 -4
- package/dashboard/app/api/restart/[name]/route.ts +2 -2
- package/dashboard/app/api/start/route.ts +2 -2
- package/dashboard/app/api/stop/[name]/route.ts +2 -2
- package/dashboard/app/api/templates/route.ts +46 -46
- package/dashboard/app/api/version/route.ts +1 -1
- package/dashboard/lib/runtime.ts +49 -0
- package/dist/api.js +94 -67
- package/dist/deploy.js +1373 -0
- package/dist/deps.js +1004 -0
- package/dist/index.js +224 -224
- package/dist/log-rotation.js +95 -0
- package/dist/server.js +1488 -0
- package/package.json +2 -17
- package/src/api.ts +0 -63
- package/src/build.ts +0 -24
- 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/dist/index.js
CHANGED
|
@@ -510,85 +510,6 @@ var init_platform = __esm(() => {
|
|
|
510
510
|
plat = createMeasure("platform");
|
|
511
511
|
});
|
|
512
512
|
|
|
513
|
-
// src/utils.ts
|
|
514
|
-
import * as fs2 from "fs";
|
|
515
|
-
function parseEnvString(envString) {
|
|
516
|
-
const env = {};
|
|
517
|
-
envString.split(",").forEach((pair) => {
|
|
518
|
-
const [key, value] = pair.split("=");
|
|
519
|
-
if (key && value)
|
|
520
|
-
env[key] = value;
|
|
521
|
-
});
|
|
522
|
-
return env;
|
|
523
|
-
}
|
|
524
|
-
function calculateRuntime(startTime) {
|
|
525
|
-
const start = new Date(startTime).getTime();
|
|
526
|
-
const now = new Date().getTime();
|
|
527
|
-
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
528
|
-
return `${diffInMinutes} minutes`;
|
|
529
|
-
}
|
|
530
|
-
async function getVersion() {
|
|
531
|
-
try {
|
|
532
|
-
const { join: join2 } = await import("path");
|
|
533
|
-
const pkgPath = join2(import.meta.dir, "../package.json");
|
|
534
|
-
const pkg = await Bun.file(pkgPath).json();
|
|
535
|
-
return pkg.version || "0.0.0";
|
|
536
|
-
} catch {
|
|
537
|
-
return "0.0.0";
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
function validateDirectory(directory) {
|
|
541
|
-
if (!directory || !fs2.existsSync(directory)) {
|
|
542
|
-
throw new Error(`Directory not found or invalid: '${directory}'`);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
function tailFile(path, prefix, colorFn, lines) {
|
|
546
|
-
let position = 0;
|
|
547
|
-
let lastPartial = "";
|
|
548
|
-
if (!fs2.existsSync(path)) {
|
|
549
|
-
return () => {};
|
|
550
|
-
}
|
|
551
|
-
const fd = fs2.openSync(path, "r");
|
|
552
|
-
const printNewContent = () => {
|
|
553
|
-
try {
|
|
554
|
-
const stats = fs2.statSync(path);
|
|
555
|
-
if (stats.size <= position)
|
|
556
|
-
return;
|
|
557
|
-
const buffer = Buffer.alloc(stats.size - position);
|
|
558
|
-
fs2.readSync(fd, buffer, 0, buffer.length, position);
|
|
559
|
-
let content = buffer.toString();
|
|
560
|
-
content = lastPartial + content;
|
|
561
|
-
lastPartial = "";
|
|
562
|
-
const lineArray = content.split(/\r?\n/);
|
|
563
|
-
if (!content.endsWith(`
|
|
564
|
-
`)) {
|
|
565
|
-
lastPartial = lineArray.pop() || "";
|
|
566
|
-
}
|
|
567
|
-
lineArray.forEach((line) => {
|
|
568
|
-
if (line) {
|
|
569
|
-
console.log(colorFn(prefix + line));
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
position = stats.size;
|
|
573
|
-
} catch (e) {}
|
|
574
|
-
};
|
|
575
|
-
const watcher = fs2.watch(path, { persistent: true }, (event) => {
|
|
576
|
-
if (event === "change") {
|
|
577
|
-
printNewContent();
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
printNewContent();
|
|
581
|
-
return () => {
|
|
582
|
-
watcher.close();
|
|
583
|
-
try {
|
|
584
|
-
fs2.closeSync(fd);
|
|
585
|
-
} catch {}
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
var init_utils = __esm(() => {
|
|
589
|
-
init_platform();
|
|
590
|
-
});
|
|
591
|
-
|
|
592
513
|
// src/db.ts
|
|
593
514
|
var exports_db = {};
|
|
594
515
|
__export(exports_db, {
|
|
@@ -628,7 +549,7 @@ __export(exports_db, {
|
|
|
628
549
|
import { Database, z } from "sqlite-zod-orm";
|
|
629
550
|
import { join as join2 } from "path";
|
|
630
551
|
var {sleep } = globalThis.Bun;
|
|
631
|
-
import { existsSync as
|
|
552
|
+
import { existsSync as existsSync2, copyFileSync as copyFileSync2 } from "fs";
|
|
632
553
|
function shouldAutoMigrateLegacyDb() {
|
|
633
554
|
const raw = (process.env.BGRUN_DISABLE_LEGACY_MIGRATION || "").trim().toLowerCase();
|
|
634
555
|
return !(raw === "1" || raw === "true" || raw === "yes");
|
|
@@ -828,7 +749,7 @@ function getDbInfo() {
|
|
|
828
749
|
dbPath,
|
|
829
750
|
bgrHome,
|
|
830
751
|
dbFilename,
|
|
831
|
-
exists:
|
|
752
|
+
exists: existsSync2(dbPath)
|
|
832
753
|
};
|
|
833
754
|
}
|
|
834
755
|
async function retryDatabaseOperation(operation, maxRetries = 5, delay = 100) {
|
|
@@ -887,7 +808,7 @@ var init_db = __esm(() => {
|
|
|
887
808
|
dbPath = join2(bgrDir, dbFilename);
|
|
888
809
|
bgrHome = bgrDir;
|
|
889
810
|
legacyDbPath = join2(bgrDir, "bgr_v2.sqlite");
|
|
890
|
-
if (shouldAutoMigrateLegacyDb() && !
|
|
811
|
+
if (shouldAutoMigrateLegacyDb() && !existsSync2(dbPath) && existsSync2(legacyDbPath)) {
|
|
891
812
|
try {
|
|
892
813
|
copyFileSync2(legacyDbPath, dbPath);
|
|
893
814
|
console.log(`[bgrun] Migrated database: ${legacyDbPath} \u2192 ${dbPath}`);
|
|
@@ -908,71 +829,84 @@ var init_db = __esm(() => {
|
|
|
908
829
|
});
|
|
909
830
|
});
|
|
910
831
|
|
|
911
|
-
// src/
|
|
912
|
-
import
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
borderStyle: "round"
|
|
922
|
-
}));
|
|
832
|
+
// src/utils.ts
|
|
833
|
+
import * as fs2 from "fs";
|
|
834
|
+
function parseEnvString(envString) {
|
|
835
|
+
const env = {};
|
|
836
|
+
envString.split(",").forEach((pair) => {
|
|
837
|
+
const [key, value] = pair.split("=");
|
|
838
|
+
if (key && value)
|
|
839
|
+
env[key] = value;
|
|
840
|
+
});
|
|
841
|
+
return env;
|
|
923
842
|
}
|
|
924
|
-
function
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
borderColor: "red",
|
|
930
|
-
title: "Error",
|
|
931
|
-
titleAlignment: "center",
|
|
932
|
-
borderStyle: "double"
|
|
933
|
-
}));
|
|
934
|
-
throw new BgrunError(text);
|
|
843
|
+
function calculateRuntime(startTime) {
|
|
844
|
+
const start = new Date(startTime).getTime();
|
|
845
|
+
const now = new Date().getTime();
|
|
846
|
+
const diffInMinutes = Math.floor((now - start) / (1000 * 60));
|
|
847
|
+
return `${diffInMinutes} minutes`;
|
|
935
848
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// src/config.ts
|
|
947
|
-
function formatEnvKey(key) {
|
|
948
|
-
return key.toUpperCase().replace(/\./g, "_");
|
|
849
|
+
async function getVersion() {
|
|
850
|
+
try {
|
|
851
|
+
const { join: join3 } = await import("path");
|
|
852
|
+
const pkgPath = join3(import.meta.dir, "../package.json");
|
|
853
|
+
const pkg = await Bun.file(pkgPath).json();
|
|
854
|
+
return pkg.version || "0.0.0";
|
|
855
|
+
} catch {
|
|
856
|
+
return "0.0.0";
|
|
857
|
+
}
|
|
949
858
|
}
|
|
950
|
-
function
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
859
|
+
function validateDirectory(directory) {
|
|
860
|
+
if (!directory || !fs2.existsSync(directory)) {
|
|
861
|
+
throw new Error(`Directory not found or invalid: '${directory}'`);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function tailFile(path, prefix, colorFn, lines) {
|
|
865
|
+
let position = 0;
|
|
866
|
+
let lastPartial = "";
|
|
867
|
+
if (!fs2.existsSync(path)) {
|
|
868
|
+
return () => {};
|
|
869
|
+
}
|
|
870
|
+
const fd = fs2.openSync(path, "r");
|
|
871
|
+
const printNewContent = () => {
|
|
872
|
+
try {
|
|
873
|
+
const stats = fs2.statSync(path);
|
|
874
|
+
if (stats.size <= position)
|
|
875
|
+
return;
|
|
876
|
+
const buffer = Buffer.alloc(stats.size - position);
|
|
877
|
+
fs2.readSync(fd, buffer, 0, buffer.length, position);
|
|
878
|
+
let content = buffer.toString();
|
|
879
|
+
content = lastPartial + content;
|
|
880
|
+
lastPartial = "";
|
|
881
|
+
const lineArray = content.split(/\r?\n/);
|
|
882
|
+
if (!content.endsWith(`
|
|
883
|
+
`)) {
|
|
884
|
+
lastPartial = lineArray.pop() || "";
|
|
885
|
+
}
|
|
886
|
+
lineArray.forEach((line) => {
|
|
887
|
+
if (line) {
|
|
888
|
+
console.log(colorFn(prefix + line));
|
|
961
889
|
}
|
|
962
890
|
});
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
891
|
+
position = stats.size;
|
|
892
|
+
} catch (e) {}
|
|
893
|
+
};
|
|
894
|
+
const watcher = fs2.watch(path, { persistent: true }, (event) => {
|
|
895
|
+
if (event === "change") {
|
|
896
|
+
printNewContent();
|
|
967
897
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
898
|
+
});
|
|
899
|
+
printNewContent();
|
|
900
|
+
return () => {
|
|
901
|
+
watcher.close();
|
|
902
|
+
try {
|
|
903
|
+
fs2.closeSync(fd);
|
|
904
|
+
} catch {}
|
|
905
|
+
};
|
|
975
906
|
}
|
|
907
|
+
var init_utils = __esm(() => {
|
|
908
|
+
init_platform();
|
|
909
|
+
});
|
|
976
910
|
|
|
977
911
|
// src/deps.ts
|
|
978
912
|
var exports_deps = {};
|
|
@@ -1063,6 +997,143 @@ var init_deps = __esm(() => {
|
|
|
1063
997
|
init_utils();
|
|
1064
998
|
});
|
|
1065
999
|
|
|
1000
|
+
// src/log-rotation.ts
|
|
1001
|
+
var exports_log_rotation = {};
|
|
1002
|
+
__export(exports_log_rotation, {
|
|
1003
|
+
startLogRotation: () => startLogRotation,
|
|
1004
|
+
rotateLogFile: () => rotateLogFile,
|
|
1005
|
+
rotateAllLogs: () => rotateAllLogs
|
|
1006
|
+
});
|
|
1007
|
+
import { existsSync as existsSync4, statSync as statSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1008
|
+
function rotateLogFile(filePath, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1009
|
+
try {
|
|
1010
|
+
if (!existsSync4(filePath))
|
|
1011
|
+
return false;
|
|
1012
|
+
const stat = statSync2(filePath);
|
|
1013
|
+
if (stat.size <= maxBytes)
|
|
1014
|
+
return false;
|
|
1015
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1016
|
+
const lines = content.split(`
|
|
1017
|
+
`);
|
|
1018
|
+
if (lines.length <= keepLines)
|
|
1019
|
+
return false;
|
|
1020
|
+
const truncated = lines.slice(-keepLines);
|
|
1021
|
+
const header = `--- [bgrun] Log rotated at ${new Date().toISOString()} (was ${lines.length} lines, ${formatBytes(stat.size)}) ---
|
|
1022
|
+
`;
|
|
1023
|
+
writeFileSync2(filePath, header + truncated.join(`
|
|
1024
|
+
`));
|
|
1025
|
+
return true;
|
|
1026
|
+
} catch {
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
function rotateAllLogs(getProcesses, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1031
|
+
const processes = getProcesses();
|
|
1032
|
+
const rotated = [];
|
|
1033
|
+
let checked = 0;
|
|
1034
|
+
for (const proc of processes) {
|
|
1035
|
+
if (proc.stdout_path) {
|
|
1036
|
+
checked++;
|
|
1037
|
+
if (rotateLogFile(proc.stdout_path, maxBytes, keepLines)) {
|
|
1038
|
+
rotated.push(`${proc.name}/stdout`);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
if (proc.stderr_path) {
|
|
1042
|
+
checked++;
|
|
1043
|
+
if (rotateLogFile(proc.stderr_path, maxBytes, keepLines)) {
|
|
1044
|
+
rotated.push(`${proc.name}/stderr`);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return { rotated, checked };
|
|
1049
|
+
}
|
|
1050
|
+
function startLogRotation(getProcesses, intervalMs = DEFAULT_CHECK_INTERVAL_MS, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1051
|
+
console.log(`[logs] Log rotation active: max ${formatBytes(maxBytes)}/file, keep ${keepLines} lines, check every ${intervalMs / 1000}s`);
|
|
1052
|
+
return setInterval(() => {
|
|
1053
|
+
const { rotated } = rotateAllLogs(getProcesses, maxBytes, keepLines);
|
|
1054
|
+
if (rotated.length > 0) {
|
|
1055
|
+
console.log(`[logs] Rotated ${rotated.length} log(s): ${rotated.join(", ")}`);
|
|
1056
|
+
}
|
|
1057
|
+
}, intervalMs);
|
|
1058
|
+
}
|
|
1059
|
+
function formatBytes(bytes) {
|
|
1060
|
+
if (bytes >= 1e6)
|
|
1061
|
+
return `${(bytes / 1e6).toFixed(1)}MB`;
|
|
1062
|
+
if (bytes >= 1000)
|
|
1063
|
+
return `${(bytes / 1000).toFixed(0)}KB`;
|
|
1064
|
+
return `${bytes}B`;
|
|
1065
|
+
}
|
|
1066
|
+
var DEFAULT_MAX_BYTES, DEFAULT_KEEP_LINES = 5000, DEFAULT_CHECK_INTERVAL_MS = 60000;
|
|
1067
|
+
var init_log_rotation = __esm(() => {
|
|
1068
|
+
DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
// src/logger.ts
|
|
1072
|
+
import boxen from "boxen";
|
|
1073
|
+
import chalk from "chalk";
|
|
1074
|
+
function announce(message, title) {
|
|
1075
|
+
console.log(boxen(message, {
|
|
1076
|
+
padding: 1,
|
|
1077
|
+
margin: 1,
|
|
1078
|
+
borderColor: "green",
|
|
1079
|
+
title: title || "bgrun",
|
|
1080
|
+
titleAlignment: "center",
|
|
1081
|
+
borderStyle: "round"
|
|
1082
|
+
}));
|
|
1083
|
+
}
|
|
1084
|
+
function error(message) {
|
|
1085
|
+
const text = message instanceof Error ? message.stack || message.message : String(message);
|
|
1086
|
+
console.error(boxen(chalk.red(text), {
|
|
1087
|
+
padding: 1,
|
|
1088
|
+
margin: 1,
|
|
1089
|
+
borderColor: "red",
|
|
1090
|
+
title: "Error",
|
|
1091
|
+
titleAlignment: "center",
|
|
1092
|
+
borderStyle: "double"
|
|
1093
|
+
}));
|
|
1094
|
+
throw new BgrunError(text);
|
|
1095
|
+
}
|
|
1096
|
+
var BgrunError;
|
|
1097
|
+
var init_logger = __esm(() => {
|
|
1098
|
+
BgrunError = class BgrunError extends Error {
|
|
1099
|
+
constructor(message) {
|
|
1100
|
+
super(message);
|
|
1101
|
+
this.name = "BgrunError";
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
// src/config.ts
|
|
1107
|
+
function formatEnvKey(key) {
|
|
1108
|
+
return key.toUpperCase().replace(/\./g, "_");
|
|
1109
|
+
}
|
|
1110
|
+
function flattenConfig(obj, prefix = "") {
|
|
1111
|
+
return Object.keys(obj).reduce((acc, key) => {
|
|
1112
|
+
const value = obj[key];
|
|
1113
|
+
const newPrefix = prefix ? `${prefix}.${key}` : key;
|
|
1114
|
+
if (Array.isArray(value)) {
|
|
1115
|
+
value.forEach((item, index) => {
|
|
1116
|
+
const indexedPrefix = `${newPrefix}.${index}`;
|
|
1117
|
+
if (typeof item === "object" && item !== null) {
|
|
1118
|
+
Object.assign(acc, flattenConfig(item, indexedPrefix));
|
|
1119
|
+
} else {
|
|
1120
|
+
acc[formatEnvKey(indexedPrefix)] = String(item);
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
} else if (typeof value === "object" && value !== null) {
|
|
1124
|
+
Object.assign(acc, flattenConfig(value, newPrefix));
|
|
1125
|
+
} else {
|
|
1126
|
+
acc[formatEnvKey(newPrefix)] = String(value);
|
|
1127
|
+
}
|
|
1128
|
+
return acc;
|
|
1129
|
+
}, {});
|
|
1130
|
+
}
|
|
1131
|
+
async function parseConfigFile(configPath) {
|
|
1132
|
+
const importPath = `${configPath}?t=${Date.now()}`;
|
|
1133
|
+
const parsedConfig = await import(importPath).then((m) => m.default);
|
|
1134
|
+
return flattenConfig(parsedConfig);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1066
1137
|
// src/commands/run.ts
|
|
1067
1138
|
var {$: $2 } = globalThis.Bun;
|
|
1068
1139
|
var {sleep: sleep2 } = globalThis.Bun;
|
|
@@ -1238,77 +1309,6 @@ var init_run = __esm(() => {
|
|
|
1238
1309
|
run = createMeasure2("run");
|
|
1239
1310
|
});
|
|
1240
1311
|
|
|
1241
|
-
// src/log-rotation.ts
|
|
1242
|
-
var exports_log_rotation = {};
|
|
1243
|
-
__export(exports_log_rotation, {
|
|
1244
|
-
startLogRotation: () => startLogRotation,
|
|
1245
|
-
rotateLogFile: () => rotateLogFile,
|
|
1246
|
-
rotateAllLogs: () => rotateAllLogs
|
|
1247
|
-
});
|
|
1248
|
-
import { existsSync as existsSync7, statSync as statSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1249
|
-
function rotateLogFile(filePath, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1250
|
-
try {
|
|
1251
|
-
if (!existsSync7(filePath))
|
|
1252
|
-
return false;
|
|
1253
|
-
const stat = statSync3(filePath);
|
|
1254
|
-
if (stat.size <= maxBytes)
|
|
1255
|
-
return false;
|
|
1256
|
-
const content = readFileSync2(filePath, "utf-8");
|
|
1257
|
-
const lines = content.split(`
|
|
1258
|
-
`);
|
|
1259
|
-
if (lines.length <= keepLines)
|
|
1260
|
-
return false;
|
|
1261
|
-
const truncated = lines.slice(-keepLines);
|
|
1262
|
-
const header = `--- [bgrun] Log rotated at ${new Date().toISOString()} (was ${lines.length} lines, ${formatBytes(stat.size)}) ---
|
|
1263
|
-
`;
|
|
1264
|
-
writeFileSync2(filePath, header + truncated.join(`
|
|
1265
|
-
`));
|
|
1266
|
-
return true;
|
|
1267
|
-
} catch {
|
|
1268
|
-
return false;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
function rotateAllLogs(getProcesses, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1272
|
-
const processes = getProcesses();
|
|
1273
|
-
const rotated = [];
|
|
1274
|
-
let checked = 0;
|
|
1275
|
-
for (const proc of processes) {
|
|
1276
|
-
if (proc.stdout_path) {
|
|
1277
|
-
checked++;
|
|
1278
|
-
if (rotateLogFile(proc.stdout_path, maxBytes, keepLines)) {
|
|
1279
|
-
rotated.push(`${proc.name}/stdout`);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
if (proc.stderr_path) {
|
|
1283
|
-
checked++;
|
|
1284
|
-
if (rotateLogFile(proc.stderr_path, maxBytes, keepLines)) {
|
|
1285
|
-
rotated.push(`${proc.name}/stderr`);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
return { rotated, checked };
|
|
1290
|
-
}
|
|
1291
|
-
function startLogRotation(getProcesses, intervalMs = DEFAULT_CHECK_INTERVAL_MS, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
1292
|
-
console.log(`[logs] Log rotation active: max ${formatBytes(maxBytes)}/file, keep ${keepLines} lines, check every ${intervalMs / 1000}s`);
|
|
1293
|
-
return setInterval(() => {
|
|
1294
|
-
const { rotated } = rotateAllLogs(getProcesses, maxBytes, keepLines);
|
|
1295
|
-
if (rotated.length > 0) {
|
|
1296
|
-
console.log(`[logs] Rotated ${rotated.length} log(s): ${rotated.join(", ")}`);
|
|
1297
|
-
}
|
|
1298
|
-
}, intervalMs);
|
|
1299
|
-
}
|
|
1300
|
-
function formatBytes(bytes) {
|
|
1301
|
-
if (bytes >= 1e6)
|
|
1302
|
-
return `${(bytes / 1e6).toFixed(1)}MB`;
|
|
1303
|
-
if (bytes >= 1000)
|
|
1304
|
-
return `${(bytes / 1000).toFixed(0)}KB`;
|
|
1305
|
-
return `${bytes}B`;
|
|
1306
|
-
}
|
|
1307
|
-
var DEFAULT_MAX_BYTES, DEFAULT_KEEP_LINES = 5000, DEFAULT_CHECK_INTERVAL_MS = 60000;
|
|
1308
|
-
var init_log_rotation = __esm(() => {
|
|
1309
|
-
DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
1310
|
-
});
|
|
1311
|
-
|
|
1312
1312
|
// src/server.ts
|
|
1313
1313
|
var exports_server = {};
|
|
1314
1314
|
__export(exports_server, {
|
|
@@ -1316,7 +1316,7 @@ __export(exports_server, {
|
|
|
1316
1316
|
guardRestartCounts: () => guardRestartCounts,
|
|
1317
1317
|
guardEvents: () => guardEvents
|
|
1318
1318
|
});
|
|
1319
|
-
import
|
|
1319
|
+
import path from "path";
|
|
1320
1320
|
async function cleanupPort(port) {
|
|
1321
1321
|
if (process.platform !== "win32")
|
|
1322
1322
|
return port;
|
|
@@ -1354,7 +1354,7 @@ async function cleanupPort(port) {
|
|
|
1354
1354
|
}
|
|
1355
1355
|
async function startServer() {
|
|
1356
1356
|
const { start } = await import("melina");
|
|
1357
|
-
const appDir =
|
|
1357
|
+
const appDir = path.join(import.meta.dir, "../dashboard/app");
|
|
1358
1358
|
const requestedPort = process.env.BUN_PORT ? parseInt(process.env.BUN_PORT, 10) : 3000;
|
|
1359
1359
|
_originalPort = requestedPort;
|
|
1360
1360
|
const resolvedPort = await cleanupPort(requestedPort);
|
|
@@ -1363,7 +1363,7 @@ async function startServer() {
|
|
|
1363
1363
|
await start({
|
|
1364
1364
|
appDir,
|
|
1365
1365
|
defaultTitle: "bgrun Dashboard - Process Manager",
|
|
1366
|
-
globalCss:
|
|
1366
|
+
globalCss: path.join(appDir, "globals.css"),
|
|
1367
1367
|
...needsExplicitPort && { port: resolvedPort }
|
|
1368
1368
|
});
|
|
1369
1369
|
startGuard();
|
|
@@ -2033,7 +2033,7 @@ init_logger();
|
|
|
2033
2033
|
init_utils();
|
|
2034
2034
|
init_run();
|
|
2035
2035
|
import * as fs4 from "fs";
|
|
2036
|
-
import
|
|
2036
|
+
import path2 from "path";
|
|
2037
2037
|
import chalk4 from "chalk";
|
|
2038
2038
|
async function handleWatch(options, logOptions) {
|
|
2039
2039
|
let currentProcess = null;
|
|
@@ -2046,10 +2046,10 @@ async function handleWatch(options, logOptions) {
|
|
|
2046
2046
|
if (!isDead)
|
|
2047
2047
|
return false;
|
|
2048
2048
|
console.log(chalk4.yellow(`\uD83D\uDC80 Process '${options.name}' died immediately after ${reason}\u2014dumping logs:`));
|
|
2049
|
-
const readAndDump = (
|
|
2049
|
+
const readAndDump = (path3, color, label) => {
|
|
2050
2050
|
try {
|
|
2051
|
-
if (fs4.existsSync(
|
|
2052
|
-
const content = fs4.readFileSync(
|
|
2051
|
+
if (fs4.existsSync(path3)) {
|
|
2052
|
+
const content = fs4.readFileSync(path3, "utf8").trim();
|
|
2053
2053
|
if (content) {
|
|
2054
2054
|
console.log(`${color.bold(label)}:
|
|
2055
2055
|
${color(content)}
|
|
@@ -2087,8 +2087,8 @@ ${color(content)}
|
|
|
2087
2087
|
resolve();
|
|
2088
2088
|
return;
|
|
2089
2089
|
}
|
|
2090
|
-
const dir =
|
|
2091
|
-
const filename =
|
|
2090
|
+
const dir = path2.dirname(logPath);
|
|
2091
|
+
const filename = path2.basename(logPath);
|
|
2092
2092
|
const watcher2 = fs4.watch(dir, (eventType, changedFilename) => {
|
|
2093
2093
|
if (changedFilename === filename && eventType === "change") {
|
|
2094
2094
|
if (checkReady()) {
|
|
@@ -2134,12 +2134,12 @@ ${color(content)}
|
|
|
2134
2134
|
}
|
|
2135
2135
|
return stops;
|
|
2136
2136
|
};
|
|
2137
|
-
const restartProcess = async (
|
|
2137
|
+
const restartProcess = async (path3) => {
|
|
2138
2138
|
if (isRestarting)
|
|
2139
2139
|
return;
|
|
2140
2140
|
isRestarting = true;
|
|
2141
|
-
const restartReason =
|
|
2142
|
-
lastRestartPath =
|
|
2141
|
+
const restartReason = path3 ? `restart (change in ${path3})` : "initial start";
|
|
2142
|
+
lastRestartPath = path3 || null;
|
|
2143
2143
|
tailStops.forEach((stop) => stop());
|
|
2144
2144
|
tailStops = [];
|
|
2145
2145
|
console.clear();
|
|
@@ -2192,7 +2192,7 @@ ${color(content)}
|
|
|
2192
2192
|
const watcher = fs4.watch(workdir, { recursive: true }, (eventType, filename) => {
|
|
2193
2193
|
if (filename == null)
|
|
2194
2194
|
return;
|
|
2195
|
-
const fullPath =
|
|
2195
|
+
const fullPath = path2.join(workdir, filename);
|
|
2196
2196
|
if (fullPath.includes(".git") || fullPath.includes("node_modules"))
|
|
2197
2197
|
return;
|
|
2198
2198
|
if (debounceTimeout)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
var __require = import.meta.require;
|
|
18
|
+
|
|
19
|
+
// src/log-rotation.ts
|
|
20
|
+
var exports_log_rotation = {};
|
|
21
|
+
__export(exports_log_rotation, {
|
|
22
|
+
startLogRotation: () => startLogRotation,
|
|
23
|
+
rotateLogFile: () => rotateLogFile,
|
|
24
|
+
rotateAllLogs: () => rotateAllLogs
|
|
25
|
+
});
|
|
26
|
+
import { existsSync, statSync, readFileSync, writeFileSync } from "fs";
|
|
27
|
+
function rotateLogFile(filePath, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
28
|
+
try {
|
|
29
|
+
if (!existsSync(filePath))
|
|
30
|
+
return false;
|
|
31
|
+
const stat = statSync(filePath);
|
|
32
|
+
if (stat.size <= maxBytes)
|
|
33
|
+
return false;
|
|
34
|
+
const content = readFileSync(filePath, "utf-8");
|
|
35
|
+
const lines = content.split(`
|
|
36
|
+
`);
|
|
37
|
+
if (lines.length <= keepLines)
|
|
38
|
+
return false;
|
|
39
|
+
const truncated = lines.slice(-keepLines);
|
|
40
|
+
const header = `--- [bgrun] Log rotated at ${new Date().toISOString()} (was ${lines.length} lines, ${formatBytes(stat.size)}) ---
|
|
41
|
+
`;
|
|
42
|
+
writeFileSync(filePath, header + truncated.join(`
|
|
43
|
+
`));
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function rotateAllLogs(getProcesses, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
50
|
+
const processes = getProcesses();
|
|
51
|
+
const rotated = [];
|
|
52
|
+
let checked = 0;
|
|
53
|
+
for (const proc of processes) {
|
|
54
|
+
if (proc.stdout_path) {
|
|
55
|
+
checked++;
|
|
56
|
+
if (rotateLogFile(proc.stdout_path, maxBytes, keepLines)) {
|
|
57
|
+
rotated.push(`${proc.name}/stdout`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (proc.stderr_path) {
|
|
61
|
+
checked++;
|
|
62
|
+
if (rotateLogFile(proc.stderr_path, maxBytes, keepLines)) {
|
|
63
|
+
rotated.push(`${proc.name}/stderr`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { rotated, checked };
|
|
68
|
+
}
|
|
69
|
+
function startLogRotation(getProcesses, intervalMs = DEFAULT_CHECK_INTERVAL_MS, maxBytes = DEFAULT_MAX_BYTES, keepLines = DEFAULT_KEEP_LINES) {
|
|
70
|
+
console.log(`[logs] Log rotation active: max ${formatBytes(maxBytes)}/file, keep ${keepLines} lines, check every ${intervalMs / 1000}s`);
|
|
71
|
+
return setInterval(() => {
|
|
72
|
+
const { rotated } = rotateAllLogs(getProcesses, maxBytes, keepLines);
|
|
73
|
+
if (rotated.length > 0) {
|
|
74
|
+
console.log(`[logs] Rotated ${rotated.length} log(s): ${rotated.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
}, intervalMs);
|
|
77
|
+
}
|
|
78
|
+
function formatBytes(bytes) {
|
|
79
|
+
if (bytes >= 1e6)
|
|
80
|
+
return `${(bytes / 1e6).toFixed(1)}MB`;
|
|
81
|
+
if (bytes >= 1000)
|
|
82
|
+
return `${(bytes / 1000).toFixed(0)}KB`;
|
|
83
|
+
return `${bytes}B`;
|
|
84
|
+
}
|
|
85
|
+
var DEFAULT_MAX_BYTES, DEFAULT_KEEP_LINES = 5000, DEFAULT_CHECK_INTERVAL_MS = 60000;
|
|
86
|
+
var init_log_rotation = __esm(() => {
|
|
87
|
+
DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
88
|
+
});
|
|
89
|
+
init_log_rotation();
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
startLogRotation,
|
|
93
|
+
rotateLogFile,
|
|
94
|
+
rotateAllLogs
|
|
95
|
+
};
|