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 CHANGED
@@ -14,13 +14,13 @@ var jsloader_exports = {};
14
14
  __export(jsloader_exports, {
15
15
  loadJsFile: () => loadJsFile
16
16
  });
17
- import * as fs5 from "node:fs";
17
+ import * as fs7 from "node:fs";
18
18
  import { createRequire } from "node:module";
19
- import path6 from "node:path";
19
+ import path7 from "node:path";
20
20
  import { pathToFileURL } from "node:url";
21
21
  async function loadJsFile(file) {
22
- const ext = path6.extname(file).toLowerCase();
23
- const source = fs5.readFileSync(file, "utf8");
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(path6.dirname(file));
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 < 100 && sock === null; 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
- const ping = await client.call("ping");
336
- if (ping.protocol !== PROTOCOL_VERSION) {
337
- client.close();
338
- throw new Error(
339
- `daemon protocol mismatch (daemon: ${ping.protocol}, cli: ${PROTOCOL_VERSION}); run 'relife2 kill' to stop the stale daemon`
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 fs3 from "node:fs";
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 (fs3.existsSync(path4.join(cur, "bun.lockb")) || fs3.existsSync(path4.join(cur, "bun.lock")))
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 = fs3.readdirSync(dir, { withFileTypes: true });
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 (!fs3.existsSync(script)) {
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 (!fs3.existsSync(target)) {
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 = fs3.statSync(target);
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 = fs3.watch(dir, (_ev, filename) => {
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 = fs3.watch(target, { recursive: true }, () => scheduleRestart(target));
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 = fs3.watch(dir, () => scheduleRestart(dir));
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
- async function run4(_args) {
793
- const client = await ensureDaemonClient();
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 fs7 from "node:fs";
818
- import os4 from "node:os";
819
- import path8 from "node:path";
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 fs6 from "node:fs";
823
- import os3 from "node:os";
824
- import path7 from "node:path";
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 fs4 from "node:fs";
944
- import os2 from "node:os";
945
- import path5 from "node:path";
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 fs4.statSync(p).isDirectory();
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 fs4.statSync(p).isFile();
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 = fs4.readdirSync(cur.dir, { withFileTypes: true });
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 = path5.join(cur.dir, e.name);
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 fs4.realpathSync(full);
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(path5.join(os2.homedir(), tail));
1007
- candidates.push(path5.join(path5.dirname(os2.homedir()), tail));
1008
- candidates.push(path5.join(path5.dirname(sourceDir), tail));
1009
- if (isDir(sourceDir) && !staleCwd.startsWith(os2.homedir())) candidates.push(sourceDir);
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 = path5.basename(sourceDir);
1212
+ const proj = path6.basename(sourceDir);
1012
1213
  if (proj) {
1013
- const marker = `${path5.sep}${proj}${path5.sep}`;
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(path5.join(sourceDir, suffix));
1019
- candidates.push(path5.join(path5.dirname(sourceDir), proj, suffix));
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 = path5.basename(staleScript);
1235
+ const base = path6.basename(staleScript);
1035
1236
  const candidates = [];
1036
- candidates.push(path5.join(cwd, base));
1037
- candidates.push(path5.join(sourceDir, base));
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(path5.join(os2.homedir(), tail));
1041
- candidates.push(path5.join(path5.dirname(os2.homedir()), tail));
1042
- candidates.push(path5.join(sourceDir, tail));
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(path5.join(sourceDir, tailParts.slice(1).join("/")));
1245
+ if (tailParts.length > 1) candidates.push(path6.join(sourceDir, tailParts.slice(1).join("/")));
1045
1246
  }
1046
- const proj = path5.basename(sourceDir);
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(path5.join(sourceDir, suffix));
1053
- candidates.push(path5.join(cwd, suffix));
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(path5.sep);
1059
- candidates.push(path5.join(sourceDir, lastTwo));
1060
- candidates.push(path5.join(cwd, lastTwo));
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 CONFIG_FILE_RE = /^(?:(?:ecosystem|pm2|relife2)[\w.-]*|[\w.-]*?)\.config\.(?:c?js|mjs|json|ts)$/i;
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
- return CONFIG_FILE_RE.test(path7.basename(file));
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 = path7.join(cwd, name);
1160
- if (fs6.existsSync(full)) return full;
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 = path7.extname(filePath).toLowerCase();
1374
+ const ext = path8.extname(filePath).toLowerCase();
1166
1375
  let exported;
1167
1376
  if (ext === ".json") {
1168
- exported = JSON.parse(fs6.readFileSync(filePath, "utf8"));
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, path7.dirname(filePath), opts);
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 = fs6.readFileSync(file, "utf8");
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 = `${file}.tmp.cjs`;
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
- fs6.writeFileSync(tmp, stripped, "utf8");
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
- fs6.unlinkSync(tmp);
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 = path7.resolve(sourceDir, rawCwd ?? ".");
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 ? path7.resolve(cwd, scriptRaw) : scriptRaw;
1470
+ const scriptRawResolved = hasSep || isJsExt ? path8.resolve(cwd, scriptRaw) : scriptRaw;
1259
1471
  let script = scriptRawResolved;
1260
- if (path7.isAbsolute(scriptRawResolved)) {
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 (!fs6.existsSync(scriptRawResolved) && f.reason) {
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 = path7.resolve(cwd, envFile);
1494
+ const envPath = path8.resolve(cwd, envFile);
1283
1495
  let parsed = null;
1284
- if (fs6.existsSync(envPath) && fs6.statSync(envPath).isFile()) {
1496
+ if (fs8.existsSync(envPath) && fs8.statSync(envPath).isFile()) {
1285
1497
  try {
1286
- parsed = parseDotenv(fs6.readFileSync(envPath, "utf8"));
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 os3.cpus().length || 1;
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 = path7.basename(script);
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 = fs7.realpathSync(dir);
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 = fs7.readdirSync(dir, { withFileTypes: true });
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 = path8.join(dir, ent.name);
1486
- if (ent.isFile() && looksLikeConfigFile(ent.name)) {
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 = path8.relative(root, p);
1516
- return rel === "" ? path8.basename(p) : 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" ? path8.parse(process.cwd()).root : "/";
1738
+ root = process.platform === "win32" ? path9.parse(process.cwd()).root : "/";
1527
1739
  } else {
1528
- root = os4.homedir() || process.cwd();
1740
+ root = os5.homedir() || process.cwd();
1529
1741
  }
1530
1742
  }
1531
- root = path8.resolve(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 (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) {
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 ${path8.basename(r.path)}: ${w}`);
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} (${path8.basename(r.path)}): ${a.message ?? "failed"}`);
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 ${path8.basename(r.path)}: ${err.message}`);
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 fs8 from "node:fs";
1682
- import os5 from "node:os";
1683
- import path9 from "node:path";
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 = path9.join(dir, pat);
1857
- if (fs8.existsSync(p)) return { script: pat, exists: true };
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 = path9.isAbsolute(hint.dir) ? hint.dir : path9.resolve(caddyfileDir, hint.dir);
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 = os5.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(path9.resolve(caddyfileDir, ".."));
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(path9.join(homedir, siteName));
1876
- if (homedir) candidates.push(path9.join(homedir, siteName.split("-")[0]));
1877
- for (const r of uniqRoots) candidates.push(path9.join(r, siteName));
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(path9.join(homedir, rawDir));
2091
+ if (rawDir && homedir) candidates.push(path10.join(homedir, rawDir));
1880
2092
  for (const cand of candidates) {
1881
- if (fs8.existsSync(cand) && fs8.statSync(cand).isDirectory()) {
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 ? path9.join(homedir, siteName) : path9.join(caddyfileDir || process.cwd(), siteName);
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 : path9.join(os5.homedir(), "Caddyfile")
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 = path9.resolve(cand);
1924
- if (fs8.existsSync(p)) {
2135
+ const p = path10.resolve(cand);
2136
+ if (fs10.existsSync(p)) {
1925
2137
  resolvedCaddy = p;
1926
- content = fs8.readFileSync(p, "utf8");
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 = path9.dirname(resolvedCaddy);
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 = fs8.existsSync(path9.isAbsolute(entry) ? entry : path9.join(dir, entry));
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 = fs8.existsSync(path9.join(dir, "bun.lockb")) || fs8.existsSync(path9.join(dir, "bun.lock"));
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 = path9.resolve(outPath ?? "relife2.config.cjs");
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
- fs8.writeFileSync(resolvedOut, fileContent, "utf8");
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 ${path9.basename(resolvedOut)} \u2192 relife2 start ${path9.basename(resolvedOut)}`
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.1";
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 Run integrity checks on the running daemon (M3)
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 fs9 from "node:fs";
2247
- import os6 from "node:os";
2248
- import path10 from "node:path";
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 ? path10.resolve(target) : path10.join(os6.homedir(), ".pm2", "dump.pm2");
2252
- if (!fs9.existsSync(dumpPath)) {
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(fs9.readFileSync(dumpPath, "utf8"));
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 = path10.join(
2306
- os6.tmpdir(),
2517
+ const tmpPath = path11.join(
2518
+ os7.tmpdir(),
2307
2519
  `relife2-import-${Date.now()}-${Math.random().toString(36).slice(2)}.cjs`
2308
2520
  );
2309
- fs9.writeFileSync(tmpPath, `module.exports = ${JSON.stringify(tmpConfig)}`, "utf8");
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
- fs9.unlinkSync(tmpPath);
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 path11 from "node:path";
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(path11.resolve(file));
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 fs10 from "node:fs";
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 = fs10.readFileSync(file, "utf8");
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 = fs10.openSync(f, "r");
2699
+ const fd = fs12.openSync(f, "r");
2488
2700
  try {
2489
2701
  const buf = Buffer.alloc(size - from);
2490
- fs10.readSync(fd, buf, 0, buf.length, from);
2702
+ fs12.readSync(fd, buf, 0, buf.length, from);
2491
2703
  process.stdout.write(buf);
2492
2704
  } finally {
2493
- fs10.closeSync(fd);
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 fs10.statSync(file).size;
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 path12 from "node:path";
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: path12.resolve(target),
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 fs11 from "node:fs";
2871
- import os7 from "node:os";
2872
- import path13 from "node:path";
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 = path13.join(os7.homedir(), ".config", "systemd", "user");
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 = path13.resolve(path13.dirname(thisFile), "..", "cli.js");
2893
- if (fs11.existsSync(cliPath)) {
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 ${path13.resolve(arg1 ?? process.cwd())}`;
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 = path13.join(SYSTEMD_USER_DIR, UNIT_NAME);
3187
+ const unitPath = path14.join(SYSTEMD_USER_DIR, UNIT_NAME);
2933
3188
  try {
2934
- fs11.mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
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
- fs11.writeFileSync(unitPath, unitContent);
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
- try {
2947
- const user = process.env.USER ?? os7.userInfo().username ?? "root";
2948
- execSync(`loginctl enable-linger ${user}`, { stdio: ["ignore", "pipe", "pipe"] });
2949
- console.log(`enabled linger for user ${user}`);
2950
- } catch (err) {
2951
- console.error(
2952
- `warning: could not run 'loginctl enable-linger' (is systemd-logind running?): ${err instanceof Error ? err.message : String(err)}`
2953
- );
2954
- console.error("your apps will only start after you log in. Run manually:");
2955
- console.error(` loginctl enable-linger ${process.env.USER ?? os7.userInfo().username}`);
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", { stdio: ["ignore", "pipe", "pipe"] });
2959
- execSync(`systemctl --user enable ${UNIT_NAME}`, { stdio: ["ignore", "pipe", "pipe"] });
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 fs18 from "node:fs";
3169
- import os8 from "node:os";
3170
- import path19 from "node:path";
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 fs12 from "node:fs";
3175
- import path14 from "node:path";
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) => path14.join(base, "logs");
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 ? path14.resolve(app.cwd, app.outFile) : path14.join(DEFAULT_LOGS_DIR(base), `${safe}.out.log`);
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 ? path14.resolve(app.cwd, app.errFile) : path14.join(DEFAULT_LOGS_DIR(base), `${safe}.err.log`);
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(path14.dirname(target));
3251
- fs12.appendFileSync(target, out);
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 fs13 from "node:fs";
3260
- import path15 from "node:path";
3534
+ import fs15 from "node:fs";
3535
+ import path16 from "node:path";
3261
3536
  function createDaemonLogger(base) {
3262
- const logsDir = path15.join(base, "logs");
3537
+ const logsDir = path16.join(base, "logs");
3263
3538
  ensureDirSync(logsDir);
3264
- const mainFile = path15.join(logsDir, "daemon.log");
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
- fs13.appendFileSync(mainFile, line);
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
- fs13.appendFileSync(path15.join(outRoot, `${safeName(name)}.out.log`), chunk);
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
- fs13.appendFileSync(path15.join(outRoot, `${safeName(name)}.err.log`), chunk);
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 fs15 from "node:fs";
3295
- import path17 from "node:path";
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 path16 from "node:path";
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(path16.join(dir, "bun.lockb")) || existsSync8(path16.join(dir, "bun.lock"))) {
3584
+ if (existsSync8(path17.join(dir, "bun.lockb")) || existsSync8(path17.join(dir, "bun.lock"))) {
3310
3585
  return true;
3311
3586
  }
3312
- const parent = path16.dirname(dir);
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 fs14 from "node:fs";
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 = fs14.readFileSync(`/proc/${pid}/status`, "utf8");
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 (path17.isAbsolute(record.config.script) && !fs15.existsSync(record.config.script)) {
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 ${path17.dirname(record.config.sourceDir)}' to locate configs or fix the config.`
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) => path17.resolve(record.config.cwd, p)) : [record.config.cwd];
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 (!fs15.existsSync(target)) {
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 = fs15.statSync(target);
4052
+ const stat = fs17.statSync(target);
3778
4053
  if (!stat.isDirectory()) {
3779
- const dir = path17.dirname(target);
3780
- const base = path17.basename(target);
3781
- const watcher = fs15.watch(dir, (_eventType, filename) => {
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 = fs15.watch(target, { recursive: true }, () => {
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 = fs15.watch(dir, () => {
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 = fs15.readdirSync(dir, { withFileTypes: true });
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 = path17.join(dir, entry.name);
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 fs16 from "node:fs";
3933
- import path18 from "node:path";
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 = path18.join(base, "state");
4214
+ const stateDir = path19.join(base, "state");
3940
4215
  ensureDirSync(stateDir);
3941
- this.snapshotPath = path18.join(stateDir, "snapshot.json");
3942
- this.journalPath = path18.join(stateDir, "journal.jsonl");
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
- fs16.appendFileSync(this.journalPath, line);
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 fs17 from "node:fs";
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
- fs17.unlinkSync(sockPath);
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 = fs17.readFileSync(portFilePath(base), "utf8");
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
- fs17.writeFileSync(portFilePath(base), String(port));
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(path19.join(data, "logs"));
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(path19.dirname(f));
4291
- fs18.writeFileSync(f, "");
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 = fs18.statSync(f);
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 = path19.join(base, "state", "snapshot.json");
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(fs18.readFileSync(snapshotPath, "utf8"));
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 = path19.join(runtimeDir(), "daemon.lock");
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(fs18.readFileSync(lockFile, "utf8"));
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, path19.dirname(target.path), target.envName);
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 = os8.cpus().length || 1;
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
- fs18.unlinkSync(oldName);
4930
+ fs20.unlinkSync(oldName);
4656
4931
  } else {
4657
4932
  if (i === 0) {
4658
- const content = fs18.readFileSync(file);
4659
- fs18.unlinkSync(file);
4933
+ const content = fs20.readFileSync(file);
4934
+ fs20.unlinkSync(file);
4660
4935
  const gz = zlib.gzipSync(content);
4661
- fs18.writeFileSync(newName, gz);
4936
+ fs20.writeFileSync(newName, gz);
4662
4937
  } else {
4663
- fs18.renameSync(oldName, newName);
4938
+ fs20.renameSync(oldName, newName);
4664
4939
  }
4665
4940
  }
4666
4941
  }