relife2 1.0.1 → 1.0.2
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/cli.js +499 -224
- package/dist/cli.js.map +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -14,13 +14,13 @@ var jsloader_exports = {};
|
|
|
14
14
|
__export(jsloader_exports, {
|
|
15
15
|
loadJsFile: () => loadJsFile
|
|
16
16
|
});
|
|
17
|
-
import * as
|
|
17
|
+
import * as fs7 from "node:fs";
|
|
18
18
|
import { createRequire } from "node:module";
|
|
19
|
-
import
|
|
19
|
+
import path7 from "node:path";
|
|
20
20
|
import { pathToFileURL } from "node:url";
|
|
21
21
|
async function loadJsFile(file) {
|
|
22
|
-
const ext =
|
|
23
|
-
const source =
|
|
22
|
+
const ext = path7.extname(file).toLowerCase();
|
|
23
|
+
const source = fs7.readFileSync(file, "utf8");
|
|
24
24
|
if (ext === ".mjs") return importFreshEs(file);
|
|
25
25
|
if (ext === ".cjs") return loadCjs(file, source);
|
|
26
26
|
if (CJS_MARKER.test(source)) return loadCjs(file, source);
|
|
@@ -51,7 +51,7 @@ function compileCjs(file, source) {
|
|
|
51
51
|
const nodeModule = req("node:module");
|
|
52
52
|
const mod = new nodeModule.Module(file, null);
|
|
53
53
|
mod.filename = file;
|
|
54
|
-
mod.paths = nodeModule._nodeModulePaths(
|
|
54
|
+
mod.paths = nodeModule._nodeModulePaths(path7.dirname(file));
|
|
55
55
|
mod._compile(source, file);
|
|
56
56
|
return mod.exports;
|
|
57
57
|
}
|
|
@@ -65,6 +65,7 @@ var init_jsloader = __esm({
|
|
|
65
65
|
|
|
66
66
|
// src/daemon/client.ts
|
|
67
67
|
import { spawn } from "node:child_process";
|
|
68
|
+
import fs3 from "node:fs";
|
|
68
69
|
import net from "node:net";
|
|
69
70
|
import path3 from "node:path";
|
|
70
71
|
|
|
@@ -318,28 +319,82 @@ var RpcClient = class {
|
|
|
318
319
|
async function ensureDaemonClient() {
|
|
319
320
|
const runtime = runtimeDir();
|
|
320
321
|
const data = dataDir();
|
|
322
|
+
ensureDirs(runtime, data);
|
|
321
323
|
let sock = await tryConnect(runtime);
|
|
324
|
+
if (sock !== null) {
|
|
325
|
+
return await validateClient(sock);
|
|
326
|
+
}
|
|
327
|
+
cleanStaleLockIfNeeded(runtime);
|
|
328
|
+
spawnDaemon();
|
|
329
|
+
for (let i = 0; i < 80 && sock === null; i++) {
|
|
330
|
+
await sleep(100);
|
|
331
|
+
sock = await tryConnect(runtime);
|
|
332
|
+
}
|
|
322
333
|
if (sock === null) {
|
|
334
|
+
cleanStaleLockIfNeeded(runtime);
|
|
323
335
|
spawnDaemon();
|
|
324
|
-
for (let i = 0; i <
|
|
336
|
+
for (let i = 0; i < 40 && sock === null; i++) {
|
|
325
337
|
await sleep(100);
|
|
326
338
|
sock = await tryConnect(runtime);
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
if (sock === null) {
|
|
330
342
|
throw new Error(
|
|
331
|
-
`cannot connect to relife2 daemon (runtime: ${runtime}, data: ${data}); inspect ${path3.join(data, "logs", "daemon.log")}`
|
|
343
|
+
`cannot connect to relife2 daemon (runtime: ${runtime}, data: ${data}); try 'relife2 doctor --fix' or inspect ${path3.join(data, "logs", "daemon.log")}`
|
|
332
344
|
);
|
|
333
345
|
}
|
|
346
|
+
return await validateClient(sock);
|
|
347
|
+
}
|
|
348
|
+
async function validateClient(sock) {
|
|
334
349
|
const client = new RpcClient(sock);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
350
|
+
try {
|
|
351
|
+
const ping = await client.call("ping");
|
|
352
|
+
if (ping.protocol !== PROTOCOL_VERSION) {
|
|
353
|
+
client.close();
|
|
354
|
+
throw new Error(
|
|
355
|
+
`daemon protocol mismatch (daemon: ${ping.protocol}, cli: ${PROTOCOL_VERSION}); run 'relife2 kill' to stop the stale daemon`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
return client;
|
|
359
|
+
} catch (err) {
|
|
360
|
+
try {
|
|
361
|
+
client.close();
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
throw err;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function ensureDirs(runtime, data) {
|
|
368
|
+
for (const dir of [runtime, data, path3.join(data, "logs"), path3.join(data, "state")]) {
|
|
369
|
+
try {
|
|
370
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
371
|
+
} catch {
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function cleanStaleLockIfNeeded(runtime) {
|
|
376
|
+
const file = lockPath(runtime);
|
|
377
|
+
try {
|
|
378
|
+
const raw = fs3.readFileSync(file, "utf8");
|
|
379
|
+
const parsed = JSON.parse(raw);
|
|
380
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
381
|
+
if (pid === null) {
|
|
382
|
+
fs3.unlinkSync(file);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
process.kill(pid, 0);
|
|
387
|
+
} catch (e) {
|
|
388
|
+
const code = e.code;
|
|
389
|
+
if (code === "ESRCH") {
|
|
390
|
+
try {
|
|
391
|
+
fs3.unlinkSync(file);
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} catch {
|
|
341
397
|
}
|
|
342
|
-
return client;
|
|
343
398
|
}
|
|
344
399
|
function spawnDaemon() {
|
|
345
400
|
const arg1 = process.argv[1];
|
|
@@ -493,7 +548,7 @@ async function run2(args) {
|
|
|
493
548
|
|
|
494
549
|
// src/commands/dev.ts
|
|
495
550
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
496
|
-
import * as
|
|
551
|
+
import * as fs4 from "node:fs";
|
|
497
552
|
import path4 from "node:path";
|
|
498
553
|
var DEV_VALUE_OPTS = /* @__PURE__ */ new Set(["watch", "interpreter", "env", "name", "w"]);
|
|
499
554
|
function hasBun() {
|
|
@@ -511,7 +566,7 @@ function isBunProject(cwd) {
|
|
|
511
566
|
let cur = path4.resolve(cwd);
|
|
512
567
|
const home = path4.resolve(process.env.HOME ?? "");
|
|
513
568
|
for (let i = 0; i < 12; i++) {
|
|
514
|
-
if (
|
|
569
|
+
if (fs4.existsSync(path4.join(cur, "bun.lockb")) || fs4.existsSync(path4.join(cur, "bun.lock")))
|
|
515
570
|
return true;
|
|
516
571
|
if (cur === home || cur === path4.dirname(cur)) break;
|
|
517
572
|
cur = path4.dirname(cur);
|
|
@@ -526,7 +581,7 @@ function collectSubDirs(root) {
|
|
|
526
581
|
if (!dir) continue;
|
|
527
582
|
let entries = [];
|
|
528
583
|
try {
|
|
529
|
-
entries =
|
|
584
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
530
585
|
} catch {
|
|
531
586
|
continue;
|
|
532
587
|
}
|
|
@@ -556,7 +611,7 @@ async function run3(args) {
|
|
|
556
611
|
return 1;
|
|
557
612
|
}
|
|
558
613
|
const script = path4.resolve(scriptArg);
|
|
559
|
-
if (!
|
|
614
|
+
if (!fs4.existsSync(script)) {
|
|
560
615
|
console.error(`dev: script not found: ${script}`);
|
|
561
616
|
return 1;
|
|
562
617
|
}
|
|
@@ -664,16 +719,16 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
|
|
|
664
719
|
}, 200);
|
|
665
720
|
};
|
|
666
721
|
for (const target of targets) {
|
|
667
|
-
if (!
|
|
722
|
+
if (!fs4.existsSync(target)) {
|
|
668
723
|
console.error(`dev: watch path does not exist: ${target} \u2014 skipping`);
|
|
669
724
|
continue;
|
|
670
725
|
}
|
|
671
|
-
const stat =
|
|
726
|
+
const stat = fs4.statSync(target);
|
|
672
727
|
if (!stat.isDirectory()) {
|
|
673
728
|
const dir = path4.dirname(target);
|
|
674
729
|
const base = path4.basename(target);
|
|
675
730
|
try {
|
|
676
|
-
const w =
|
|
731
|
+
const w = fs4.watch(dir, (_ev, filename) => {
|
|
677
732
|
if (filename && filename !== base) return;
|
|
678
733
|
scheduleRestart(target);
|
|
679
734
|
});
|
|
@@ -685,7 +740,7 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
|
|
|
685
740
|
}
|
|
686
741
|
if (process.platform === "win32" || process.platform === "darwin") {
|
|
687
742
|
try {
|
|
688
|
-
const w =
|
|
743
|
+
const w = fs4.watch(target, { recursive: true }, () => scheduleRestart(target));
|
|
689
744
|
watchers.push(w);
|
|
690
745
|
} catch (err) {
|
|
691
746
|
console.error(`dev: cannot watch ${target}: ${err.message}`);
|
|
@@ -694,7 +749,7 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
|
|
|
694
749
|
const dirs = collectSubDirs(target);
|
|
695
750
|
for (const dir of dirs) {
|
|
696
751
|
try {
|
|
697
|
-
const w =
|
|
752
|
+
const w = fs4.watch(dir, () => scheduleRestart(dir));
|
|
698
753
|
watchers.push(w);
|
|
699
754
|
} catch {
|
|
700
755
|
}
|
|
@@ -789,8 +844,32 @@ Examples:
|
|
|
789
844
|
}
|
|
790
845
|
|
|
791
846
|
// src/commands/doctor.ts
|
|
792
|
-
|
|
793
|
-
|
|
847
|
+
import fs5 from "node:fs";
|
|
848
|
+
import os2 from "node:os";
|
|
849
|
+
import path5 from "node:path";
|
|
850
|
+
async function run4(args) {
|
|
851
|
+
const fix = args.includes("--fix") || args.includes("-f");
|
|
852
|
+
if (fix) {
|
|
853
|
+
const healed = healLocally();
|
|
854
|
+
if (healed.length > 0) {
|
|
855
|
+
for (const m of healed) console.log(`healed: ${m}`);
|
|
856
|
+
} else {
|
|
857
|
+
console.log("heal: nothing to fix locally");
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
let client;
|
|
861
|
+
try {
|
|
862
|
+
client = await ensureDaemonClient();
|
|
863
|
+
} catch (err) {
|
|
864
|
+
if (fix) {
|
|
865
|
+
console.error(`doctor: daemon still not reachable after heal: ${err.message}`);
|
|
866
|
+
console.error("try: rm -rf ~/.relife2/state/* && relife2 kill; relife2 list");
|
|
867
|
+
} else {
|
|
868
|
+
console.error(err.message);
|
|
869
|
+
console.error("hint: relife2 doctor --fix (cleans stale lock + *.tmp.cjs)");
|
|
870
|
+
}
|
|
871
|
+
return 1;
|
|
872
|
+
}
|
|
794
873
|
try {
|
|
795
874
|
const report = await client.call("doctor", {});
|
|
796
875
|
let hadErrors = false;
|
|
@@ -807,21 +886,143 @@ async function run4(_args) {
|
|
|
807
886
|
console.log(
|
|
808
887
|
`checks: ${report.summary.total} total, ${report.summary.ok} ok, ${report.summary.warnings} warnings, ${report.summary.errors} errors`
|
|
809
888
|
);
|
|
889
|
+
if (hadErrors && !fix) {
|
|
890
|
+
console.log("hint: relife2 doctor --fix (cleans stale lock + *.tmp.cjs)");
|
|
891
|
+
}
|
|
810
892
|
return hadErrors ? 1 : 0;
|
|
811
893
|
} finally {
|
|
812
894
|
client.close();
|
|
813
895
|
}
|
|
814
896
|
}
|
|
897
|
+
function healLocally() {
|
|
898
|
+
const out = [];
|
|
899
|
+
const runtime = runtimeDir();
|
|
900
|
+
const data = dataDir();
|
|
901
|
+
for (const dir of [runtime, data, path5.join(data, "logs"), path5.join(data, "state")]) {
|
|
902
|
+
try {
|
|
903
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
904
|
+
} catch {
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const lockFile = path5.join(runtime, "daemon.lock");
|
|
908
|
+
try {
|
|
909
|
+
const raw = fs5.readFileSync(lockFile, "utf8");
|
|
910
|
+
const parsed = JSON.parse(raw);
|
|
911
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
912
|
+
let alive = false;
|
|
913
|
+
if (pid !== null) {
|
|
914
|
+
try {
|
|
915
|
+
process.kill(pid, 0);
|
|
916
|
+
alive = true;
|
|
917
|
+
} catch (e) {
|
|
918
|
+
const code = e.code;
|
|
919
|
+
if (code === "EPERM") alive = true;
|
|
920
|
+
else if (code === "ESRCH") alive = false;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (pid === null || !alive) {
|
|
924
|
+
fs5.unlinkSync(lockFile);
|
|
925
|
+
out.push(`removed stale lock ${lockFile} (pid=${pid})`);
|
|
926
|
+
}
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
const home = os2.homedir();
|
|
930
|
+
if (home && fs5.existsSync(home)) {
|
|
931
|
+
try {
|
|
932
|
+
const tmpLeftovers = collectTmpLeftovers(home, 6, 50);
|
|
933
|
+
for (const f of tmpLeftovers) {
|
|
934
|
+
try {
|
|
935
|
+
fs5.unlinkSync(f);
|
|
936
|
+
out.push(`removed leftover ${path5.relative(home, f)}`);
|
|
937
|
+
} catch {
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
} catch {
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
const tmpDir = os2.tmpdir();
|
|
945
|
+
const entries = fs5.readdirSync(tmpDir);
|
|
946
|
+
const now = Date.now();
|
|
947
|
+
for (const name of entries) {
|
|
948
|
+
if (!name.startsWith("relife2-") || !name.endsWith(".tmp.cjs")) continue;
|
|
949
|
+
const full = path5.join(tmpDir, name);
|
|
950
|
+
try {
|
|
951
|
+
const st = fs5.statSync(full);
|
|
952
|
+
if (now - st.mtimeMs > 60 * 60 * 1e3) {
|
|
953
|
+
fs5.unlinkSync(full);
|
|
954
|
+
out.push(`removed old tmp ${name}`);
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
} catch {
|
|
960
|
+
}
|
|
961
|
+
return out;
|
|
962
|
+
}
|
|
963
|
+
function collectTmpLeftovers(root, maxDepth, limit) {
|
|
964
|
+
const out = [];
|
|
965
|
+
const stack = [{ dir: root, depth: 0 }];
|
|
966
|
+
const ignored = /* @__PURE__ */ new Set([
|
|
967
|
+
"node_modules",
|
|
968
|
+
".git",
|
|
969
|
+
".hg",
|
|
970
|
+
".svn",
|
|
971
|
+
"vendor",
|
|
972
|
+
".next",
|
|
973
|
+
".output",
|
|
974
|
+
"dist",
|
|
975
|
+
"build",
|
|
976
|
+
"target",
|
|
977
|
+
"__pycache__",
|
|
978
|
+
".cache",
|
|
979
|
+
".pnpm",
|
|
980
|
+
".yarn",
|
|
981
|
+
".turbo",
|
|
982
|
+
".parcel-cache",
|
|
983
|
+
"Library",
|
|
984
|
+
"AppData",
|
|
985
|
+
".local",
|
|
986
|
+
"proc",
|
|
987
|
+
"sys",
|
|
988
|
+
"dev"
|
|
989
|
+
]);
|
|
990
|
+
while (stack.length > 0 && out.length < limit) {
|
|
991
|
+
const cur = stack.pop();
|
|
992
|
+
if (!cur) break;
|
|
993
|
+
if (cur.depth > maxDepth) continue;
|
|
994
|
+
let entries;
|
|
995
|
+
try {
|
|
996
|
+
entries = fs5.readdirSync(cur.dir, { withFileTypes: true });
|
|
997
|
+
} catch {
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
for (const ent of entries) {
|
|
1001
|
+
if (out.length >= limit) break;
|
|
1002
|
+
const full = path5.join(cur.dir, ent.name);
|
|
1003
|
+
if (ent.isFile() && ent.name.includes(".tmp.") && ent.name.endsWith(".cjs")) {
|
|
1004
|
+
if (ent.name.includes(".config.")) out.push(full);
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (!ent.isDirectory()) continue;
|
|
1008
|
+
if (ignored.has(ent.name)) continue;
|
|
1009
|
+
if (ent.name.startsWith(".") && cur.depth < 2) continue;
|
|
1010
|
+
if (ent.isSymbolicLink()) continue;
|
|
1011
|
+
if (cur.depth + 1 <= maxDepth) stack.push({ dir: full, depth: cur.depth + 1 });
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return out;
|
|
1015
|
+
}
|
|
815
1016
|
|
|
816
1017
|
// src/commands/find.ts
|
|
817
|
-
import * as
|
|
818
|
-
import
|
|
819
|
-
import
|
|
1018
|
+
import * as fs9 from "node:fs";
|
|
1019
|
+
import os5 from "node:os";
|
|
1020
|
+
import path9 from "node:path";
|
|
820
1021
|
|
|
821
1022
|
// src/config/loader.ts
|
|
822
|
-
import * as
|
|
823
|
-
import
|
|
824
|
-
import
|
|
1023
|
+
import * as fs8 from "node:fs";
|
|
1024
|
+
import os4 from "node:os";
|
|
1025
|
+
import path8 from "node:path";
|
|
825
1026
|
|
|
826
1027
|
// src/util/cron.ts
|
|
827
1028
|
var FIELD_RANGES = [
|
|
@@ -940,19 +1141,19 @@ function parseMemory(v) {
|
|
|
940
1141
|
}
|
|
941
1142
|
|
|
942
1143
|
// src/util/portable.ts
|
|
943
|
-
import * as
|
|
944
|
-
import
|
|
945
|
-
import
|
|
1144
|
+
import * as fs6 from "node:fs";
|
|
1145
|
+
import os3 from "node:os";
|
|
1146
|
+
import path6 from "node:path";
|
|
946
1147
|
function isDir(p) {
|
|
947
1148
|
try {
|
|
948
|
-
return
|
|
1149
|
+
return fs6.statSync(p).isDirectory();
|
|
949
1150
|
} catch {
|
|
950
1151
|
return false;
|
|
951
1152
|
}
|
|
952
1153
|
}
|
|
953
1154
|
function isFile(p) {
|
|
954
1155
|
try {
|
|
955
|
-
return
|
|
1156
|
+
return fs6.statSync(p).isFile();
|
|
956
1157
|
} catch {
|
|
957
1158
|
return false;
|
|
958
1159
|
}
|
|
@@ -974,18 +1175,18 @@ function searchByBasename(root, basename, maxDepth = 4) {
|
|
|
974
1175
|
if (cur.depth > maxDepth) continue;
|
|
975
1176
|
let entries;
|
|
976
1177
|
try {
|
|
977
|
-
entries =
|
|
1178
|
+
entries = fs6.readdirSync(cur.dir, { withFileTypes: true });
|
|
978
1179
|
} catch {
|
|
979
1180
|
continue;
|
|
980
1181
|
}
|
|
981
1182
|
for (const e of entries) {
|
|
982
|
-
const full =
|
|
1183
|
+
const full = path6.join(cur.dir, e.name);
|
|
983
1184
|
if (e.isFile() && e.name === basename) return full;
|
|
984
1185
|
if (e.isDirectory() && !e.isSymbolicLink()) {
|
|
985
1186
|
if (e.name === "node_modules" || e.name === ".git") continue;
|
|
986
1187
|
const real = (() => {
|
|
987
1188
|
try {
|
|
988
|
-
return
|
|
1189
|
+
return fs6.realpathSync(full);
|
|
989
1190
|
} catch {
|
|
990
1191
|
return full;
|
|
991
1192
|
}
|
|
@@ -1003,20 +1204,20 @@ function fixCwd(staleCwd, sourceDir) {
|
|
|
1003
1204
|
const candidates = [];
|
|
1004
1205
|
const tail = stripHomePrefix(staleCwd);
|
|
1005
1206
|
if (tail !== null) {
|
|
1006
|
-
candidates.push(
|
|
1007
|
-
candidates.push(
|
|
1008
|
-
candidates.push(
|
|
1009
|
-
if (isDir(sourceDir) && !staleCwd.startsWith(
|
|
1207
|
+
candidates.push(path6.join(os3.homedir(), tail));
|
|
1208
|
+
candidates.push(path6.join(path6.dirname(os3.homedir()), tail));
|
|
1209
|
+
candidates.push(path6.join(path6.dirname(sourceDir), tail));
|
|
1210
|
+
if (isDir(sourceDir) && !staleCwd.startsWith(os3.homedir())) candidates.push(sourceDir);
|
|
1010
1211
|
}
|
|
1011
|
-
const proj =
|
|
1212
|
+
const proj = path6.basename(sourceDir);
|
|
1012
1213
|
if (proj) {
|
|
1013
|
-
const marker = `${
|
|
1214
|
+
const marker = `${path6.sep}${proj}${path6.sep}`;
|
|
1014
1215
|
const altMarker = `/${proj}/`;
|
|
1015
1216
|
const idx = staleCwd.includes(marker) ? staleCwd.lastIndexOf(marker) : staleCwd.lastIndexOf(altMarker);
|
|
1016
1217
|
if (idx >= 0) {
|
|
1017
1218
|
const suffix = staleCwd.slice(idx + proj.length + 2);
|
|
1018
|
-
candidates.push(
|
|
1019
|
-
candidates.push(
|
|
1219
|
+
candidates.push(path6.join(sourceDir, suffix));
|
|
1220
|
+
candidates.push(path6.join(path6.dirname(sourceDir), proj, suffix));
|
|
1020
1221
|
}
|
|
1021
1222
|
}
|
|
1022
1223
|
for (const c of candidates) {
|
|
@@ -1031,33 +1232,33 @@ function fixCwd(staleCwd, sourceDir) {
|
|
|
1031
1232
|
}
|
|
1032
1233
|
function fixScript(staleScript, cwd, sourceDir) {
|
|
1033
1234
|
if (isFile(staleScript)) return { path: staleScript, fixed: false };
|
|
1034
|
-
const base =
|
|
1235
|
+
const base = path6.basename(staleScript);
|
|
1035
1236
|
const candidates = [];
|
|
1036
|
-
candidates.push(
|
|
1037
|
-
candidates.push(
|
|
1237
|
+
candidates.push(path6.join(cwd, base));
|
|
1238
|
+
candidates.push(path6.join(sourceDir, base));
|
|
1038
1239
|
const tail = stripHomePrefix(staleScript);
|
|
1039
1240
|
if (tail !== null) {
|
|
1040
|
-
candidates.push(
|
|
1041
|
-
candidates.push(
|
|
1042
|
-
candidates.push(
|
|
1241
|
+
candidates.push(path6.join(os3.homedir(), tail));
|
|
1242
|
+
candidates.push(path6.join(path6.dirname(os3.homedir()), tail));
|
|
1243
|
+
candidates.push(path6.join(sourceDir, tail));
|
|
1043
1244
|
const tailParts = tail.split("/");
|
|
1044
|
-
if (tailParts.length > 1) candidates.push(
|
|
1245
|
+
if (tailParts.length > 1) candidates.push(path6.join(sourceDir, tailParts.slice(1).join("/")));
|
|
1045
1246
|
}
|
|
1046
|
-
const proj =
|
|
1247
|
+
const proj = path6.basename(sourceDir);
|
|
1047
1248
|
if (proj) {
|
|
1048
1249
|
const marker = `/${proj}/`;
|
|
1049
1250
|
const idx = staleScript.lastIndexOf(marker);
|
|
1050
1251
|
if (idx >= 0) {
|
|
1051
1252
|
const suffix = staleScript.slice(idx + proj.length + 2);
|
|
1052
|
-
candidates.push(
|
|
1053
|
-
candidates.push(
|
|
1253
|
+
candidates.push(path6.join(sourceDir, suffix));
|
|
1254
|
+
candidates.push(path6.join(cwd, suffix));
|
|
1054
1255
|
}
|
|
1055
1256
|
}
|
|
1056
1257
|
const parts = staleScript.split(/[/\\]/).filter(Boolean);
|
|
1057
1258
|
if (parts.length >= 2) {
|
|
1058
|
-
const lastTwo = parts.slice(-2).join(
|
|
1059
|
-
candidates.push(
|
|
1060
|
-
candidates.push(
|
|
1259
|
+
const lastTwo = parts.slice(-2).join(path6.sep);
|
|
1260
|
+
candidates.push(path6.join(sourceDir, lastTwo));
|
|
1261
|
+
candidates.push(path6.join(cwd, lastTwo));
|
|
1061
1262
|
}
|
|
1062
1263
|
for (const c of candidates) {
|
|
1063
1264
|
if (c && isFile(c))
|
|
@@ -1137,9 +1338,17 @@ var LATER_KEYS = {
|
|
|
1137
1338
|
uid: "M6",
|
|
1138
1339
|
gid: "M6"
|
|
1139
1340
|
};
|
|
1140
|
-
var
|
|
1341
|
+
var RELIFE2_CONFIG_RE = /^(?:ecosystem|pm2|relife2)[\w.-]*\.config\.(?:c?js|mjs|json|ts)$/i;
|
|
1342
|
+
var ANY_CONFIG_RE = /^[\w.-]*\.config\.(?:c?js|mjs|json|ts)$/i;
|
|
1141
1343
|
function looksLikeConfigFile(file) {
|
|
1142
|
-
|
|
1344
|
+
const base = path8.basename(file);
|
|
1345
|
+
if (base.includes(".tmp.")) return false;
|
|
1346
|
+
return ANY_CONFIG_RE.test(base);
|
|
1347
|
+
}
|
|
1348
|
+
function isRelife2ConfigFile(file) {
|
|
1349
|
+
const base = path8.basename(file);
|
|
1350
|
+
if (base.includes(".tmp.")) return false;
|
|
1351
|
+
return RELIFE2_CONFIG_RE.test(base);
|
|
1143
1352
|
}
|
|
1144
1353
|
function findDefaultConfig(cwd) {
|
|
1145
1354
|
const candidates = [
|
|
@@ -1156,16 +1365,16 @@ function findDefaultConfig(cwd) {
|
|
|
1156
1365
|
"pm2.config.cjs"
|
|
1157
1366
|
];
|
|
1158
1367
|
for (const name of candidates) {
|
|
1159
|
-
const full =
|
|
1160
|
-
if (
|
|
1368
|
+
const full = path8.join(cwd, name);
|
|
1369
|
+
if (fs8.existsSync(full)) return full;
|
|
1161
1370
|
}
|
|
1162
1371
|
return null;
|
|
1163
1372
|
}
|
|
1164
1373
|
async function loadConfigFile(filePath, opts) {
|
|
1165
|
-
const ext =
|
|
1374
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
1166
1375
|
let exported;
|
|
1167
1376
|
if (ext === ".json") {
|
|
1168
|
-
exported = JSON.parse(
|
|
1377
|
+
exported = JSON.parse(fs8.readFileSync(filePath, "utf8"));
|
|
1169
1378
|
} else if (ext === ".ts") {
|
|
1170
1379
|
exported = await loadTsFile(filePath);
|
|
1171
1380
|
} else {
|
|
@@ -1175,7 +1384,7 @@ async function loadConfigFile(filePath, opts) {
|
|
|
1175
1384
|
}
|
|
1176
1385
|
if (exported instanceof Promise) exported = await exported;
|
|
1177
1386
|
}
|
|
1178
|
-
return normalizeExported(exported,
|
|
1387
|
+
return normalizeExported(exported, path8.dirname(filePath), opts);
|
|
1179
1388
|
}
|
|
1180
1389
|
async function loadTsFile(file) {
|
|
1181
1390
|
try {
|
|
@@ -1184,16 +1393,19 @@ async function loadTsFile(file) {
|
|
|
1184
1393
|
const mod = await import(url);
|
|
1185
1394
|
return mod.default ?? mod;
|
|
1186
1395
|
} catch {
|
|
1187
|
-
const raw =
|
|
1396
|
+
const raw = fs8.readFileSync(file, "utf8");
|
|
1188
1397
|
const stripped = raw.replace(/^\s*import\s+type\s+.*$/gm, "").replace(/:\s*[\w<>[\]|&\s,?]+(?=[=;,\n)}])/g, "").replace(/\s+as\s+const/g, "");
|
|
1189
|
-
const tmp =
|
|
1398
|
+
const tmp = path8.join(
|
|
1399
|
+
os4.tmpdir(),
|
|
1400
|
+
`relife2-${path8.basename(file).replace(/[^a-zA-Z0-9._-]/g, "_")}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.tmp.cjs`
|
|
1401
|
+
);
|
|
1190
1402
|
try {
|
|
1191
|
-
|
|
1403
|
+
fs8.writeFileSync(tmp, stripped, "utf8");
|
|
1192
1404
|
const { loadJsFile: lf } = await Promise.resolve().then(() => (init_jsloader(), jsloader_exports));
|
|
1193
1405
|
return await lf(tmp);
|
|
1194
1406
|
} finally {
|
|
1195
1407
|
try {
|
|
1196
|
-
|
|
1408
|
+
fs8.unlinkSync(tmp);
|
|
1197
1409
|
} catch {
|
|
1198
1410
|
}
|
|
1199
1411
|
}
|
|
@@ -1249,20 +1461,20 @@ function normalizeApp(raw, sourceDir, warnings, index, envName) {
|
|
|
1249
1461
|
const instances = parseInstances(input.instances, index);
|
|
1250
1462
|
const execMode = parseExecMode(input.exec_mode, index);
|
|
1251
1463
|
const rawCwd = typeof input.cwd === "string" ? input.cwd : void 0;
|
|
1252
|
-
const cwdRaw =
|
|
1464
|
+
const cwdRaw = path8.resolve(sourceDir, rawCwd ?? ".");
|
|
1253
1465
|
const cwdFix = fixCwd(cwdRaw, sourceDir);
|
|
1254
1466
|
const cwd = cwdFix.path;
|
|
1255
1467
|
if (cwdFix.fixed && cwdFix.reason) warnings.push(cwdFix.reason);
|
|
1256
1468
|
const hasSep = scriptRaw.includes("/") || scriptRaw.includes("\\");
|
|
1257
1469
|
const isJsExt = /\.(?:c?js|mjs|ts|mts|cts)$/i.test(scriptRaw);
|
|
1258
|
-
const scriptRawResolved = hasSep || isJsExt ?
|
|
1470
|
+
const scriptRawResolved = hasSep || isJsExt ? path8.resolve(cwd, scriptRaw) : scriptRaw;
|
|
1259
1471
|
let script = scriptRawResolved;
|
|
1260
|
-
if (
|
|
1472
|
+
if (path8.isAbsolute(scriptRawResolved)) {
|
|
1261
1473
|
const f = fixScript(scriptRawResolved, cwd, sourceDir);
|
|
1262
1474
|
if (f.fixed) {
|
|
1263
1475
|
warnings.push(f.reason ?? `script '${scriptRawResolved}' \u2192 '${f.path}'`);
|
|
1264
1476
|
script = f.path;
|
|
1265
|
-
} else if (!
|
|
1477
|
+
} else if (!fs8.existsSync(scriptRawResolved) && f.reason) {
|
|
1266
1478
|
warnings.push(f.reason);
|
|
1267
1479
|
}
|
|
1268
1480
|
}
|
|
@@ -1279,11 +1491,11 @@ function normalizeApp(raw, sourceDir, warnings, index, envName) {
|
|
|
1279
1491
|
}
|
|
1280
1492
|
const envFile = typeof input.env_file === "string" && input.env_file !== "" ? input.env_file : void 0;
|
|
1281
1493
|
if (envFile !== void 0) {
|
|
1282
|
-
const envPath =
|
|
1494
|
+
const envPath = path8.resolve(cwd, envFile);
|
|
1283
1495
|
let parsed = null;
|
|
1284
|
-
if (
|
|
1496
|
+
if (fs8.existsSync(envPath) && fs8.statSync(envPath).isFile()) {
|
|
1285
1497
|
try {
|
|
1286
|
-
parsed = parseDotenv(
|
|
1498
|
+
parsed = parseDotenv(fs8.readFileSync(envPath, "utf8"));
|
|
1287
1499
|
} catch (err) {
|
|
1288
1500
|
warnings.push(
|
|
1289
1501
|
`apps[${index}] env_file '${envFile}' could not be read: ${err.message}`
|
|
@@ -1406,7 +1618,7 @@ function parseWatchField(v, field, index) {
|
|
|
1406
1618
|
}
|
|
1407
1619
|
function parseInstances(v, index) {
|
|
1408
1620
|
if (v === void 0 || v === null) return 1;
|
|
1409
|
-
if (v === "max" || v === "MAX") return
|
|
1621
|
+
if (v === "max" || v === "MAX") return os4.cpus().length || 1;
|
|
1410
1622
|
const n = Number(v);
|
|
1411
1623
|
if (!Number.isFinite(n) || n < 1) {
|
|
1412
1624
|
throw new ConfigError(`apps[${index}] 'instances' must be a positive number or 'max'`);
|
|
@@ -1421,7 +1633,7 @@ function parseExecMode(v, index) {
|
|
|
1421
1633
|
throw new ConfigError(`apps[${index}] 'exec_mode' must be 'fork' or 'cluster'`);
|
|
1422
1634
|
}
|
|
1423
1635
|
function defaultName(script) {
|
|
1424
|
-
const base =
|
|
1636
|
+
const base = path8.basename(script);
|
|
1425
1637
|
return base.replace(/\.(?:c?js|mjs|ts|mts|cts)$/i, "") || base;
|
|
1426
1638
|
}
|
|
1427
1639
|
function normalizeSingleApp(input, sourceDir, envName) {
|
|
@@ -1469,7 +1681,7 @@ function findConfigs(root, maxDepth) {
|
|
|
1469
1681
|
if (depth > maxDepth) continue;
|
|
1470
1682
|
let real;
|
|
1471
1683
|
try {
|
|
1472
|
-
real =
|
|
1684
|
+
real = fs9.realpathSync(dir);
|
|
1473
1685
|
} catch {
|
|
1474
1686
|
continue;
|
|
1475
1687
|
}
|
|
@@ -1477,13 +1689,13 @@ function findConfigs(root, maxDepth) {
|
|
|
1477
1689
|
visited.add(real);
|
|
1478
1690
|
let entries;
|
|
1479
1691
|
try {
|
|
1480
|
-
entries =
|
|
1692
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
1481
1693
|
} catch {
|
|
1482
1694
|
continue;
|
|
1483
1695
|
}
|
|
1484
1696
|
for (const ent of entries) {
|
|
1485
|
-
const full =
|
|
1486
|
-
if (ent.isFile() &&
|
|
1697
|
+
const full = path9.join(dir, ent.name);
|
|
1698
|
+
if (ent.isFile() && isRelife2ConfigFile(ent.name)) {
|
|
1487
1699
|
out.push(full);
|
|
1488
1700
|
continue;
|
|
1489
1701
|
}
|
|
@@ -1512,8 +1724,8 @@ async function enrich(found) {
|
|
|
1512
1724
|
return entries;
|
|
1513
1725
|
}
|
|
1514
1726
|
function relToRoot(p, root) {
|
|
1515
|
-
const rel =
|
|
1516
|
-
return rel === "" ?
|
|
1727
|
+
const rel = path9.relative(root, p);
|
|
1728
|
+
return rel === "" ? path9.basename(p) : rel;
|
|
1517
1729
|
}
|
|
1518
1730
|
async function run5(args) {
|
|
1519
1731
|
const { positionals, opts } = parseCliArgs(args, VALUE_OPTS);
|
|
@@ -1523,12 +1735,12 @@ async function run5(args) {
|
|
|
1523
1735
|
let root = opts.get("root");
|
|
1524
1736
|
if (root === void 0) {
|
|
1525
1737
|
if (allFlag) {
|
|
1526
|
-
root = process.platform === "win32" ?
|
|
1738
|
+
root = process.platform === "win32" ? path9.parse(process.cwd()).root : "/";
|
|
1527
1739
|
} else {
|
|
1528
|
-
root =
|
|
1740
|
+
root = os5.homedir() || process.cwd();
|
|
1529
1741
|
}
|
|
1530
1742
|
}
|
|
1531
|
-
root =
|
|
1743
|
+
root = path9.resolve(root);
|
|
1532
1744
|
let depth = 6;
|
|
1533
1745
|
const depthRaw = opts.get("depth");
|
|
1534
1746
|
if (depthRaw !== void 0) {
|
|
@@ -1540,7 +1752,7 @@ async function run5(args) {
|
|
|
1540
1752
|
depth = Math.round(n);
|
|
1541
1753
|
}
|
|
1542
1754
|
if (allFlag) depth = Math.max(depth, 8);
|
|
1543
|
-
if (!
|
|
1755
|
+
if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) {
|
|
1544
1756
|
console.error(`find: root '${root}' does not exist or is not a directory`);
|
|
1545
1757
|
return 1;
|
|
1546
1758
|
}
|
|
@@ -1631,10 +1843,10 @@ Starting ${toStart.length} config(s) not yet running\u2026
|
|
|
1631
1843
|
const res = await client.call("start", {
|
|
1632
1844
|
target: { type: "config", path: r.path }
|
|
1633
1845
|
});
|
|
1634
|
-
for (const w of res.warnings ?? []) console.error(`warning ${
|
|
1846
|
+
for (const w of res.warnings ?? []) console.error(`warning ${path9.basename(r.path)}: ${w}`);
|
|
1635
1847
|
for (const a of res.apps) {
|
|
1636
1848
|
if (a.status === "errored") {
|
|
1637
|
-
console.error(`x ${a.name} (${
|
|
1849
|
+
console.error(`x ${a.name} (${path9.basename(r.path)}): ${a.message ?? "failed"}`);
|
|
1638
1850
|
failed++;
|
|
1639
1851
|
} else {
|
|
1640
1852
|
console.log(
|
|
@@ -1644,7 +1856,7 @@ Starting ${toStart.length} config(s) not yet running\u2026
|
|
|
1644
1856
|
}
|
|
1645
1857
|
}
|
|
1646
1858
|
} catch (err) {
|
|
1647
|
-
console.error(`x ${
|
|
1859
|
+
console.error(`x ${path9.basename(r.path)}: ${err.message}`);
|
|
1648
1860
|
failed++;
|
|
1649
1861
|
}
|
|
1650
1862
|
}
|
|
@@ -1678,9 +1890,9 @@ async function run6(args) {
|
|
|
1678
1890
|
}
|
|
1679
1891
|
|
|
1680
1892
|
// src/commands/fromCaddy.ts
|
|
1681
|
-
import * as
|
|
1682
|
-
import
|
|
1683
|
-
import
|
|
1893
|
+
import * as fs10 from "node:fs";
|
|
1894
|
+
import os6 from "node:os";
|
|
1895
|
+
import path10 from "node:path";
|
|
1684
1896
|
var LOOPBACK_RE = /(?:127\.0\.0\.1|localhost|::1):(\d{2,5})/g;
|
|
1685
1897
|
var ENTRY_PATTERNS = [
|
|
1686
1898
|
".output/server/index.mjs",
|
|
@@ -1853,37 +2065,37 @@ function parseCaddyfile(content) {
|
|
|
1853
2065
|
}
|
|
1854
2066
|
function findEntry(dir) {
|
|
1855
2067
|
for (const pat of ENTRY_PATTERNS) {
|
|
1856
|
-
const p =
|
|
1857
|
-
if (
|
|
2068
|
+
const p = path10.join(dir, pat);
|
|
2069
|
+
if (fs10.existsSync(p)) return { script: pat, exists: true };
|
|
1858
2070
|
}
|
|
1859
2071
|
return null;
|
|
1860
2072
|
}
|
|
1861
2073
|
function guessDir(site, _port, hint, caddyfileDir) {
|
|
1862
2074
|
const warnings = [];
|
|
1863
2075
|
if (hint?.dir) {
|
|
1864
|
-
const d =
|
|
2076
|
+
const d = path10.isAbsolute(hint.dir) ? hint.dir : path10.resolve(caddyfileDir, hint.dir);
|
|
1865
2077
|
return { dir: d, warnings };
|
|
1866
2078
|
}
|
|
1867
2079
|
const searchRoots = [];
|
|
1868
|
-
const homedir =
|
|
2080
|
+
const homedir = os6.homedir();
|
|
1869
2081
|
if (homedir) searchRoots.push(homedir);
|
|
1870
2082
|
if (caddyfileDir) searchRoots.push(caddyfileDir);
|
|
1871
|
-
if (caddyfileDir) searchRoots.push(
|
|
2083
|
+
if (caddyfileDir) searchRoots.push(path10.resolve(caddyfileDir, ".."));
|
|
1872
2084
|
const uniqRoots = [...new Set(searchRoots)].filter(Boolean);
|
|
1873
2085
|
const siteName = sanitizeName(site);
|
|
1874
2086
|
const candidates = [];
|
|
1875
|
-
if (homedir) candidates.push(
|
|
1876
|
-
if (homedir) candidates.push(
|
|
1877
|
-
for (const r of uniqRoots) candidates.push(
|
|
2087
|
+
if (homedir) candidates.push(path10.join(homedir, siteName));
|
|
2088
|
+
if (homedir) candidates.push(path10.join(homedir, siteName.split("-")[0]));
|
|
2089
|
+
for (const r of uniqRoots) candidates.push(path10.join(r, siteName));
|
|
1878
2090
|
const rawDir = site.split(".")[0].replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1879
|
-
if (rawDir && homedir) candidates.push(
|
|
2091
|
+
if (rawDir && homedir) candidates.push(path10.join(homedir, rawDir));
|
|
1880
2092
|
for (const cand of candidates) {
|
|
1881
|
-
if (
|
|
2093
|
+
if (fs10.existsSync(cand) && fs10.statSync(cand).isDirectory()) {
|
|
1882
2094
|
const e = findEntry(cand);
|
|
1883
2095
|
if (e) return { dir: cand, warnings };
|
|
1884
2096
|
}
|
|
1885
2097
|
}
|
|
1886
|
-
const fallback = homedir ?
|
|
2098
|
+
const fallback = homedir ? path10.join(homedir, siteName) : path10.join(caddyfileDir || process.cwd(), siteName);
|
|
1887
2099
|
warnings.push(
|
|
1888
2100
|
`no existing dir found for ${site} \u2192 using fallback ${fallback} (create or set via '# relife2: dir=...')`
|
|
1889
2101
|
);
|
|
@@ -1915,15 +2127,15 @@ async function run7(args) {
|
|
|
1915
2127
|
caddyfilePath ? null : "./Caddyfile",
|
|
1916
2128
|
caddyfilePath ? null : "Caddyfile",
|
|
1917
2129
|
caddyfilePath ? null : "/etc/caddy/Caddyfile",
|
|
1918
|
-
caddyfilePath ? null :
|
|
2130
|
+
caddyfilePath ? null : path10.join(os6.homedir(), "Caddyfile")
|
|
1919
2131
|
].filter(Boolean);
|
|
1920
2132
|
let resolvedCaddy = null;
|
|
1921
2133
|
let content = null;
|
|
1922
2134
|
for (const cand of candidates) {
|
|
1923
|
-
const p =
|
|
1924
|
-
if (
|
|
2135
|
+
const p = path10.resolve(cand);
|
|
2136
|
+
if (fs10.existsSync(p)) {
|
|
1925
2137
|
resolvedCaddy = p;
|
|
1926
|
-
content =
|
|
2138
|
+
content = fs10.readFileSync(p, "utf8");
|
|
1927
2139
|
break;
|
|
1928
2140
|
}
|
|
1929
2141
|
}
|
|
@@ -1934,7 +2146,7 @@ async function run7(args) {
|
|
|
1934
2146
|
);
|
|
1935
2147
|
return 1;
|
|
1936
2148
|
}
|
|
1937
|
-
const caddyfileDir =
|
|
2149
|
+
const caddyfileDir = path10.dirname(resolvedCaddy);
|
|
1938
2150
|
const { sites } = parseCaddyfile(content);
|
|
1939
2151
|
if (sites.length === 0) {
|
|
1940
2152
|
console.error(`from-caddy: no sites found in ${resolvedCaddy}`);
|
|
@@ -1982,7 +2194,7 @@ async function run7(args) {
|
|
|
1982
2194
|
let relScript = "";
|
|
1983
2195
|
if (entry) {
|
|
1984
2196
|
relScript = entry;
|
|
1985
|
-
exists =
|
|
2197
|
+
exists = fs10.existsSync(path10.isAbsolute(entry) ? entry : path10.join(dir, entry));
|
|
1986
2198
|
if (!exists) warnings.push(`hint script '${entry}' for ${name} not found under ${dir}`);
|
|
1987
2199
|
} else {
|
|
1988
2200
|
const found = findEntry(dir);
|
|
@@ -2000,7 +2212,7 @@ async function run7(args) {
|
|
|
2000
2212
|
let interpreter = site.hint?.interpreter;
|
|
2001
2213
|
if (!interpreter) {
|
|
2002
2214
|
if (relScript.endsWith(".mjs") || relScript.endsWith(".ts")) {
|
|
2003
|
-
const hasBun2 =
|
|
2215
|
+
const hasBun2 = fs10.existsSync(path10.join(dir, "bun.lockb")) || fs10.existsSync(path10.join(dir, "bun.lock"));
|
|
2004
2216
|
if (hasBun2) interpreter = "bun";
|
|
2005
2217
|
}
|
|
2006
2218
|
}
|
|
@@ -2081,7 +2293,7 @@ Would write ${planned.length} app(s) to ${outPath ?? "relife2.config.cjs (dry-ru
|
|
|
2081
2293
|
console.log(`Run without --dry-run to generate.`);
|
|
2082
2294
|
return 0;
|
|
2083
2295
|
}
|
|
2084
|
-
const resolvedOut =
|
|
2296
|
+
const resolvedOut = path10.resolve(outPath ?? "relife2.config.cjs");
|
|
2085
2297
|
const appsCode = planned.map((p) => {
|
|
2086
2298
|
const interp = p._interpreter;
|
|
2087
2299
|
const isCaddy = p.name === "caddy";
|
|
@@ -2138,7 +2350,7 @@ ${appsCode}
|
|
|
2138
2350
|
]
|
|
2139
2351
|
};
|
|
2140
2352
|
`;
|
|
2141
|
-
|
|
2353
|
+
fs10.writeFileSync(resolvedOut, fileContent, "utf8");
|
|
2142
2354
|
console.log(
|
|
2143
2355
|
`from-caddy: parsed ${sites.length} site(s), ${planned.length} upstream(s) from ${resolvedCaddy}`
|
|
2144
2356
|
);
|
|
@@ -2156,7 +2368,7 @@ Warnings:`);
|
|
|
2156
2368
|
console.log(`
|
|
2157
2369
|
Wrote ${resolvedOut} (${planned.length} app(s))`);
|
|
2158
2370
|
console.log(
|
|
2159
|
-
`Next: relife2 inspect-config ${
|
|
2371
|
+
`Next: relife2 inspect-config ${path10.basename(resolvedOut)} \u2192 relife2 start ${path10.basename(resolvedOut)}`
|
|
2160
2372
|
);
|
|
2161
2373
|
return 0;
|
|
2162
2374
|
}
|
|
@@ -2185,7 +2397,7 @@ function printUsage2() {
|
|
|
2185
2397
|
}
|
|
2186
2398
|
|
|
2187
2399
|
// src/version.ts
|
|
2188
|
-
var VERSION = "1.0.
|
|
2400
|
+
var VERSION = "1.0.2";
|
|
2189
2401
|
|
|
2190
2402
|
// src/commands/help.ts
|
|
2191
2403
|
var HELP_TEXT = `relife2 ${VERSION} \u2014 process manager for Node.js & Bun apps (PM2 alternative, done right)
|
|
@@ -2211,7 +2423,7 @@ Commands:
|
|
|
2211
2423
|
flush [name|all] Empty the app log file(s)
|
|
2212
2424
|
rotate [--max-size <bytes>] [--retain <n>] [name|all]
|
|
2213
2425
|
Rotate log files exceeding the size threshold (M3)
|
|
2214
|
-
doctor
|
|
2426
|
+
doctor [--fix] Run integrity checks on the running daemon (M3) \u2014 --fix heals stale lock + *.tmp.cjs
|
|
2215
2427
|
metrics Show daemon self-metrics + per-app summary (M3)
|
|
2216
2428
|
monit [--once] Live dashboard (M6): cpu/mem/uptime per app, q to quit
|
|
2217
2429
|
find [start] [--root <dir>] [--depth <n>] [--json] [--all]
|
|
@@ -2243,13 +2455,13 @@ function printHelp() {
|
|
|
2243
2455
|
}
|
|
2244
2456
|
|
|
2245
2457
|
// src/commands/importpm2.ts
|
|
2246
|
-
import * as
|
|
2247
|
-
import
|
|
2248
|
-
import
|
|
2458
|
+
import * as fs11 from "node:fs";
|
|
2459
|
+
import os7 from "node:os";
|
|
2460
|
+
import path11 from "node:path";
|
|
2249
2461
|
async function run8(args) {
|
|
2250
2462
|
const target = args[0];
|
|
2251
|
-
const dumpPath = target !== void 0 ?
|
|
2252
|
-
if (!
|
|
2463
|
+
const dumpPath = target !== void 0 ? path11.resolve(target) : path11.join(os7.homedir(), ".pm2", "dump.pm2");
|
|
2464
|
+
if (!fs11.existsSync(dumpPath)) {
|
|
2253
2465
|
console.error(`import-pm2: dump file not found: ${dumpPath}`);
|
|
2254
2466
|
console.error(
|
|
2255
2467
|
" pm2 saves to ~/.pm2/dump.pm2 via `pm2 save` \u2014 run that first, or pass a path:"
|
|
@@ -2259,7 +2471,7 @@ async function run8(args) {
|
|
|
2259
2471
|
}
|
|
2260
2472
|
let raw;
|
|
2261
2473
|
try {
|
|
2262
|
-
raw = JSON.parse(
|
|
2474
|
+
raw = JSON.parse(fs11.readFileSync(dumpPath, "utf8"));
|
|
2263
2475
|
} catch (err) {
|
|
2264
2476
|
console.error(`import-pm2: cannot parse ${dumpPath}: ${err.message}`);
|
|
2265
2477
|
return 1;
|
|
@@ -2302,11 +2514,11 @@ async function run8(args) {
|
|
|
2302
2514
|
}
|
|
2303
2515
|
]
|
|
2304
2516
|
};
|
|
2305
|
-
const tmpPath =
|
|
2306
|
-
|
|
2517
|
+
const tmpPath = path11.join(
|
|
2518
|
+
os7.tmpdir(),
|
|
2307
2519
|
`relife2-import-${Date.now()}-${Math.random().toString(36).slice(2)}.cjs`
|
|
2308
2520
|
);
|
|
2309
|
-
|
|
2521
|
+
fs11.writeFileSync(tmpPath, `module.exports = ${JSON.stringify(tmpConfig)}`, "utf8");
|
|
2310
2522
|
try {
|
|
2311
2523
|
const res = await client.call("start", {
|
|
2312
2524
|
target: { type: "config", path: tmpPath }
|
|
@@ -2322,7 +2534,7 @@ async function run8(args) {
|
|
|
2322
2534
|
console.error(`import ${name}: ${err.message}`);
|
|
2323
2535
|
} finally {
|
|
2324
2536
|
try {
|
|
2325
|
-
|
|
2537
|
+
fs11.unlinkSync(tmpPath);
|
|
2326
2538
|
} catch {
|
|
2327
2539
|
}
|
|
2328
2540
|
}
|
|
@@ -2339,7 +2551,7 @@ imported ${imported} app(s) from ${dumpPath}`);
|
|
|
2339
2551
|
}
|
|
2340
2552
|
|
|
2341
2553
|
// src/commands/inspectconfig.ts
|
|
2342
|
-
import
|
|
2554
|
+
import path12 from "node:path";
|
|
2343
2555
|
async function run9(args) {
|
|
2344
2556
|
const { positionals } = parseCliArgs(args);
|
|
2345
2557
|
const file = positionals[0];
|
|
@@ -2349,7 +2561,7 @@ async function run9(args) {
|
|
|
2349
2561
|
}
|
|
2350
2562
|
let loaded;
|
|
2351
2563
|
try {
|
|
2352
|
-
loaded = await loadConfigFile(
|
|
2564
|
+
loaded = await loadConfigFile(path12.resolve(file));
|
|
2353
2565
|
} catch (err) {
|
|
2354
2566
|
console.error(`relife2: ${err.message}`);
|
|
2355
2567
|
return 1;
|
|
@@ -2418,7 +2630,7 @@ function printTable(rows) {
|
|
|
2418
2630
|
}
|
|
2419
2631
|
|
|
2420
2632
|
// src/commands/logs.ts
|
|
2421
|
-
import * as
|
|
2633
|
+
import * as fs12 from "node:fs";
|
|
2422
2634
|
var VALUE_OPTS2 = /* @__PURE__ */ new Set(["lines"]);
|
|
2423
2635
|
async function run12(args) {
|
|
2424
2636
|
const { positionals, opts } = parseCliArgs(args, VALUE_OPTS2);
|
|
@@ -2458,7 +2670,7 @@ function parseLines(v) {
|
|
|
2458
2670
|
}
|
|
2459
2671
|
function printTail(file, lines) {
|
|
2460
2672
|
try {
|
|
2461
|
-
const text =
|
|
2673
|
+
const text = fs12.readFileSync(file, "utf8");
|
|
2462
2674
|
const all = text.split(/\r?\n/);
|
|
2463
2675
|
if (all.length > 0 && all[all.length - 1] === "") all.pop();
|
|
2464
2676
|
const slice = all.slice(Math.max(0, all.length - lines));
|
|
@@ -2484,13 +2696,13 @@ async function followFiles(files) {
|
|
|
2484
2696
|
const from = positions.get(f) ?? 0;
|
|
2485
2697
|
if (size > from) {
|
|
2486
2698
|
try {
|
|
2487
|
-
const fd =
|
|
2699
|
+
const fd = fs12.openSync(f, "r");
|
|
2488
2700
|
try {
|
|
2489
2701
|
const buf = Buffer.alloc(size - from);
|
|
2490
|
-
|
|
2702
|
+
fs12.readSync(fd, buf, 0, buf.length, from);
|
|
2491
2703
|
process.stdout.write(buf);
|
|
2492
2704
|
} finally {
|
|
2493
|
-
|
|
2705
|
+
fs12.closeSync(fd);
|
|
2494
2706
|
}
|
|
2495
2707
|
} catch {
|
|
2496
2708
|
}
|
|
@@ -2501,7 +2713,7 @@ async function followFiles(files) {
|
|
|
2501
2713
|
}
|
|
2502
2714
|
function fileSize(file) {
|
|
2503
2715
|
try {
|
|
2504
|
-
return
|
|
2716
|
+
return fs12.statSync(file).size;
|
|
2505
2717
|
} catch {
|
|
2506
2718
|
return 0;
|
|
2507
2719
|
}
|
|
@@ -2791,7 +3003,7 @@ async function run20(_args) {
|
|
|
2791
3003
|
}
|
|
2792
3004
|
|
|
2793
3005
|
// src/commands/start.ts
|
|
2794
|
-
import
|
|
3006
|
+
import path13 from "node:path";
|
|
2795
3007
|
var VALUE_OPTS4 = /* @__PURE__ */ new Set([
|
|
2796
3008
|
"name",
|
|
2797
3009
|
"env",
|
|
@@ -2840,7 +3052,7 @@ async function run21(args) {
|
|
|
2840
3052
|
const res = await client.call("start", {
|
|
2841
3053
|
target: {
|
|
2842
3054
|
type: isConfig ? "config" : "script",
|
|
2843
|
-
path:
|
|
3055
|
+
path: path13.resolve(target),
|
|
2844
3056
|
name,
|
|
2845
3057
|
args: passthrough.length > 0 ? passthrough : void 0,
|
|
2846
3058
|
envName: envName ?? void 0,
|
|
@@ -2867,11 +3079,11 @@ async function run21(args) {
|
|
|
2867
3079
|
|
|
2868
3080
|
// src/commands/startup.ts
|
|
2869
3081
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
2870
|
-
import * as
|
|
2871
|
-
import
|
|
2872
|
-
import
|
|
3082
|
+
import * as fs13 from "node:fs";
|
|
3083
|
+
import os8 from "node:os";
|
|
3084
|
+
import path14 from "node:path";
|
|
2873
3085
|
import { fileURLToPath } from "node:url";
|
|
2874
|
-
var SYSTEMD_USER_DIR =
|
|
3086
|
+
var SYSTEMD_USER_DIR = path14.join(os8.homedir(), ".config", "systemd", "user");
|
|
2875
3087
|
var UNIT_NAME = "relife2.service";
|
|
2876
3088
|
function getRelife2Bin() {
|
|
2877
3089
|
const which = spawnSync2(process.platform === "win32" ? "where" : "which", ["relife2"], {
|
|
@@ -2889,13 +3101,13 @@ function getRelife2Bin() {
|
|
|
2889
3101
|
}
|
|
2890
3102
|
try {
|
|
2891
3103
|
const thisFile = fileURLToPath(import.meta.url);
|
|
2892
|
-
const cliPath =
|
|
2893
|
-
if (
|
|
3104
|
+
const cliPath = path14.resolve(path14.dirname(thisFile), "..", "cli.js");
|
|
3105
|
+
if (fs13.existsSync(cliPath)) {
|
|
2894
3106
|
return `node ${cliPath}`;
|
|
2895
3107
|
}
|
|
2896
3108
|
} catch {
|
|
2897
3109
|
}
|
|
2898
|
-
return `node ${
|
|
3110
|
+
return `node ${path14.resolve(arg1 ?? process.cwd())}`;
|
|
2899
3111
|
}
|
|
2900
3112
|
function generateUnit(bin) {
|
|
2901
3113
|
const envHint = process.env.RELIFE2_DIR !== void 0 ? `
|
|
@@ -2916,6 +3128,49 @@ Environment=RELIFE2_DAEMON=1${envHint}
|
|
|
2916
3128
|
WantedBy=default.target
|
|
2917
3129
|
`;
|
|
2918
3130
|
}
|
|
3131
|
+
function isLingerEnabled(user) {
|
|
3132
|
+
try {
|
|
3133
|
+
const out = execSync(`loginctl show-user ${user} -p Linger --value`, {
|
|
3134
|
+
encoding: "utf8",
|
|
3135
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3136
|
+
timeout: 3e3
|
|
3137
|
+
});
|
|
3138
|
+
const v = out.trim().toLowerCase();
|
|
3139
|
+
if (v === "yes") return true;
|
|
3140
|
+
if (v === "no") return false;
|
|
3141
|
+
} catch {
|
|
3142
|
+
}
|
|
3143
|
+
try {
|
|
3144
|
+
const out = execSync(`loginctl show-user ${user}`, {
|
|
3145
|
+
encoding: "utf8",
|
|
3146
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3147
|
+
timeout: 3e3
|
|
3148
|
+
});
|
|
3149
|
+
if (/Linger=yes/i.test(out)) return true;
|
|
3150
|
+
if (/Linger=no/i.test(out)) return false;
|
|
3151
|
+
} catch {
|
|
3152
|
+
}
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
function tryEnableLinger(user) {
|
|
3156
|
+
const attempts = [
|
|
3157
|
+
{ cmd: `loginctl enable-linger ${user}`, label: "loginctl" },
|
|
3158
|
+
{ cmd: `sudo -n loginctl enable-linger ${user}`, label: "sudo -n" },
|
|
3159
|
+
{ cmd: `sudo loginctl enable-linger ${user}`, label: "sudo" }
|
|
3160
|
+
];
|
|
3161
|
+
for (const a of attempts) {
|
|
3162
|
+
try {
|
|
3163
|
+
execSync(a.cmd, { stdio: ["ignore", "pipe", "pipe"], timeout: 8e3 });
|
|
3164
|
+
const enabled = isLingerEnabled(user);
|
|
3165
|
+
if (enabled === true || enabled === null) {
|
|
3166
|
+
console.log(`enabled linger for user ${user} via ${a.label}`);
|
|
3167
|
+
return true;
|
|
3168
|
+
}
|
|
3169
|
+
} catch {
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
return false;
|
|
3173
|
+
}
|
|
2919
3174
|
async function run22(_args) {
|
|
2920
3175
|
if (process.platform === "win32") {
|
|
2921
3176
|
console.error(
|
|
@@ -2929,47 +3184,67 @@ async function run22(_args) {
|
|
|
2929
3184
|
}
|
|
2930
3185
|
const bin = getRelife2Bin();
|
|
2931
3186
|
const unitContent = generateUnit(bin);
|
|
2932
|
-
const unitPath =
|
|
3187
|
+
const unitPath = path14.join(SYSTEMD_USER_DIR, UNIT_NAME);
|
|
2933
3188
|
try {
|
|
2934
|
-
|
|
3189
|
+
fs13.mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
|
|
2935
3190
|
} catch (err) {
|
|
2936
3191
|
console.error(`cannot create ${SYSTEMD_USER_DIR}: ${err.message}`);
|
|
2937
3192
|
return 1;
|
|
2938
3193
|
}
|
|
2939
3194
|
try {
|
|
2940
|
-
|
|
3195
|
+
fs13.writeFileSync(unitPath, unitContent);
|
|
2941
3196
|
console.log(`written systemd user unit: ${unitPath}`);
|
|
2942
3197
|
} catch (err) {
|
|
2943
3198
|
console.error(`cannot write ${unitPath}: ${err.message}`);
|
|
2944
3199
|
return 1;
|
|
2945
3200
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
console.log(`enabled
|
|
2950
|
-
}
|
|
2951
|
-
console.
|
|
2952
|
-
|
|
2953
|
-
)
|
|
2954
|
-
|
|
2955
|
-
|
|
3201
|
+
const user = process.env.USER ?? process.env.LOGNAME ?? os8.userInfo().username ?? "opc";
|
|
3202
|
+
const lingerState = isLingerEnabled(user);
|
|
3203
|
+
if (lingerState === true) {
|
|
3204
|
+
console.log(`linger already enabled for user ${user}`);
|
|
3205
|
+
} else if (lingerState === false) {
|
|
3206
|
+
console.log(`linger is disabled for ${user} \u2014 enabling\u2026`);
|
|
3207
|
+
const ok = tryEnableLinger(user);
|
|
3208
|
+
if (!ok) {
|
|
3209
|
+
console.error(`warning: could not enable linger (is systemd-logind running?)`);
|
|
3210
|
+
console.error(`your apps will only start after you log in. Run manually:`);
|
|
3211
|
+
console.error(` sudo loginctl enable-linger ${user}`);
|
|
3212
|
+
}
|
|
3213
|
+
} else {
|
|
3214
|
+
const ok = tryEnableLinger(user);
|
|
3215
|
+
if (!ok) {
|
|
3216
|
+
console.error(`note: could not verify linger status (no logind?) \u2014 skipping`);
|
|
3217
|
+
}
|
|
2956
3218
|
}
|
|
2957
3219
|
try {
|
|
2958
|
-
execSync("systemctl --user daemon-reload", {
|
|
2959
|
-
|
|
3220
|
+
execSync("systemctl --user daemon-reload", {
|
|
3221
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3222
|
+
timeout: 8e3
|
|
3223
|
+
});
|
|
3224
|
+
execSync(`systemctl --user enable ${UNIT_NAME}`, {
|
|
3225
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3226
|
+
timeout: 8e3
|
|
3227
|
+
});
|
|
2960
3228
|
console.log(`enabled and reloaded: systemctl --user enable ${UNIT_NAME}`);
|
|
3229
|
+
try {
|
|
3230
|
+
execSync(`systemctl --user start ${UNIT_NAME}`, {
|
|
3231
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3232
|
+
timeout: 8e3
|
|
3233
|
+
});
|
|
3234
|
+
console.log(`started: systemctl --user start ${UNIT_NAME}`);
|
|
3235
|
+
} catch {
|
|
3236
|
+
}
|
|
2961
3237
|
} catch (err) {
|
|
2962
3238
|
console.error(
|
|
2963
3239
|
`warning: could not enable unit (is systemd available?): ${err instanceof Error ? err.message : String(err)}`
|
|
2964
3240
|
);
|
|
2965
3241
|
console.error("run manually:");
|
|
2966
3242
|
console.error(" systemctl --user daemon-reload");
|
|
2967
|
-
console.error(` systemctl --user enable ${UNIT_NAME}`);
|
|
3243
|
+
console.error(` systemctl --user enable --now ${UNIT_NAME}`);
|
|
2968
3244
|
}
|
|
2969
3245
|
console.log("");
|
|
2970
3246
|
console.log("relife2 will now start automatically on boot.");
|
|
2971
3247
|
console.log(`ExecStart is: ${bin}`);
|
|
2972
|
-
console.log("To start now, run: systemctl --user start relife2.service");
|
|
2973
3248
|
console.log("To check status: systemctl --user status relife2.service");
|
|
2974
3249
|
return 0;
|
|
2975
3250
|
}
|
|
@@ -3165,14 +3440,14 @@ function detectRuntime() {
|
|
|
3165
3440
|
}
|
|
3166
3441
|
|
|
3167
3442
|
// src/daemon/daemon.ts
|
|
3168
|
-
import * as
|
|
3169
|
-
import
|
|
3170
|
-
import
|
|
3443
|
+
import * as fs20 from "node:fs";
|
|
3444
|
+
import os9 from "node:os";
|
|
3445
|
+
import path20 from "node:path";
|
|
3171
3446
|
import * as zlib from "node:zlib";
|
|
3172
3447
|
|
|
3173
3448
|
// src/daemon/applog.ts
|
|
3174
|
-
import * as
|
|
3175
|
-
import
|
|
3449
|
+
import * as fs14 from "node:fs";
|
|
3450
|
+
import path15 from "node:path";
|
|
3176
3451
|
|
|
3177
3452
|
// src/util/datefmt.ts
|
|
3178
3453
|
function formatDate(pattern, date = /* @__PURE__ */ new Date()) {
|
|
@@ -3197,12 +3472,12 @@ function formatDate(pattern, date = /* @__PURE__ */ new Date()) {
|
|
|
3197
3472
|
|
|
3198
3473
|
// src/daemon/applog.ts
|
|
3199
3474
|
var DEFAULT_LOG_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS Z";
|
|
3200
|
-
var DEFAULT_LOGS_DIR = (base) =>
|
|
3475
|
+
var DEFAULT_LOGS_DIR = (base) => path15.join(base, "logs");
|
|
3201
3476
|
function resolveLogPaths(app, base) {
|
|
3202
3477
|
const safe = app.name.replace(/[^\w.-]/g, "_");
|
|
3203
|
-
const out = app.outFile ?
|
|
3478
|
+
const out = app.outFile ? path15.resolve(app.cwd, app.outFile) : path15.join(DEFAULT_LOGS_DIR(base), `${safe}.out.log`);
|
|
3204
3479
|
if (app.mergeLogs) return { out, err: null };
|
|
3205
|
-
const err = app.errFile ?
|
|
3480
|
+
const err = app.errFile ? path15.resolve(app.cwd, app.errFile) : path15.join(DEFAULT_LOGS_DIR(base), `${safe}.err.log`);
|
|
3206
3481
|
return { out, err };
|
|
3207
3482
|
}
|
|
3208
3483
|
var AppLogSink = class {
|
|
@@ -3247,8 +3522,8 @@ var AppLogSink = class {
|
|
|
3247
3522
|
const out = `${this.prefix}${line}
|
|
3248
3523
|
`;
|
|
3249
3524
|
try {
|
|
3250
|
-
ensureDirSync(
|
|
3251
|
-
|
|
3525
|
+
ensureDirSync(path15.dirname(target));
|
|
3526
|
+
fs14.appendFileSync(target, out);
|
|
3252
3527
|
} catch {
|
|
3253
3528
|
}
|
|
3254
3529
|
}
|
|
@@ -3256,18 +3531,18 @@ var AppLogSink = class {
|
|
|
3256
3531
|
};
|
|
3257
3532
|
|
|
3258
3533
|
// src/daemon/logger.ts
|
|
3259
|
-
import
|
|
3260
|
-
import
|
|
3534
|
+
import fs15 from "node:fs";
|
|
3535
|
+
import path16 from "node:path";
|
|
3261
3536
|
function createDaemonLogger(base) {
|
|
3262
|
-
const logsDir =
|
|
3537
|
+
const logsDir = path16.join(base, "logs");
|
|
3263
3538
|
ensureDirSync(logsDir);
|
|
3264
|
-
const mainFile =
|
|
3539
|
+
const mainFile = path16.join(logsDir, "daemon.log");
|
|
3265
3540
|
const outRoot = logsDir;
|
|
3266
3541
|
function log(msg, extra) {
|
|
3267
3542
|
const line = `${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), msg, ...extra ?? {} })}
|
|
3268
3543
|
`;
|
|
3269
3544
|
try {
|
|
3270
|
-
|
|
3545
|
+
fs15.appendFileSync(mainFile, line);
|
|
3271
3546
|
} catch {
|
|
3272
3547
|
}
|
|
3273
3548
|
}
|
|
@@ -3276,13 +3551,13 @@ function createDaemonLogger(base) {
|
|
|
3276
3551
|
}
|
|
3277
3552
|
function appOut(name, chunk) {
|
|
3278
3553
|
try {
|
|
3279
|
-
|
|
3554
|
+
fs15.appendFileSync(path16.join(outRoot, `${safeName(name)}.out.log`), chunk);
|
|
3280
3555
|
} catch {
|
|
3281
3556
|
}
|
|
3282
3557
|
}
|
|
3283
3558
|
function appErr(name, chunk) {
|
|
3284
3559
|
try {
|
|
3285
|
-
|
|
3560
|
+
fs15.appendFileSync(path16.join(outRoot, `${safeName(name)}.err.log`), chunk);
|
|
3286
3561
|
} catch {
|
|
3287
3562
|
}
|
|
3288
3563
|
}
|
|
@@ -3291,12 +3566,12 @@ function createDaemonLogger(base) {
|
|
|
3291
3566
|
|
|
3292
3567
|
// src/daemon/pm.ts
|
|
3293
3568
|
import { spawn as spawn3 } from "node:child_process";
|
|
3294
|
-
import * as
|
|
3295
|
-
import
|
|
3569
|
+
import * as fs17 from "node:fs";
|
|
3570
|
+
import path18 from "node:path";
|
|
3296
3571
|
|
|
3297
3572
|
// src/daemon/command.ts
|
|
3298
3573
|
import { existsSync as existsSync8 } from "node:fs";
|
|
3299
|
-
import
|
|
3574
|
+
import path17 from "node:path";
|
|
3300
3575
|
import process2 from "node:process";
|
|
3301
3576
|
var JS_RE = /\.(?:c?js|mjs|ts|mts|cts)$/i;
|
|
3302
3577
|
function isJsScript(p) {
|
|
@@ -3306,10 +3581,10 @@ function isBunProject2(scriptDir) {
|
|
|
3306
3581
|
let dir = scriptDir;
|
|
3307
3582
|
const home = process2.env.HOME ?? "";
|
|
3308
3583
|
while (true) {
|
|
3309
|
-
if (existsSync8(
|
|
3584
|
+
if (existsSync8(path17.join(dir, "bun.lockb")) || existsSync8(path17.join(dir, "bun.lock"))) {
|
|
3310
3585
|
return true;
|
|
3311
3586
|
}
|
|
3312
|
-
const parent =
|
|
3587
|
+
const parent = path17.dirname(dir);
|
|
3313
3588
|
if (parent === dir) break;
|
|
3314
3589
|
if (home !== "" && dir === home) break;
|
|
3315
3590
|
dir = parent;
|
|
@@ -3345,7 +3620,7 @@ function buildSpawnSpec(app) {
|
|
|
3345
3620
|
|
|
3346
3621
|
// src/daemon/memproc.ts
|
|
3347
3622
|
import { execFileSync } from "node:child_process";
|
|
3348
|
-
import * as
|
|
3623
|
+
import * as fs16 from "node:fs";
|
|
3349
3624
|
function processRss(pid) {
|
|
3350
3625
|
if (pid <= 0) return void 0;
|
|
3351
3626
|
try {
|
|
@@ -3361,7 +3636,7 @@ function processRss(pid) {
|
|
|
3361
3636
|
}
|
|
3362
3637
|
}
|
|
3363
3638
|
function rssFromProc(pid) {
|
|
3364
|
-
const status =
|
|
3639
|
+
const status = fs16.readFileSync(`/proc/${pid}/status`, "utf8");
|
|
3365
3640
|
const match = /^VmRSS:\s+(\d+)\s+kB/m.exec(status);
|
|
3366
3641
|
if (match?.[1] === void 0) return void 0;
|
|
3367
3642
|
return Number(match[1]) * 1024;
|
|
@@ -3436,7 +3711,7 @@ var ProcessManager = class {
|
|
|
3436
3711
|
});
|
|
3437
3712
|
record.config.cwd = cwdFix.path;
|
|
3438
3713
|
}
|
|
3439
|
-
if (
|
|
3714
|
+
if (path18.isAbsolute(record.config.script) && !fs17.existsSync(record.config.script)) {
|
|
3440
3715
|
const sFix = fixScript(record.config.script, record.config.cwd, record.config.sourceDir);
|
|
3441
3716
|
if (sFix.fixed && sFix.reason) {
|
|
3442
3717
|
this.logger.log(sFix.reason);
|
|
@@ -3450,7 +3725,7 @@ var ProcessManager = class {
|
|
|
3450
3725
|
} else if (!sFix.fixed) {
|
|
3451
3726
|
this.fail(
|
|
3452
3727
|
record,
|
|
3453
|
-
`script not found: '${record.config.script}'. ${sFix.reason ?? ""} Run 'relife2 find --root ${
|
|
3728
|
+
`script not found: '${record.config.script}'. ${sFix.reason ?? ""} Run 'relife2 find --root ${path18.dirname(record.config.sourceDir)}' to locate configs or fix the config.`
|
|
3454
3729
|
);
|
|
3455
3730
|
this.store.saveSync();
|
|
3456
3731
|
return;
|
|
@@ -3766,19 +4041,19 @@ var ProcessManager = class {
|
|
|
3766
4041
|
const watch3 = record.config.watch;
|
|
3767
4042
|
if (watch3 === void 0 || watch3 === false) return;
|
|
3768
4043
|
if (this.watchers.has(record.name)) return;
|
|
3769
|
-
const rawPaths = Array.isArray(watch3) ? watch3.map((p) =>
|
|
4044
|
+
const rawPaths = Array.isArray(watch3) ? watch3.map((p) => path18.resolve(record.config.cwd, p)) : [record.config.cwd];
|
|
3770
4045
|
const watchers = [];
|
|
3771
4046
|
for (const target of rawPaths) {
|
|
3772
4047
|
try {
|
|
3773
|
-
if (!
|
|
4048
|
+
if (!fs17.existsSync(target)) {
|
|
3774
4049
|
this.logger.log(`watch: path '${target}' does not exist yet; skipping`);
|
|
3775
4050
|
continue;
|
|
3776
4051
|
}
|
|
3777
|
-
const stat =
|
|
4052
|
+
const stat = fs17.statSync(target);
|
|
3778
4053
|
if (!stat.isDirectory()) {
|
|
3779
|
-
const dir =
|
|
3780
|
-
const base =
|
|
3781
|
-
const watcher =
|
|
4054
|
+
const dir = path18.dirname(target);
|
|
4055
|
+
const base = path18.basename(target);
|
|
4056
|
+
const watcher = fs17.watch(dir, (_eventType, filename) => {
|
|
3782
4057
|
if (filename !== null && filename !== base) return;
|
|
3783
4058
|
this.onWatchChange(record.name, target);
|
|
3784
4059
|
});
|
|
@@ -3786,7 +4061,7 @@ var ProcessManager = class {
|
|
|
3786
4061
|
continue;
|
|
3787
4062
|
}
|
|
3788
4063
|
if (process.platform === "win32" || process.platform === "darwin") {
|
|
3789
|
-
const watcher =
|
|
4064
|
+
const watcher = fs17.watch(target, { recursive: true }, () => {
|
|
3790
4065
|
this.onWatchChange(record.name, target);
|
|
3791
4066
|
});
|
|
3792
4067
|
watchers.push(watcher);
|
|
@@ -3794,7 +4069,7 @@ var ProcessManager = class {
|
|
|
3794
4069
|
const dirs = this.collectSubDirs(target);
|
|
3795
4070
|
for (const dir of dirs) {
|
|
3796
4071
|
try {
|
|
3797
|
-
const watcher =
|
|
4072
|
+
const watcher = fs17.watch(dir, () => {
|
|
3798
4073
|
this.onWatchChange(record.name, target);
|
|
3799
4074
|
});
|
|
3800
4075
|
watchers.push(watcher);
|
|
@@ -3821,7 +4096,7 @@ var ProcessManager = class {
|
|
|
3821
4096
|
const dir = queue.shift();
|
|
3822
4097
|
let entries;
|
|
3823
4098
|
try {
|
|
3824
|
-
entries =
|
|
4099
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
3825
4100
|
} catch {
|
|
3826
4101
|
continue;
|
|
3827
4102
|
}
|
|
@@ -3829,7 +4104,7 @@ var ProcessManager = class {
|
|
|
3829
4104
|
if (!entry.isDirectory()) continue;
|
|
3830
4105
|
if (entry.name.startsWith(".")) continue;
|
|
3831
4106
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
3832
|
-
const full =
|
|
4107
|
+
const full = path18.join(dir, entry.name);
|
|
3833
4108
|
if (seen.has(full)) continue;
|
|
3834
4109
|
seen.add(full);
|
|
3835
4110
|
out.push(full);
|
|
@@ -3929,17 +4204,17 @@ var ProcessManager = class {
|
|
|
3929
4204
|
};
|
|
3930
4205
|
|
|
3931
4206
|
// src/daemon/state.ts
|
|
3932
|
-
import
|
|
3933
|
-
import
|
|
4207
|
+
import fs18 from "node:fs";
|
|
4208
|
+
import path19 from "node:path";
|
|
3934
4209
|
var StateStore = class {
|
|
3935
4210
|
apps = /* @__PURE__ */ new Map();
|
|
3936
4211
|
snapshotPath;
|
|
3937
4212
|
journalPath;
|
|
3938
4213
|
constructor(base) {
|
|
3939
|
-
const stateDir =
|
|
4214
|
+
const stateDir = path19.join(base, "state");
|
|
3940
4215
|
ensureDirSync(stateDir);
|
|
3941
|
-
this.snapshotPath =
|
|
3942
|
-
this.journalPath =
|
|
4216
|
+
this.snapshotPath = path19.join(stateDir, "snapshot.json");
|
|
4217
|
+
this.journalPath = path19.join(stateDir, "journal.jsonl");
|
|
3943
4218
|
}
|
|
3944
4219
|
load() {
|
|
3945
4220
|
const data = readJsonFile(this.snapshotPath);
|
|
@@ -3976,7 +4251,7 @@ var StateStore = class {
|
|
|
3976
4251
|
const line = `${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), type, ...data ?? {} })}
|
|
3977
4252
|
`;
|
|
3978
4253
|
try {
|
|
3979
|
-
|
|
4254
|
+
fs18.appendFileSync(this.journalPath, line);
|
|
3980
4255
|
} catch {
|
|
3981
4256
|
}
|
|
3982
4257
|
}
|
|
@@ -3988,7 +4263,7 @@ var StateStore = class {
|
|
|
3988
4263
|
};
|
|
3989
4264
|
|
|
3990
4265
|
// src/daemon/transport.ts
|
|
3991
|
-
import
|
|
4266
|
+
import fs19 from "node:fs";
|
|
3992
4267
|
import net2 from "node:net";
|
|
3993
4268
|
async function createDaemonTransport(base) {
|
|
3994
4269
|
return process.platform === "win32" ? createWinTransport(base) : createPosixTransport(base);
|
|
@@ -4002,7 +4277,7 @@ async function createPosixTransport(base) {
|
|
|
4002
4277
|
if (err.code !== "EADDRINUSE") return null;
|
|
4003
4278
|
if (await probeActive(sockPath)) return null;
|
|
4004
4279
|
try {
|
|
4005
|
-
|
|
4280
|
+
fs19.unlinkSync(sockPath);
|
|
4006
4281
|
} catch {
|
|
4007
4282
|
}
|
|
4008
4283
|
}
|
|
@@ -4055,7 +4330,7 @@ function probeActive(sockPath) {
|
|
|
4055
4330
|
}
|
|
4056
4331
|
function readPortFile(base) {
|
|
4057
4332
|
try {
|
|
4058
|
-
const raw =
|
|
4333
|
+
const raw = fs19.readFileSync(portFilePath(base), "utf8");
|
|
4059
4334
|
return Number.parseInt(raw.trim(), 10) || null;
|
|
4060
4335
|
} catch {
|
|
4061
4336
|
return null;
|
|
@@ -4063,7 +4338,7 @@ function readPortFile(base) {
|
|
|
4063
4338
|
}
|
|
4064
4339
|
function writePortFile(base, port) {
|
|
4065
4340
|
try {
|
|
4066
|
-
|
|
4341
|
+
fs19.writeFileSync(portFilePath(base), String(port));
|
|
4067
4342
|
} catch {
|
|
4068
4343
|
}
|
|
4069
4344
|
}
|
|
@@ -4072,7 +4347,7 @@ function writePortFile(base, port) {
|
|
|
4072
4347
|
async function runDaemon() {
|
|
4073
4348
|
const data = dataDir();
|
|
4074
4349
|
const runtime = runtimeDir();
|
|
4075
|
-
ensureDirSync(
|
|
4350
|
+
ensureDirSync(path20.join(data, "logs"));
|
|
4076
4351
|
const logger = createDaemonLogger(data);
|
|
4077
4352
|
logger.log("daemon starting", { pid: process.pid, version: VERSION, platform: process.platform });
|
|
4078
4353
|
const lock = acquireLock(runtime);
|
|
@@ -4287,8 +4562,8 @@ var METHODS = {
|
|
|
4287
4562
|
const files = paths.err === null ? [paths.out] : [paths.out, paths.err];
|
|
4288
4563
|
for (const f of files) {
|
|
4289
4564
|
try {
|
|
4290
|
-
ensureDirSync(
|
|
4291
|
-
|
|
4565
|
+
ensureDirSync(path20.dirname(f));
|
|
4566
|
+
fs20.writeFileSync(f, "");
|
|
4292
4567
|
} catch (err) {
|
|
4293
4568
|
logger.log(`flush ${rec.name}: cannot truncate ${f}: ${err.message}`);
|
|
4294
4569
|
}
|
|
@@ -4321,7 +4596,7 @@ var METHODS = {
|
|
|
4321
4596
|
for (const f of files) {
|
|
4322
4597
|
if (!pathExists(f)) continue;
|
|
4323
4598
|
try {
|
|
4324
|
-
const stat =
|
|
4599
|
+
const stat = fs20.statSync(f);
|
|
4325
4600
|
if (stat.size >= max) {
|
|
4326
4601
|
rotateFile(f, n);
|
|
4327
4602
|
rotated.push(f);
|
|
@@ -4375,11 +4650,11 @@ var METHODS = {
|
|
|
4375
4650
|
},
|
|
4376
4651
|
doctor: (_params, { store, base, transport }) => {
|
|
4377
4652
|
const checks = [];
|
|
4378
|
-
const snapshotPath =
|
|
4653
|
+
const snapshotPath = path20.join(base, "state", "snapshot.json");
|
|
4379
4654
|
const stateIssues = [];
|
|
4380
4655
|
if (pathExists(snapshotPath)) {
|
|
4381
4656
|
try {
|
|
4382
|
-
const data = JSON.parse(
|
|
4657
|
+
const data = JSON.parse(fs20.readFileSync(snapshotPath, "utf8"));
|
|
4383
4658
|
if (data.schema === 1 && typeof data.apps === "object") {
|
|
4384
4659
|
stateIssues.push({
|
|
4385
4660
|
severity: "ok",
|
|
@@ -4412,11 +4687,11 @@ var METHODS = {
|
|
|
4412
4687
|
ok: !stateIssues.some((i) => i.severity === "error"),
|
|
4413
4688
|
issues: stateIssues
|
|
4414
4689
|
});
|
|
4415
|
-
const lockFile =
|
|
4690
|
+
const lockFile = path20.join(runtimeDir(), "daemon.lock");
|
|
4416
4691
|
const lockIssues = [];
|
|
4417
4692
|
if (pathExists(lockFile)) {
|
|
4418
4693
|
try {
|
|
4419
|
-
const lock = JSON.parse(
|
|
4694
|
+
const lock = JSON.parse(fs20.readFileSync(lockFile, "utf8"));
|
|
4420
4695
|
if (typeof lock.pid === "number" && isAlive(lock.pid)) {
|
|
4421
4696
|
lockIssues.push({
|
|
4422
4697
|
severity: "ok",
|
|
@@ -4527,7 +4802,7 @@ async function startApps(target, ctx) {
|
|
|
4527
4802
|
if (target.interpreter !== void 0) input.interpreter = target.interpreter;
|
|
4528
4803
|
if (target.cwd !== void 0) input.cwd = target.cwd;
|
|
4529
4804
|
if (target.cliOverrides) Object.assign(input, target.cliOverrides);
|
|
4530
|
-
const single = normalizeSingleApp(input,
|
|
4805
|
+
const single = normalizeSingleApp(input, path20.dirname(target.path), target.envName);
|
|
4531
4806
|
apps = [single.app];
|
|
4532
4807
|
warnings = single.warnings;
|
|
4533
4808
|
}
|
|
@@ -4592,7 +4867,7 @@ function expandTargets(store, name) {
|
|
|
4592
4867
|
function applyCliOverrides(app, overrides) {
|
|
4593
4868
|
if (overrides.instances !== void 0) {
|
|
4594
4869
|
const v = overrides.instances;
|
|
4595
|
-
if (v === "max" || v === "MAX") app.instances =
|
|
4870
|
+
if (v === "max" || v === "MAX") app.instances = os9.cpus().length || 1;
|
|
4596
4871
|
else {
|
|
4597
4872
|
const n = Number(v);
|
|
4598
4873
|
if (Number.isFinite(n) && n >= 1) app.instances = Math.round(n);
|
|
@@ -4652,15 +4927,15 @@ function rotateFile(file, retain) {
|
|
|
4652
4927
|
const newName = `${file}.${i + 1}.gz`;
|
|
4653
4928
|
if (pathExists(oldName)) {
|
|
4654
4929
|
if (i + 1 >= retain) {
|
|
4655
|
-
|
|
4930
|
+
fs20.unlinkSync(oldName);
|
|
4656
4931
|
} else {
|
|
4657
4932
|
if (i === 0) {
|
|
4658
|
-
const content =
|
|
4659
|
-
|
|
4933
|
+
const content = fs20.readFileSync(file);
|
|
4934
|
+
fs20.unlinkSync(file);
|
|
4660
4935
|
const gz = zlib.gzipSync(content);
|
|
4661
|
-
|
|
4936
|
+
fs20.writeFileSync(newName, gz);
|
|
4662
4937
|
} else {
|
|
4663
|
-
|
|
4938
|
+
fs20.renameSync(oldName, newName);
|
|
4664
4939
|
}
|
|
4665
4940
|
}
|
|
4666
4941
|
}
|