relife2 1.0.1 → 1.0.3

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,50 @@ var RpcClient = class {
318
319
  async function ensureDaemonClient() {
319
320
  const runtime = runtimeDir();
320
321
  const data = dataDir();
322
+ for (const dir of [runtime, data, path3.join(data, "logs"), path3.join(data, "state")]) {
323
+ try {
324
+ fs3.mkdirSync(dir, { recursive: true });
325
+ } catch {
326
+ }
327
+ }
321
328
  let sock = await tryConnect(runtime);
329
+ if (sock !== null) {
330
+ return await validateClient(sock);
331
+ }
332
+ spawnDaemon();
333
+ for (let i = 0; i < 80 && sock === null; i++) {
334
+ await sleep(100);
335
+ sock = await tryConnect(runtime);
336
+ }
322
337
  if (sock === null) {
323
- spawnDaemon();
324
- for (let i = 0; i < 100 && sock === null; i++) {
325
- await sleep(100);
326
- sock = await tryConnect(runtime);
327
- }
338
+ await sleep(200);
339
+ sock = await tryConnect(runtime);
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;
341
365
  }
342
- return client;
343
366
  }
344
367
  function spawnDaemon() {
345
368
  const arg1 = process.argv[1];
@@ -493,7 +516,7 @@ async function run2(args) {
493
516
 
494
517
  // src/commands/dev.ts
495
518
  import { spawn as spawn2, spawnSync } from "node:child_process";
496
- import * as fs3 from "node:fs";
519
+ import * as fs4 from "node:fs";
497
520
  import path4 from "node:path";
498
521
  var DEV_VALUE_OPTS = /* @__PURE__ */ new Set(["watch", "interpreter", "env", "name", "w"]);
499
522
  function hasBun() {
@@ -511,7 +534,7 @@ function isBunProject(cwd) {
511
534
  let cur = path4.resolve(cwd);
512
535
  const home = path4.resolve(process.env.HOME ?? "");
513
536
  for (let i = 0; i < 12; i++) {
514
- if (fs3.existsSync(path4.join(cur, "bun.lockb")) || fs3.existsSync(path4.join(cur, "bun.lock")))
537
+ if (fs4.existsSync(path4.join(cur, "bun.lockb")) || fs4.existsSync(path4.join(cur, "bun.lock")))
515
538
  return true;
516
539
  if (cur === home || cur === path4.dirname(cur)) break;
517
540
  cur = path4.dirname(cur);
@@ -526,7 +549,7 @@ function collectSubDirs(root) {
526
549
  if (!dir) continue;
527
550
  let entries = [];
528
551
  try {
529
- entries = fs3.readdirSync(dir, { withFileTypes: true });
552
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
530
553
  } catch {
531
554
  continue;
532
555
  }
@@ -556,7 +579,7 @@ async function run3(args) {
556
579
  return 1;
557
580
  }
558
581
  const script = path4.resolve(scriptArg);
559
- if (!fs3.existsSync(script)) {
582
+ if (!fs4.existsSync(script)) {
560
583
  console.error(`dev: script not found: ${script}`);
561
584
  return 1;
562
585
  }
@@ -664,16 +687,16 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
664
687
  }, 200);
665
688
  };
666
689
  for (const target of targets) {
667
- if (!fs3.existsSync(target)) {
690
+ if (!fs4.existsSync(target)) {
668
691
  console.error(`dev: watch path does not exist: ${target} \u2014 skipping`);
669
692
  continue;
670
693
  }
671
- const stat = fs3.statSync(target);
694
+ const stat = fs4.statSync(target);
672
695
  if (!stat.isDirectory()) {
673
696
  const dir = path4.dirname(target);
674
697
  const base = path4.basename(target);
675
698
  try {
676
- const w = fs3.watch(dir, (_ev, filename) => {
699
+ const w = fs4.watch(dir, (_ev, filename) => {
677
700
  if (filename && filename !== base) return;
678
701
  scheduleRestart(target);
679
702
  });
@@ -685,7 +708,7 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
685
708
  }
686
709
  if (process.platform === "win32" || process.platform === "darwin") {
687
710
  try {
688
- const w = fs3.watch(target, { recursive: true }, () => scheduleRestart(target));
711
+ const w = fs4.watch(target, { recursive: true }, () => scheduleRestart(target));
689
712
  watchers.push(w);
690
713
  } catch (err) {
691
714
  console.error(`dev: cannot watch ${target}: ${err.message}`);
@@ -694,7 +717,7 @@ async function runManualWatch(script, cwd, interpreter, appArgs, watchRaw) {
694
717
  const dirs = collectSubDirs(target);
695
718
  for (const dir of dirs) {
696
719
  try {
697
- const w = fs3.watch(dir, () => scheduleRestart(dir));
720
+ const w = fs4.watch(dir, () => scheduleRestart(dir));
698
721
  watchers.push(w);
699
722
  } catch {
700
723
  }
@@ -789,8 +812,32 @@ Examples:
789
812
  }
790
813
 
791
814
  // src/commands/doctor.ts
792
- async function run4(_args) {
793
- const client = await ensureDaemonClient();
815
+ import fs5 from "node:fs";
816
+ import os2 from "node:os";
817
+ import path5 from "node:path";
818
+ async function run4(args) {
819
+ const fix = args.includes("--fix") || args.includes("-f");
820
+ if (fix) {
821
+ const healed = healLocally();
822
+ if (healed.length > 0) {
823
+ for (const m of healed) console.log(`healed: ${m}`);
824
+ } else {
825
+ console.log("heal: nothing to fix locally");
826
+ }
827
+ }
828
+ let client;
829
+ try {
830
+ client = await ensureDaemonClient();
831
+ } catch (err) {
832
+ if (fix) {
833
+ console.error(`doctor: daemon still not reachable after heal: ${err.message}`);
834
+ console.error("try: rm -rf ~/.relife2/state/* && relife2 kill; relife2 list");
835
+ } else {
836
+ console.error(err.message);
837
+ console.error("hint: relife2 doctor --fix (cleans stale lock + *.tmp.cjs)");
838
+ }
839
+ return 1;
840
+ }
794
841
  try {
795
842
  const report = await client.call("doctor", {});
796
843
  let hadErrors = false;
@@ -807,21 +854,143 @@ async function run4(_args) {
807
854
  console.log(
808
855
  `checks: ${report.summary.total} total, ${report.summary.ok} ok, ${report.summary.warnings} warnings, ${report.summary.errors} errors`
809
856
  );
857
+ if (hadErrors && !fix) {
858
+ console.log("hint: relife2 doctor --fix (cleans stale lock + *.tmp.cjs)");
859
+ }
810
860
  return hadErrors ? 1 : 0;
811
861
  } finally {
812
862
  client.close();
813
863
  }
814
864
  }
865
+ function healLocally() {
866
+ const out = [];
867
+ const runtime = runtimeDir();
868
+ const data = dataDir();
869
+ for (const dir of [runtime, data, path5.join(data, "logs"), path5.join(data, "state")]) {
870
+ try {
871
+ fs5.mkdirSync(dir, { recursive: true });
872
+ } catch {
873
+ }
874
+ }
875
+ const lockFile = path5.join(runtime, "daemon.lock");
876
+ try {
877
+ const raw = fs5.readFileSync(lockFile, "utf8");
878
+ const parsed = JSON.parse(raw);
879
+ const pid = typeof parsed.pid === "number" ? parsed.pid : null;
880
+ let alive = false;
881
+ if (pid !== null) {
882
+ try {
883
+ process.kill(pid, 0);
884
+ alive = true;
885
+ } catch (e) {
886
+ const code = e.code;
887
+ if (code === "EPERM") alive = true;
888
+ else if (code === "ESRCH") alive = false;
889
+ }
890
+ }
891
+ if (pid === null || !alive) {
892
+ fs5.unlinkSync(lockFile);
893
+ out.push(`removed stale lock ${lockFile} (pid=${pid})`);
894
+ }
895
+ } catch {
896
+ }
897
+ const home = os2.homedir();
898
+ if (home && fs5.existsSync(home)) {
899
+ try {
900
+ const tmpLeftovers = collectTmpLeftovers(home, 6, 50);
901
+ for (const f of tmpLeftovers) {
902
+ try {
903
+ fs5.unlinkSync(f);
904
+ out.push(`removed leftover ${path5.relative(home, f)}`);
905
+ } catch {
906
+ }
907
+ }
908
+ } catch {
909
+ }
910
+ }
911
+ try {
912
+ const tmpDir = os2.tmpdir();
913
+ const entries = fs5.readdirSync(tmpDir);
914
+ const now = Date.now();
915
+ for (const name of entries) {
916
+ if (!name.startsWith("relife2-") || !name.endsWith(".tmp.cjs")) continue;
917
+ const full = path5.join(tmpDir, name);
918
+ try {
919
+ const st = fs5.statSync(full);
920
+ if (now - st.mtimeMs > 60 * 60 * 1e3) {
921
+ fs5.unlinkSync(full);
922
+ out.push(`removed old tmp ${name}`);
923
+ }
924
+ } catch {
925
+ }
926
+ }
927
+ } catch {
928
+ }
929
+ return out;
930
+ }
931
+ function collectTmpLeftovers(root, maxDepth, limit) {
932
+ const out = [];
933
+ const stack = [{ dir: root, depth: 0 }];
934
+ const ignored = /* @__PURE__ */ new Set([
935
+ "node_modules",
936
+ ".git",
937
+ ".hg",
938
+ ".svn",
939
+ "vendor",
940
+ ".next",
941
+ ".output",
942
+ "dist",
943
+ "build",
944
+ "target",
945
+ "__pycache__",
946
+ ".cache",
947
+ ".pnpm",
948
+ ".yarn",
949
+ ".turbo",
950
+ ".parcel-cache",
951
+ "Library",
952
+ "AppData",
953
+ ".local",
954
+ "proc",
955
+ "sys",
956
+ "dev"
957
+ ]);
958
+ while (stack.length > 0 && out.length < limit) {
959
+ const cur = stack.pop();
960
+ if (!cur) break;
961
+ if (cur.depth > maxDepth) continue;
962
+ let entries;
963
+ try {
964
+ entries = fs5.readdirSync(cur.dir, { withFileTypes: true });
965
+ } catch {
966
+ continue;
967
+ }
968
+ for (const ent of entries) {
969
+ if (out.length >= limit) break;
970
+ const full = path5.join(cur.dir, ent.name);
971
+ if (ent.isFile() && ent.name.includes(".tmp.") && ent.name.endsWith(".cjs")) {
972
+ if (ent.name.includes(".config.")) out.push(full);
973
+ continue;
974
+ }
975
+ if (!ent.isDirectory()) continue;
976
+ if (ignored.has(ent.name)) continue;
977
+ if (ent.name.startsWith(".") && cur.depth < 2) continue;
978
+ if (ent.isSymbolicLink()) continue;
979
+ if (cur.depth + 1 <= maxDepth) stack.push({ dir: full, depth: cur.depth + 1 });
980
+ }
981
+ }
982
+ return out;
983
+ }
815
984
 
816
985
  // src/commands/find.ts
817
- import * as fs7 from "node:fs";
818
- import os4 from "node:os";
819
- import path8 from "node:path";
986
+ import * as fs9 from "node:fs";
987
+ import os5 from "node:os";
988
+ import path9 from "node:path";
820
989
 
821
990
  // src/config/loader.ts
822
- import * as fs6 from "node:fs";
823
- import os3 from "node:os";
824
- import path7 from "node:path";
991
+ import * as fs8 from "node:fs";
992
+ import os4 from "node:os";
993
+ import path8 from "node:path";
825
994
 
826
995
  // src/util/cron.ts
827
996
  var FIELD_RANGES = [
@@ -940,19 +1109,19 @@ function parseMemory(v) {
940
1109
  }
941
1110
 
942
1111
  // src/util/portable.ts
943
- import * as fs4 from "node:fs";
944
- import os2 from "node:os";
945
- import path5 from "node:path";
1112
+ import * as fs6 from "node:fs";
1113
+ import os3 from "node:os";
1114
+ import path6 from "node:path";
946
1115
  function isDir(p) {
947
1116
  try {
948
- return fs4.statSync(p).isDirectory();
1117
+ return fs6.statSync(p).isDirectory();
949
1118
  } catch {
950
1119
  return false;
951
1120
  }
952
1121
  }
953
1122
  function isFile(p) {
954
1123
  try {
955
- return fs4.statSync(p).isFile();
1124
+ return fs6.statSync(p).isFile();
956
1125
  } catch {
957
1126
  return false;
958
1127
  }
@@ -974,18 +1143,18 @@ function searchByBasename(root, basename, maxDepth = 4) {
974
1143
  if (cur.depth > maxDepth) continue;
975
1144
  let entries;
976
1145
  try {
977
- entries = fs4.readdirSync(cur.dir, { withFileTypes: true });
1146
+ entries = fs6.readdirSync(cur.dir, { withFileTypes: true });
978
1147
  } catch {
979
1148
  continue;
980
1149
  }
981
1150
  for (const e of entries) {
982
- const full = path5.join(cur.dir, e.name);
1151
+ const full = path6.join(cur.dir, e.name);
983
1152
  if (e.isFile() && e.name === basename) return full;
984
1153
  if (e.isDirectory() && !e.isSymbolicLink()) {
985
1154
  if (e.name === "node_modules" || e.name === ".git") continue;
986
1155
  const real = (() => {
987
1156
  try {
988
- return fs4.realpathSync(full);
1157
+ return fs6.realpathSync(full);
989
1158
  } catch {
990
1159
  return full;
991
1160
  }
@@ -1003,20 +1172,20 @@ function fixCwd(staleCwd, sourceDir) {
1003
1172
  const candidates = [];
1004
1173
  const tail = stripHomePrefix(staleCwd);
1005
1174
  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);
1175
+ candidates.push(path6.join(os3.homedir(), tail));
1176
+ candidates.push(path6.join(path6.dirname(os3.homedir()), tail));
1177
+ candidates.push(path6.join(path6.dirname(sourceDir), tail));
1178
+ if (isDir(sourceDir) && !staleCwd.startsWith(os3.homedir())) candidates.push(sourceDir);
1010
1179
  }
1011
- const proj = path5.basename(sourceDir);
1180
+ const proj = path6.basename(sourceDir);
1012
1181
  if (proj) {
1013
- const marker = `${path5.sep}${proj}${path5.sep}`;
1182
+ const marker = `${path6.sep}${proj}${path6.sep}`;
1014
1183
  const altMarker = `/${proj}/`;
1015
1184
  const idx = staleCwd.includes(marker) ? staleCwd.lastIndexOf(marker) : staleCwd.lastIndexOf(altMarker);
1016
1185
  if (idx >= 0) {
1017
1186
  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));
1187
+ candidates.push(path6.join(sourceDir, suffix));
1188
+ candidates.push(path6.join(path6.dirname(sourceDir), proj, suffix));
1020
1189
  }
1021
1190
  }
1022
1191
  for (const c of candidates) {
@@ -1031,33 +1200,33 @@ function fixCwd(staleCwd, sourceDir) {
1031
1200
  }
1032
1201
  function fixScript(staleScript, cwd, sourceDir) {
1033
1202
  if (isFile(staleScript)) return { path: staleScript, fixed: false };
1034
- const base = path5.basename(staleScript);
1203
+ const base = path6.basename(staleScript);
1035
1204
  const candidates = [];
1036
- candidates.push(path5.join(cwd, base));
1037
- candidates.push(path5.join(sourceDir, base));
1205
+ candidates.push(path6.join(cwd, base));
1206
+ candidates.push(path6.join(sourceDir, base));
1038
1207
  const tail = stripHomePrefix(staleScript);
1039
1208
  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));
1209
+ candidates.push(path6.join(os3.homedir(), tail));
1210
+ candidates.push(path6.join(path6.dirname(os3.homedir()), tail));
1211
+ candidates.push(path6.join(sourceDir, tail));
1043
1212
  const tailParts = tail.split("/");
1044
- if (tailParts.length > 1) candidates.push(path5.join(sourceDir, tailParts.slice(1).join("/")));
1213
+ if (tailParts.length > 1) candidates.push(path6.join(sourceDir, tailParts.slice(1).join("/")));
1045
1214
  }
1046
- const proj = path5.basename(sourceDir);
1215
+ const proj = path6.basename(sourceDir);
1047
1216
  if (proj) {
1048
1217
  const marker = `/${proj}/`;
1049
1218
  const idx = staleScript.lastIndexOf(marker);
1050
1219
  if (idx >= 0) {
1051
1220
  const suffix = staleScript.slice(idx + proj.length + 2);
1052
- candidates.push(path5.join(sourceDir, suffix));
1053
- candidates.push(path5.join(cwd, suffix));
1221
+ candidates.push(path6.join(sourceDir, suffix));
1222
+ candidates.push(path6.join(cwd, suffix));
1054
1223
  }
1055
1224
  }
1056
1225
  const parts = staleScript.split(/[/\\]/).filter(Boolean);
1057
1226
  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));
1227
+ const lastTwo = parts.slice(-2).join(path6.sep);
1228
+ candidates.push(path6.join(sourceDir, lastTwo));
1229
+ candidates.push(path6.join(cwd, lastTwo));
1061
1230
  }
1062
1231
  for (const c of candidates) {
1063
1232
  if (c && isFile(c))
@@ -1137,9 +1306,17 @@ var LATER_KEYS = {
1137
1306
  uid: "M6",
1138
1307
  gid: "M6"
1139
1308
  };
1140
- var CONFIG_FILE_RE = /^(?:(?:ecosystem|pm2|relife2)[\w.-]*|[\w.-]*?)\.config\.(?:c?js|mjs|json|ts)$/i;
1309
+ var RELIFE2_CONFIG_RE = /^(?:ecosystem|pm2|relife2)[\w.-]*\.config\.(?:c?js|mjs|json|ts)$/i;
1310
+ var ANY_CONFIG_RE = /^[\w.-]*\.config\.(?:c?js|mjs|json|ts)$/i;
1141
1311
  function looksLikeConfigFile(file) {
1142
- return CONFIG_FILE_RE.test(path7.basename(file));
1312
+ const base = path8.basename(file);
1313
+ if (base.includes(".tmp.")) return false;
1314
+ return ANY_CONFIG_RE.test(base);
1315
+ }
1316
+ function isRelife2ConfigFile(file) {
1317
+ const base = path8.basename(file);
1318
+ if (base.includes(".tmp.")) return false;
1319
+ return RELIFE2_CONFIG_RE.test(base);
1143
1320
  }
1144
1321
  function findDefaultConfig(cwd) {
1145
1322
  const candidates = [
@@ -1156,16 +1333,16 @@ function findDefaultConfig(cwd) {
1156
1333
  "pm2.config.cjs"
1157
1334
  ];
1158
1335
  for (const name of candidates) {
1159
- const full = path7.join(cwd, name);
1160
- if (fs6.existsSync(full)) return full;
1336
+ const full = path8.join(cwd, name);
1337
+ if (fs8.existsSync(full)) return full;
1161
1338
  }
1162
1339
  return null;
1163
1340
  }
1164
1341
  async function loadConfigFile(filePath, opts) {
1165
- const ext = path7.extname(filePath).toLowerCase();
1342
+ const ext = path8.extname(filePath).toLowerCase();
1166
1343
  let exported;
1167
1344
  if (ext === ".json") {
1168
- exported = JSON.parse(fs6.readFileSync(filePath, "utf8"));
1345
+ exported = JSON.parse(fs8.readFileSync(filePath, "utf8"));
1169
1346
  } else if (ext === ".ts") {
1170
1347
  exported = await loadTsFile(filePath);
1171
1348
  } else {
@@ -1175,7 +1352,7 @@ async function loadConfigFile(filePath, opts) {
1175
1352
  }
1176
1353
  if (exported instanceof Promise) exported = await exported;
1177
1354
  }
1178
- return normalizeExported(exported, path7.dirname(filePath), opts);
1355
+ return normalizeExported(exported, path8.dirname(filePath), opts);
1179
1356
  }
1180
1357
  async function loadTsFile(file) {
1181
1358
  try {
@@ -1184,16 +1361,19 @@ async function loadTsFile(file) {
1184
1361
  const mod = await import(url);
1185
1362
  return mod.default ?? mod;
1186
1363
  } catch {
1187
- const raw = fs6.readFileSync(file, "utf8");
1364
+ const raw = fs8.readFileSync(file, "utf8");
1188
1365
  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`;
1366
+ const tmp = path8.join(
1367
+ os4.tmpdir(),
1368
+ `relife2-${path8.basename(file).replace(/[^a-zA-Z0-9._-]/g, "_")}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.tmp.cjs`
1369
+ );
1190
1370
  try {
1191
- fs6.writeFileSync(tmp, stripped, "utf8");
1371
+ fs8.writeFileSync(tmp, stripped, "utf8");
1192
1372
  const { loadJsFile: lf } = await Promise.resolve().then(() => (init_jsloader(), jsloader_exports));
1193
1373
  return await lf(tmp);
1194
1374
  } finally {
1195
1375
  try {
1196
- fs6.unlinkSync(tmp);
1376
+ fs8.unlinkSync(tmp);
1197
1377
  } catch {
1198
1378
  }
1199
1379
  }
@@ -1249,20 +1429,20 @@ function normalizeApp(raw, sourceDir, warnings, index, envName) {
1249
1429
  const instances = parseInstances(input.instances, index);
1250
1430
  const execMode = parseExecMode(input.exec_mode, index);
1251
1431
  const rawCwd = typeof input.cwd === "string" ? input.cwd : void 0;
1252
- const cwdRaw = path7.resolve(sourceDir, rawCwd ?? ".");
1432
+ const cwdRaw = path8.resolve(sourceDir, rawCwd ?? ".");
1253
1433
  const cwdFix = fixCwd(cwdRaw, sourceDir);
1254
1434
  const cwd = cwdFix.path;
1255
1435
  if (cwdFix.fixed && cwdFix.reason) warnings.push(cwdFix.reason);
1256
1436
  const hasSep = scriptRaw.includes("/") || scriptRaw.includes("\\");
1257
1437
  const isJsExt = /\.(?:c?js|mjs|ts|mts|cts)$/i.test(scriptRaw);
1258
- const scriptRawResolved = hasSep || isJsExt ? path7.resolve(cwd, scriptRaw) : scriptRaw;
1438
+ const scriptRawResolved = hasSep || isJsExt ? path8.resolve(cwd, scriptRaw) : scriptRaw;
1259
1439
  let script = scriptRawResolved;
1260
- if (path7.isAbsolute(scriptRawResolved)) {
1440
+ if (path8.isAbsolute(scriptRawResolved)) {
1261
1441
  const f = fixScript(scriptRawResolved, cwd, sourceDir);
1262
1442
  if (f.fixed) {
1263
1443
  warnings.push(f.reason ?? `script '${scriptRawResolved}' \u2192 '${f.path}'`);
1264
1444
  script = f.path;
1265
- } else if (!fs6.existsSync(scriptRawResolved) && f.reason) {
1445
+ } else if (!fs8.existsSync(scriptRawResolved) && f.reason) {
1266
1446
  warnings.push(f.reason);
1267
1447
  }
1268
1448
  }
@@ -1279,11 +1459,11 @@ function normalizeApp(raw, sourceDir, warnings, index, envName) {
1279
1459
  }
1280
1460
  const envFile = typeof input.env_file === "string" && input.env_file !== "" ? input.env_file : void 0;
1281
1461
  if (envFile !== void 0) {
1282
- const envPath = path7.resolve(cwd, envFile);
1462
+ const envPath = path8.resolve(cwd, envFile);
1283
1463
  let parsed = null;
1284
- if (fs6.existsSync(envPath) && fs6.statSync(envPath).isFile()) {
1464
+ if (fs8.existsSync(envPath) && fs8.statSync(envPath).isFile()) {
1285
1465
  try {
1286
- parsed = parseDotenv(fs6.readFileSync(envPath, "utf8"));
1466
+ parsed = parseDotenv(fs8.readFileSync(envPath, "utf8"));
1287
1467
  } catch (err) {
1288
1468
  warnings.push(
1289
1469
  `apps[${index}] env_file '${envFile}' could not be read: ${err.message}`
@@ -1406,7 +1586,7 @@ function parseWatchField(v, field, index) {
1406
1586
  }
1407
1587
  function parseInstances(v, index) {
1408
1588
  if (v === void 0 || v === null) return 1;
1409
- if (v === "max" || v === "MAX") return os3.cpus().length || 1;
1589
+ if (v === "max" || v === "MAX") return os4.cpus().length || 1;
1410
1590
  const n = Number(v);
1411
1591
  if (!Number.isFinite(n) || n < 1) {
1412
1592
  throw new ConfigError(`apps[${index}] 'instances' must be a positive number or 'max'`);
@@ -1421,7 +1601,7 @@ function parseExecMode(v, index) {
1421
1601
  throw new ConfigError(`apps[${index}] 'exec_mode' must be 'fork' or 'cluster'`);
1422
1602
  }
1423
1603
  function defaultName(script) {
1424
- const base = path7.basename(script);
1604
+ const base = path8.basename(script);
1425
1605
  return base.replace(/\.(?:c?js|mjs|ts|mts|cts)$/i, "") || base;
1426
1606
  }
1427
1607
  function normalizeSingleApp(input, sourceDir, envName) {
@@ -1469,7 +1649,7 @@ function findConfigs(root, maxDepth) {
1469
1649
  if (depth > maxDepth) continue;
1470
1650
  let real;
1471
1651
  try {
1472
- real = fs7.realpathSync(dir);
1652
+ real = fs9.realpathSync(dir);
1473
1653
  } catch {
1474
1654
  continue;
1475
1655
  }
@@ -1477,13 +1657,13 @@ function findConfigs(root, maxDepth) {
1477
1657
  visited.add(real);
1478
1658
  let entries;
1479
1659
  try {
1480
- entries = fs7.readdirSync(dir, { withFileTypes: true });
1660
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
1481
1661
  } catch {
1482
1662
  continue;
1483
1663
  }
1484
1664
  for (const ent of entries) {
1485
- const full = path8.join(dir, ent.name);
1486
- if (ent.isFile() && looksLikeConfigFile(ent.name)) {
1665
+ const full = path9.join(dir, ent.name);
1666
+ if (ent.isFile() && isRelife2ConfigFile(ent.name)) {
1487
1667
  out.push(full);
1488
1668
  continue;
1489
1669
  }
@@ -1512,8 +1692,8 @@ async function enrich(found) {
1512
1692
  return entries;
1513
1693
  }
1514
1694
  function relToRoot(p, root) {
1515
- const rel = path8.relative(root, p);
1516
- return rel === "" ? path8.basename(p) : rel;
1695
+ const rel = path9.relative(root, p);
1696
+ return rel === "" ? path9.basename(p) : rel;
1517
1697
  }
1518
1698
  async function run5(args) {
1519
1699
  const { positionals, opts } = parseCliArgs(args, VALUE_OPTS);
@@ -1523,12 +1703,12 @@ async function run5(args) {
1523
1703
  let root = opts.get("root");
1524
1704
  if (root === void 0) {
1525
1705
  if (allFlag) {
1526
- root = process.platform === "win32" ? path8.parse(process.cwd()).root : "/";
1706
+ root = process.platform === "win32" ? path9.parse(process.cwd()).root : "/";
1527
1707
  } else {
1528
- root = os4.homedir() || process.cwd();
1708
+ root = os5.homedir() || process.cwd();
1529
1709
  }
1530
1710
  }
1531
- root = path8.resolve(root);
1711
+ root = path9.resolve(root);
1532
1712
  let depth = 6;
1533
1713
  const depthRaw = opts.get("depth");
1534
1714
  if (depthRaw !== void 0) {
@@ -1540,7 +1720,7 @@ async function run5(args) {
1540
1720
  depth = Math.round(n);
1541
1721
  }
1542
1722
  if (allFlag) depth = Math.max(depth, 8);
1543
- if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) {
1723
+ if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) {
1544
1724
  console.error(`find: root '${root}' does not exist or is not a directory`);
1545
1725
  return 1;
1546
1726
  }
@@ -1631,10 +1811,10 @@ Starting ${toStart.length} config(s) not yet running\u2026
1631
1811
  const res = await client.call("start", {
1632
1812
  target: { type: "config", path: r.path }
1633
1813
  });
1634
- for (const w of res.warnings ?? []) console.error(`warning ${path8.basename(r.path)}: ${w}`);
1814
+ for (const w of res.warnings ?? []) console.error(`warning ${path9.basename(r.path)}: ${w}`);
1635
1815
  for (const a of res.apps) {
1636
1816
  if (a.status === "errored") {
1637
- console.error(`x ${a.name} (${path8.basename(r.path)}): ${a.message ?? "failed"}`);
1817
+ console.error(`x ${a.name} (${path9.basename(r.path)}): ${a.message ?? "failed"}`);
1638
1818
  failed++;
1639
1819
  } else {
1640
1820
  console.log(
@@ -1644,7 +1824,7 @@ Starting ${toStart.length} config(s) not yet running\u2026
1644
1824
  }
1645
1825
  }
1646
1826
  } catch (err) {
1647
- console.error(`x ${path8.basename(r.path)}: ${err.message}`);
1827
+ console.error(`x ${path9.basename(r.path)}: ${err.message}`);
1648
1828
  failed++;
1649
1829
  }
1650
1830
  }
@@ -1678,9 +1858,9 @@ async function run6(args) {
1678
1858
  }
1679
1859
 
1680
1860
  // src/commands/fromCaddy.ts
1681
- import * as fs8 from "node:fs";
1682
- import os5 from "node:os";
1683
- import path9 from "node:path";
1861
+ import * as fs10 from "node:fs";
1862
+ import os6 from "node:os";
1863
+ import path10 from "node:path";
1684
1864
  var LOOPBACK_RE = /(?:127\.0\.0\.1|localhost|::1):(\d{2,5})/g;
1685
1865
  var ENTRY_PATTERNS = [
1686
1866
  ".output/server/index.mjs",
@@ -1853,37 +2033,37 @@ function parseCaddyfile(content) {
1853
2033
  }
1854
2034
  function findEntry(dir) {
1855
2035
  for (const pat of ENTRY_PATTERNS) {
1856
- const p = path9.join(dir, pat);
1857
- if (fs8.existsSync(p)) return { script: pat, exists: true };
2036
+ const p = path10.join(dir, pat);
2037
+ if (fs10.existsSync(p)) return { script: pat, exists: true };
1858
2038
  }
1859
2039
  return null;
1860
2040
  }
1861
2041
  function guessDir(site, _port, hint, caddyfileDir) {
1862
2042
  const warnings = [];
1863
2043
  if (hint?.dir) {
1864
- const d = path9.isAbsolute(hint.dir) ? hint.dir : path9.resolve(caddyfileDir, hint.dir);
2044
+ const d = path10.isAbsolute(hint.dir) ? hint.dir : path10.resolve(caddyfileDir, hint.dir);
1865
2045
  return { dir: d, warnings };
1866
2046
  }
1867
2047
  const searchRoots = [];
1868
- const homedir = os5.homedir();
2048
+ const homedir = os6.homedir();
1869
2049
  if (homedir) searchRoots.push(homedir);
1870
2050
  if (caddyfileDir) searchRoots.push(caddyfileDir);
1871
- if (caddyfileDir) searchRoots.push(path9.resolve(caddyfileDir, ".."));
2051
+ if (caddyfileDir) searchRoots.push(path10.resolve(caddyfileDir, ".."));
1872
2052
  const uniqRoots = [...new Set(searchRoots)].filter(Boolean);
1873
2053
  const siteName = sanitizeName(site);
1874
2054
  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));
2055
+ if (homedir) candidates.push(path10.join(homedir, siteName));
2056
+ if (homedir) candidates.push(path10.join(homedir, siteName.split("-")[0]));
2057
+ for (const r of uniqRoots) candidates.push(path10.join(r, siteName));
1878
2058
  const rawDir = site.split(".")[0].replace(/[^a-zA-Z0-9_-]/g, "");
1879
- if (rawDir && homedir) candidates.push(path9.join(homedir, rawDir));
2059
+ if (rawDir && homedir) candidates.push(path10.join(homedir, rawDir));
1880
2060
  for (const cand of candidates) {
1881
- if (fs8.existsSync(cand) && fs8.statSync(cand).isDirectory()) {
2061
+ if (fs10.existsSync(cand) && fs10.statSync(cand).isDirectory()) {
1882
2062
  const e = findEntry(cand);
1883
2063
  if (e) return { dir: cand, warnings };
1884
2064
  }
1885
2065
  }
1886
- const fallback = homedir ? path9.join(homedir, siteName) : path9.join(caddyfileDir || process.cwd(), siteName);
2066
+ const fallback = homedir ? path10.join(homedir, siteName) : path10.join(caddyfileDir || process.cwd(), siteName);
1887
2067
  warnings.push(
1888
2068
  `no existing dir found for ${site} \u2192 using fallback ${fallback} (create or set via '# relife2: dir=...')`
1889
2069
  );
@@ -1915,15 +2095,15 @@ async function run7(args) {
1915
2095
  caddyfilePath ? null : "./Caddyfile",
1916
2096
  caddyfilePath ? null : "Caddyfile",
1917
2097
  caddyfilePath ? null : "/etc/caddy/Caddyfile",
1918
- caddyfilePath ? null : path9.join(os5.homedir(), "Caddyfile")
2098
+ caddyfilePath ? null : path10.join(os6.homedir(), "Caddyfile")
1919
2099
  ].filter(Boolean);
1920
2100
  let resolvedCaddy = null;
1921
2101
  let content = null;
1922
2102
  for (const cand of candidates) {
1923
- const p = path9.resolve(cand);
1924
- if (fs8.existsSync(p)) {
2103
+ const p = path10.resolve(cand);
2104
+ if (fs10.existsSync(p)) {
1925
2105
  resolvedCaddy = p;
1926
- content = fs8.readFileSync(p, "utf8");
2106
+ content = fs10.readFileSync(p, "utf8");
1927
2107
  break;
1928
2108
  }
1929
2109
  }
@@ -1934,7 +2114,7 @@ async function run7(args) {
1934
2114
  );
1935
2115
  return 1;
1936
2116
  }
1937
- const caddyfileDir = path9.dirname(resolvedCaddy);
2117
+ const caddyfileDir = path10.dirname(resolvedCaddy);
1938
2118
  const { sites } = parseCaddyfile(content);
1939
2119
  if (sites.length === 0) {
1940
2120
  console.error(`from-caddy: no sites found in ${resolvedCaddy}`);
@@ -1982,7 +2162,7 @@ async function run7(args) {
1982
2162
  let relScript = "";
1983
2163
  if (entry) {
1984
2164
  relScript = entry;
1985
- exists = fs8.existsSync(path9.isAbsolute(entry) ? entry : path9.join(dir, entry));
2165
+ exists = fs10.existsSync(path10.isAbsolute(entry) ? entry : path10.join(dir, entry));
1986
2166
  if (!exists) warnings.push(`hint script '${entry}' for ${name} not found under ${dir}`);
1987
2167
  } else {
1988
2168
  const found = findEntry(dir);
@@ -2000,7 +2180,7 @@ async function run7(args) {
2000
2180
  let interpreter = site.hint?.interpreter;
2001
2181
  if (!interpreter) {
2002
2182
  if (relScript.endsWith(".mjs") || relScript.endsWith(".ts")) {
2003
- const hasBun2 = fs8.existsSync(path9.join(dir, "bun.lockb")) || fs8.existsSync(path9.join(dir, "bun.lock"));
2183
+ const hasBun2 = fs10.existsSync(path10.join(dir, "bun.lockb")) || fs10.existsSync(path10.join(dir, "bun.lock"));
2004
2184
  if (hasBun2) interpreter = "bun";
2005
2185
  }
2006
2186
  }
@@ -2081,7 +2261,7 @@ Would write ${planned.length} app(s) to ${outPath ?? "relife2.config.cjs (dry-ru
2081
2261
  console.log(`Run without --dry-run to generate.`);
2082
2262
  return 0;
2083
2263
  }
2084
- const resolvedOut = path9.resolve(outPath ?? "relife2.config.cjs");
2264
+ const resolvedOut = path10.resolve(outPath ?? "relife2.config.cjs");
2085
2265
  const appsCode = planned.map((p) => {
2086
2266
  const interp = p._interpreter;
2087
2267
  const isCaddy = p.name === "caddy";
@@ -2138,7 +2318,7 @@ ${appsCode}
2138
2318
  ]
2139
2319
  };
2140
2320
  `;
2141
- fs8.writeFileSync(resolvedOut, fileContent, "utf8");
2321
+ fs10.writeFileSync(resolvedOut, fileContent, "utf8");
2142
2322
  console.log(
2143
2323
  `from-caddy: parsed ${sites.length} site(s), ${planned.length} upstream(s) from ${resolvedCaddy}`
2144
2324
  );
@@ -2156,7 +2336,7 @@ Warnings:`);
2156
2336
  console.log(`
2157
2337
  Wrote ${resolvedOut} (${planned.length} app(s))`);
2158
2338
  console.log(
2159
- `Next: relife2 inspect-config ${path9.basename(resolvedOut)} \u2192 relife2 start ${path9.basename(resolvedOut)}`
2339
+ `Next: relife2 inspect-config ${path10.basename(resolvedOut)} \u2192 relife2 start ${path10.basename(resolvedOut)}`
2160
2340
  );
2161
2341
  return 0;
2162
2342
  }
@@ -2185,7 +2365,7 @@ function printUsage2() {
2185
2365
  }
2186
2366
 
2187
2367
  // src/version.ts
2188
- var VERSION = "1.0.1";
2368
+ var VERSION = "1.0.3";
2189
2369
 
2190
2370
  // src/commands/help.ts
2191
2371
  var HELP_TEXT = `relife2 ${VERSION} \u2014 process manager for Node.js & Bun apps (PM2 alternative, done right)
@@ -2211,7 +2391,7 @@ Commands:
2211
2391
  flush [name|all] Empty the app log file(s)
2212
2392
  rotate [--max-size <bytes>] [--retain <n>] [name|all]
2213
2393
  Rotate log files exceeding the size threshold (M3)
2214
- doctor Run integrity checks on the running daemon (M3)
2394
+ doctor [--fix] Run integrity checks on the running daemon (M3) \u2014 --fix heals stale lock + *.tmp.cjs
2215
2395
  metrics Show daemon self-metrics + per-app summary (M3)
2216
2396
  monit [--once] Live dashboard (M6): cpu/mem/uptime per app, q to quit
2217
2397
  find [start] [--root <dir>] [--depth <n>] [--json] [--all]
@@ -2243,13 +2423,13 @@ function printHelp() {
2243
2423
  }
2244
2424
 
2245
2425
  // src/commands/importpm2.ts
2246
- import * as fs9 from "node:fs";
2247
- import os6 from "node:os";
2248
- import path10 from "node:path";
2426
+ import * as fs11 from "node:fs";
2427
+ import os7 from "node:os";
2428
+ import path11 from "node:path";
2249
2429
  async function run8(args) {
2250
2430
  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)) {
2431
+ const dumpPath = target !== void 0 ? path11.resolve(target) : path11.join(os7.homedir(), ".pm2", "dump.pm2");
2432
+ if (!fs11.existsSync(dumpPath)) {
2253
2433
  console.error(`import-pm2: dump file not found: ${dumpPath}`);
2254
2434
  console.error(
2255
2435
  " pm2 saves to ~/.pm2/dump.pm2 via `pm2 save` \u2014 run that first, or pass a path:"
@@ -2259,7 +2439,7 @@ async function run8(args) {
2259
2439
  }
2260
2440
  let raw;
2261
2441
  try {
2262
- raw = JSON.parse(fs9.readFileSync(dumpPath, "utf8"));
2442
+ raw = JSON.parse(fs11.readFileSync(dumpPath, "utf8"));
2263
2443
  } catch (err) {
2264
2444
  console.error(`import-pm2: cannot parse ${dumpPath}: ${err.message}`);
2265
2445
  return 1;
@@ -2302,11 +2482,11 @@ async function run8(args) {
2302
2482
  }
2303
2483
  ]
2304
2484
  };
2305
- const tmpPath = path10.join(
2306
- os6.tmpdir(),
2485
+ const tmpPath = path11.join(
2486
+ os7.tmpdir(),
2307
2487
  `relife2-import-${Date.now()}-${Math.random().toString(36).slice(2)}.cjs`
2308
2488
  );
2309
- fs9.writeFileSync(tmpPath, `module.exports = ${JSON.stringify(tmpConfig)}`, "utf8");
2489
+ fs11.writeFileSync(tmpPath, `module.exports = ${JSON.stringify(tmpConfig)}`, "utf8");
2310
2490
  try {
2311
2491
  const res = await client.call("start", {
2312
2492
  target: { type: "config", path: tmpPath }
@@ -2322,7 +2502,7 @@ async function run8(args) {
2322
2502
  console.error(`import ${name}: ${err.message}`);
2323
2503
  } finally {
2324
2504
  try {
2325
- fs9.unlinkSync(tmpPath);
2505
+ fs11.unlinkSync(tmpPath);
2326
2506
  } catch {
2327
2507
  }
2328
2508
  }
@@ -2339,7 +2519,7 @@ imported ${imported} app(s) from ${dumpPath}`);
2339
2519
  }
2340
2520
 
2341
2521
  // src/commands/inspectconfig.ts
2342
- import path11 from "node:path";
2522
+ import path12 from "node:path";
2343
2523
  async function run9(args) {
2344
2524
  const { positionals } = parseCliArgs(args);
2345
2525
  const file = positionals[0];
@@ -2349,7 +2529,7 @@ async function run9(args) {
2349
2529
  }
2350
2530
  let loaded;
2351
2531
  try {
2352
- loaded = await loadConfigFile(path11.resolve(file));
2532
+ loaded = await loadConfigFile(path12.resolve(file));
2353
2533
  } catch (err) {
2354
2534
  console.error(`relife2: ${err.message}`);
2355
2535
  return 1;
@@ -2418,7 +2598,7 @@ function printTable(rows) {
2418
2598
  }
2419
2599
 
2420
2600
  // src/commands/logs.ts
2421
- import * as fs10 from "node:fs";
2601
+ import * as fs12 from "node:fs";
2422
2602
  var VALUE_OPTS2 = /* @__PURE__ */ new Set(["lines"]);
2423
2603
  async function run12(args) {
2424
2604
  const { positionals, opts } = parseCliArgs(args, VALUE_OPTS2);
@@ -2458,7 +2638,7 @@ function parseLines(v) {
2458
2638
  }
2459
2639
  function printTail(file, lines) {
2460
2640
  try {
2461
- const text = fs10.readFileSync(file, "utf8");
2641
+ const text = fs12.readFileSync(file, "utf8");
2462
2642
  const all = text.split(/\r?\n/);
2463
2643
  if (all.length > 0 && all[all.length - 1] === "") all.pop();
2464
2644
  const slice = all.slice(Math.max(0, all.length - lines));
@@ -2484,13 +2664,13 @@ async function followFiles(files) {
2484
2664
  const from = positions.get(f) ?? 0;
2485
2665
  if (size > from) {
2486
2666
  try {
2487
- const fd = fs10.openSync(f, "r");
2667
+ const fd = fs12.openSync(f, "r");
2488
2668
  try {
2489
2669
  const buf = Buffer.alloc(size - from);
2490
- fs10.readSync(fd, buf, 0, buf.length, from);
2670
+ fs12.readSync(fd, buf, 0, buf.length, from);
2491
2671
  process.stdout.write(buf);
2492
2672
  } finally {
2493
- fs10.closeSync(fd);
2673
+ fs12.closeSync(fd);
2494
2674
  }
2495
2675
  } catch {
2496
2676
  }
@@ -2501,7 +2681,7 @@ async function followFiles(files) {
2501
2681
  }
2502
2682
  function fileSize(file) {
2503
2683
  try {
2504
- return fs10.statSync(file).size;
2684
+ return fs12.statSync(file).size;
2505
2685
  } catch {
2506
2686
  return 0;
2507
2687
  }
@@ -2791,7 +2971,7 @@ async function run20(_args) {
2791
2971
  }
2792
2972
 
2793
2973
  // src/commands/start.ts
2794
- import path12 from "node:path";
2974
+ import path13 from "node:path";
2795
2975
  var VALUE_OPTS4 = /* @__PURE__ */ new Set([
2796
2976
  "name",
2797
2977
  "env",
@@ -2840,7 +3020,7 @@ async function run21(args) {
2840
3020
  const res = await client.call("start", {
2841
3021
  target: {
2842
3022
  type: isConfig ? "config" : "script",
2843
- path: path12.resolve(target),
3023
+ path: path13.resolve(target),
2844
3024
  name,
2845
3025
  args: passthrough.length > 0 ? passthrough : void 0,
2846
3026
  envName: envName ?? void 0,
@@ -2867,11 +3047,11 @@ async function run21(args) {
2867
3047
 
2868
3048
  // src/commands/startup.ts
2869
3049
  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";
3050
+ import * as fs13 from "node:fs";
3051
+ import os8 from "node:os";
3052
+ import path14 from "node:path";
2873
3053
  import { fileURLToPath } from "node:url";
2874
- var SYSTEMD_USER_DIR = path13.join(os7.homedir(), ".config", "systemd", "user");
3054
+ var SYSTEMD_USER_DIR = path14.join(os8.homedir(), ".config", "systemd", "user");
2875
3055
  var UNIT_NAME = "relife2.service";
2876
3056
  function getRelife2Bin() {
2877
3057
  const which = spawnSync2(process.platform === "win32" ? "where" : "which", ["relife2"], {
@@ -2889,13 +3069,13 @@ function getRelife2Bin() {
2889
3069
  }
2890
3070
  try {
2891
3071
  const thisFile = fileURLToPath(import.meta.url);
2892
- const cliPath = path13.resolve(path13.dirname(thisFile), "..", "cli.js");
2893
- if (fs11.existsSync(cliPath)) {
3072
+ const cliPath = path14.resolve(path14.dirname(thisFile), "..", "cli.js");
3073
+ if (fs13.existsSync(cliPath)) {
2894
3074
  return `node ${cliPath}`;
2895
3075
  }
2896
3076
  } catch {
2897
3077
  }
2898
- return `node ${path13.resolve(arg1 ?? process.cwd())}`;
3078
+ return `node ${path14.resolve(arg1 ?? process.cwd())}`;
2899
3079
  }
2900
3080
  function generateUnit(bin) {
2901
3081
  const envHint = process.env.RELIFE2_DIR !== void 0 ? `
@@ -2916,6 +3096,49 @@ Environment=RELIFE2_DAEMON=1${envHint}
2916
3096
  WantedBy=default.target
2917
3097
  `;
2918
3098
  }
3099
+ function isLingerEnabled(user) {
3100
+ try {
3101
+ const out = execSync(`loginctl show-user ${user} -p Linger --value`, {
3102
+ encoding: "utf8",
3103
+ stdio: ["ignore", "pipe", "ignore"],
3104
+ timeout: 3e3
3105
+ });
3106
+ const v = out.trim().toLowerCase();
3107
+ if (v === "yes") return true;
3108
+ if (v === "no") return false;
3109
+ } catch {
3110
+ }
3111
+ try {
3112
+ const out = execSync(`loginctl show-user ${user}`, {
3113
+ encoding: "utf8",
3114
+ stdio: ["ignore", "pipe", "ignore"],
3115
+ timeout: 3e3
3116
+ });
3117
+ if (/Linger=yes/i.test(out)) return true;
3118
+ if (/Linger=no/i.test(out)) return false;
3119
+ } catch {
3120
+ }
3121
+ return null;
3122
+ }
3123
+ function tryEnableLinger(user) {
3124
+ const attempts = [
3125
+ { cmd: `loginctl enable-linger ${user}`, label: "loginctl" },
3126
+ { cmd: `sudo -n loginctl enable-linger ${user}`, label: "sudo -n" },
3127
+ { cmd: `sudo loginctl enable-linger ${user}`, label: "sudo" }
3128
+ ];
3129
+ for (const a of attempts) {
3130
+ try {
3131
+ execSync(a.cmd, { stdio: ["ignore", "pipe", "pipe"], timeout: 8e3 });
3132
+ const enabled = isLingerEnabled(user);
3133
+ if (enabled === true || enabled === null) {
3134
+ console.log(`enabled linger for user ${user} via ${a.label}`);
3135
+ return true;
3136
+ }
3137
+ } catch {
3138
+ }
3139
+ }
3140
+ return false;
3141
+ }
2919
3142
  async function run22(_args) {
2920
3143
  if (process.platform === "win32") {
2921
3144
  console.error(
@@ -2929,47 +3152,67 @@ async function run22(_args) {
2929
3152
  }
2930
3153
  const bin = getRelife2Bin();
2931
3154
  const unitContent = generateUnit(bin);
2932
- const unitPath = path13.join(SYSTEMD_USER_DIR, UNIT_NAME);
3155
+ const unitPath = path14.join(SYSTEMD_USER_DIR, UNIT_NAME);
2933
3156
  try {
2934
- fs11.mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
3157
+ fs13.mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
2935
3158
  } catch (err) {
2936
3159
  console.error(`cannot create ${SYSTEMD_USER_DIR}: ${err.message}`);
2937
3160
  return 1;
2938
3161
  }
2939
3162
  try {
2940
- fs11.writeFileSync(unitPath, unitContent);
3163
+ fs13.writeFileSync(unitPath, unitContent);
2941
3164
  console.log(`written systemd user unit: ${unitPath}`);
2942
3165
  } catch (err) {
2943
3166
  console.error(`cannot write ${unitPath}: ${err.message}`);
2944
3167
  return 1;
2945
3168
  }
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}`);
3169
+ const user = process.env.USER ?? process.env.LOGNAME ?? os8.userInfo().username ?? "opc";
3170
+ const lingerState = isLingerEnabled(user);
3171
+ if (lingerState === true) {
3172
+ console.log(`linger already enabled for user ${user}`);
3173
+ } else if (lingerState === false) {
3174
+ console.log(`linger is disabled for ${user} \u2014 enabling\u2026`);
3175
+ const ok = tryEnableLinger(user);
3176
+ if (!ok) {
3177
+ console.error(`warning: could not enable linger (is systemd-logind running?)`);
3178
+ console.error(`your apps will only start after you log in. Run manually:`);
3179
+ console.error(` sudo loginctl enable-linger ${user}`);
3180
+ }
3181
+ } else {
3182
+ const ok = tryEnableLinger(user);
3183
+ if (!ok) {
3184
+ console.error(`note: could not verify linger status (no logind?) \u2014 skipping`);
3185
+ }
2956
3186
  }
2957
3187
  try {
2958
- execSync("systemctl --user daemon-reload", { stdio: ["ignore", "pipe", "pipe"] });
2959
- execSync(`systemctl --user enable ${UNIT_NAME}`, { stdio: ["ignore", "pipe", "pipe"] });
3188
+ execSync("systemctl --user daemon-reload", {
3189
+ stdio: ["ignore", "pipe", "pipe"],
3190
+ timeout: 8e3
3191
+ });
3192
+ execSync(`systemctl --user enable ${UNIT_NAME}`, {
3193
+ stdio: ["ignore", "pipe", "pipe"],
3194
+ timeout: 8e3
3195
+ });
2960
3196
  console.log(`enabled and reloaded: systemctl --user enable ${UNIT_NAME}`);
3197
+ try {
3198
+ execSync(`systemctl --user start ${UNIT_NAME}`, {
3199
+ stdio: ["ignore", "pipe", "pipe"],
3200
+ timeout: 8e3
3201
+ });
3202
+ console.log(`started: systemctl --user start ${UNIT_NAME}`);
3203
+ } catch {
3204
+ }
2961
3205
  } catch (err) {
2962
3206
  console.error(
2963
3207
  `warning: could not enable unit (is systemd available?): ${err instanceof Error ? err.message : String(err)}`
2964
3208
  );
2965
3209
  console.error("run manually:");
2966
3210
  console.error(" systemctl --user daemon-reload");
2967
- console.error(` systemctl --user enable ${UNIT_NAME}`);
3211
+ console.error(` systemctl --user enable --now ${UNIT_NAME}`);
2968
3212
  }
2969
3213
  console.log("");
2970
3214
  console.log("relife2 will now start automatically on boot.");
2971
3215
  console.log(`ExecStart is: ${bin}`);
2972
- console.log("To start now, run: systemctl --user start relife2.service");
2973
3216
  console.log("To check status: systemctl --user status relife2.service");
2974
3217
  return 0;
2975
3218
  }
@@ -3165,14 +3408,14 @@ function detectRuntime() {
3165
3408
  }
3166
3409
 
3167
3410
  // src/daemon/daemon.ts
3168
- import * as fs18 from "node:fs";
3169
- import os8 from "node:os";
3170
- import path19 from "node:path";
3411
+ import * as fs20 from "node:fs";
3412
+ import os9 from "node:os";
3413
+ import path20 from "node:path";
3171
3414
  import * as zlib from "node:zlib";
3172
3415
 
3173
3416
  // src/daemon/applog.ts
3174
- import * as fs12 from "node:fs";
3175
- import path14 from "node:path";
3417
+ import * as fs14 from "node:fs";
3418
+ import path15 from "node:path";
3176
3419
 
3177
3420
  // src/util/datefmt.ts
3178
3421
  function formatDate(pattern, date = /* @__PURE__ */ new Date()) {
@@ -3197,12 +3440,12 @@ function formatDate(pattern, date = /* @__PURE__ */ new Date()) {
3197
3440
 
3198
3441
  // src/daemon/applog.ts
3199
3442
  var DEFAULT_LOG_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS Z";
3200
- var DEFAULT_LOGS_DIR = (base) => path14.join(base, "logs");
3443
+ var DEFAULT_LOGS_DIR = (base) => path15.join(base, "logs");
3201
3444
  function resolveLogPaths(app, base) {
3202
3445
  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`);
3446
+ const out = app.outFile ? path15.resolve(app.cwd, app.outFile) : path15.join(DEFAULT_LOGS_DIR(base), `${safe}.out.log`);
3204
3447
  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`);
3448
+ const err = app.errFile ? path15.resolve(app.cwd, app.errFile) : path15.join(DEFAULT_LOGS_DIR(base), `${safe}.err.log`);
3206
3449
  return { out, err };
3207
3450
  }
3208
3451
  var AppLogSink = class {
@@ -3247,8 +3490,8 @@ var AppLogSink = class {
3247
3490
  const out = `${this.prefix}${line}
3248
3491
  `;
3249
3492
  try {
3250
- ensureDirSync(path14.dirname(target));
3251
- fs12.appendFileSync(target, out);
3493
+ ensureDirSync(path15.dirname(target));
3494
+ fs14.appendFileSync(target, out);
3252
3495
  } catch {
3253
3496
  }
3254
3497
  }
@@ -3256,18 +3499,18 @@ var AppLogSink = class {
3256
3499
  };
3257
3500
 
3258
3501
  // src/daemon/logger.ts
3259
- import fs13 from "node:fs";
3260
- import path15 from "node:path";
3502
+ import fs15 from "node:fs";
3503
+ import path16 from "node:path";
3261
3504
  function createDaemonLogger(base) {
3262
- const logsDir = path15.join(base, "logs");
3505
+ const logsDir = path16.join(base, "logs");
3263
3506
  ensureDirSync(logsDir);
3264
- const mainFile = path15.join(logsDir, "daemon.log");
3507
+ const mainFile = path16.join(logsDir, "daemon.log");
3265
3508
  const outRoot = logsDir;
3266
3509
  function log(msg, extra) {
3267
3510
  const line = `${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), msg, ...extra ?? {} })}
3268
3511
  `;
3269
3512
  try {
3270
- fs13.appendFileSync(mainFile, line);
3513
+ fs15.appendFileSync(mainFile, line);
3271
3514
  } catch {
3272
3515
  }
3273
3516
  }
@@ -3276,13 +3519,13 @@ function createDaemonLogger(base) {
3276
3519
  }
3277
3520
  function appOut(name, chunk) {
3278
3521
  try {
3279
- fs13.appendFileSync(path15.join(outRoot, `${safeName(name)}.out.log`), chunk);
3522
+ fs15.appendFileSync(path16.join(outRoot, `${safeName(name)}.out.log`), chunk);
3280
3523
  } catch {
3281
3524
  }
3282
3525
  }
3283
3526
  function appErr(name, chunk) {
3284
3527
  try {
3285
- fs13.appendFileSync(path15.join(outRoot, `${safeName(name)}.err.log`), chunk);
3528
+ fs15.appendFileSync(path16.join(outRoot, `${safeName(name)}.err.log`), chunk);
3286
3529
  } catch {
3287
3530
  }
3288
3531
  }
@@ -3291,12 +3534,12 @@ function createDaemonLogger(base) {
3291
3534
 
3292
3535
  // src/daemon/pm.ts
3293
3536
  import { spawn as spawn3 } from "node:child_process";
3294
- import * as fs15 from "node:fs";
3295
- import path17 from "node:path";
3537
+ import * as fs17 from "node:fs";
3538
+ import path18 from "node:path";
3296
3539
 
3297
3540
  // src/daemon/command.ts
3298
3541
  import { existsSync as existsSync8 } from "node:fs";
3299
- import path16 from "node:path";
3542
+ import path17 from "node:path";
3300
3543
  import process2 from "node:process";
3301
3544
  var JS_RE = /\.(?:c?js|mjs|ts|mts|cts)$/i;
3302
3545
  function isJsScript(p) {
@@ -3306,10 +3549,10 @@ function isBunProject2(scriptDir) {
3306
3549
  let dir = scriptDir;
3307
3550
  const home = process2.env.HOME ?? "";
3308
3551
  while (true) {
3309
- if (existsSync8(path16.join(dir, "bun.lockb")) || existsSync8(path16.join(dir, "bun.lock"))) {
3552
+ if (existsSync8(path17.join(dir, "bun.lockb")) || existsSync8(path17.join(dir, "bun.lock"))) {
3310
3553
  return true;
3311
3554
  }
3312
- const parent = path16.dirname(dir);
3555
+ const parent = path17.dirname(dir);
3313
3556
  if (parent === dir) break;
3314
3557
  if (home !== "" && dir === home) break;
3315
3558
  dir = parent;
@@ -3345,7 +3588,7 @@ function buildSpawnSpec(app) {
3345
3588
 
3346
3589
  // src/daemon/memproc.ts
3347
3590
  import { execFileSync } from "node:child_process";
3348
- import * as fs14 from "node:fs";
3591
+ import * as fs16 from "node:fs";
3349
3592
  function processRss(pid) {
3350
3593
  if (pid <= 0) return void 0;
3351
3594
  try {
@@ -3361,7 +3604,7 @@ function processRss(pid) {
3361
3604
  }
3362
3605
  }
3363
3606
  function rssFromProc(pid) {
3364
- const status = fs14.readFileSync(`/proc/${pid}/status`, "utf8");
3607
+ const status = fs16.readFileSync(`/proc/${pid}/status`, "utf8");
3365
3608
  const match = /^VmRSS:\s+(\d+)\s+kB/m.exec(status);
3366
3609
  if (match?.[1] === void 0) return void 0;
3367
3610
  return Number(match[1]) * 1024;
@@ -3436,7 +3679,7 @@ var ProcessManager = class {
3436
3679
  });
3437
3680
  record.config.cwd = cwdFix.path;
3438
3681
  }
3439
- if (path17.isAbsolute(record.config.script) && !fs15.existsSync(record.config.script)) {
3682
+ if (path18.isAbsolute(record.config.script) && !fs17.existsSync(record.config.script)) {
3440
3683
  const sFix = fixScript(record.config.script, record.config.cwd, record.config.sourceDir);
3441
3684
  if (sFix.fixed && sFix.reason) {
3442
3685
  this.logger.log(sFix.reason);
@@ -3450,7 +3693,7 @@ var ProcessManager = class {
3450
3693
  } else if (!sFix.fixed) {
3451
3694
  this.fail(
3452
3695
  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.`
3696
+ `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
3697
  );
3455
3698
  this.store.saveSync();
3456
3699
  return;
@@ -3766,19 +4009,19 @@ var ProcessManager = class {
3766
4009
  const watch3 = record.config.watch;
3767
4010
  if (watch3 === void 0 || watch3 === false) return;
3768
4011
  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];
4012
+ const rawPaths = Array.isArray(watch3) ? watch3.map((p) => path18.resolve(record.config.cwd, p)) : [record.config.cwd];
3770
4013
  const watchers = [];
3771
4014
  for (const target of rawPaths) {
3772
4015
  try {
3773
- if (!fs15.existsSync(target)) {
4016
+ if (!fs17.existsSync(target)) {
3774
4017
  this.logger.log(`watch: path '${target}' does not exist yet; skipping`);
3775
4018
  continue;
3776
4019
  }
3777
- const stat = fs15.statSync(target);
4020
+ const stat = fs17.statSync(target);
3778
4021
  if (!stat.isDirectory()) {
3779
- const dir = path17.dirname(target);
3780
- const base = path17.basename(target);
3781
- const watcher = fs15.watch(dir, (_eventType, filename) => {
4022
+ const dir = path18.dirname(target);
4023
+ const base = path18.basename(target);
4024
+ const watcher = fs17.watch(dir, (_eventType, filename) => {
3782
4025
  if (filename !== null && filename !== base) return;
3783
4026
  this.onWatchChange(record.name, target);
3784
4027
  });
@@ -3786,7 +4029,7 @@ var ProcessManager = class {
3786
4029
  continue;
3787
4030
  }
3788
4031
  if (process.platform === "win32" || process.platform === "darwin") {
3789
- const watcher = fs15.watch(target, { recursive: true }, () => {
4032
+ const watcher = fs17.watch(target, { recursive: true }, () => {
3790
4033
  this.onWatchChange(record.name, target);
3791
4034
  });
3792
4035
  watchers.push(watcher);
@@ -3794,7 +4037,7 @@ var ProcessManager = class {
3794
4037
  const dirs = this.collectSubDirs(target);
3795
4038
  for (const dir of dirs) {
3796
4039
  try {
3797
- const watcher = fs15.watch(dir, () => {
4040
+ const watcher = fs17.watch(dir, () => {
3798
4041
  this.onWatchChange(record.name, target);
3799
4042
  });
3800
4043
  watchers.push(watcher);
@@ -3821,7 +4064,7 @@ var ProcessManager = class {
3821
4064
  const dir = queue.shift();
3822
4065
  let entries;
3823
4066
  try {
3824
- entries = fs15.readdirSync(dir, { withFileTypes: true });
4067
+ entries = fs17.readdirSync(dir, { withFileTypes: true });
3825
4068
  } catch {
3826
4069
  continue;
3827
4070
  }
@@ -3829,7 +4072,7 @@ var ProcessManager = class {
3829
4072
  if (!entry.isDirectory()) continue;
3830
4073
  if (entry.name.startsWith(".")) continue;
3831
4074
  if (entry.name === "node_modules" || entry.name === ".git") continue;
3832
- const full = path17.join(dir, entry.name);
4075
+ const full = path18.join(dir, entry.name);
3833
4076
  if (seen.has(full)) continue;
3834
4077
  seen.add(full);
3835
4078
  out.push(full);
@@ -3929,17 +4172,17 @@ var ProcessManager = class {
3929
4172
  };
3930
4173
 
3931
4174
  // src/daemon/state.ts
3932
- import fs16 from "node:fs";
3933
- import path18 from "node:path";
4175
+ import fs18 from "node:fs";
4176
+ import path19 from "node:path";
3934
4177
  var StateStore = class {
3935
4178
  apps = /* @__PURE__ */ new Map();
3936
4179
  snapshotPath;
3937
4180
  journalPath;
3938
4181
  constructor(base) {
3939
- const stateDir = path18.join(base, "state");
4182
+ const stateDir = path19.join(base, "state");
3940
4183
  ensureDirSync(stateDir);
3941
- this.snapshotPath = path18.join(stateDir, "snapshot.json");
3942
- this.journalPath = path18.join(stateDir, "journal.jsonl");
4184
+ this.snapshotPath = path19.join(stateDir, "snapshot.json");
4185
+ this.journalPath = path19.join(stateDir, "journal.jsonl");
3943
4186
  }
3944
4187
  load() {
3945
4188
  const data = readJsonFile(this.snapshotPath);
@@ -3976,7 +4219,7 @@ var StateStore = class {
3976
4219
  const line = `${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), type, ...data ?? {} })}
3977
4220
  `;
3978
4221
  try {
3979
- fs16.appendFileSync(this.journalPath, line);
4222
+ fs18.appendFileSync(this.journalPath, line);
3980
4223
  } catch {
3981
4224
  }
3982
4225
  }
@@ -3988,7 +4231,7 @@ var StateStore = class {
3988
4231
  };
3989
4232
 
3990
4233
  // src/daemon/transport.ts
3991
- import fs17 from "node:fs";
4234
+ import fs19 from "node:fs";
3992
4235
  import net2 from "node:net";
3993
4236
  async function createDaemonTransport(base) {
3994
4237
  return process.platform === "win32" ? createWinTransport(base) : createPosixTransport(base);
@@ -4002,7 +4245,7 @@ async function createPosixTransport(base) {
4002
4245
  if (err.code !== "EADDRINUSE") return null;
4003
4246
  if (await probeActive(sockPath)) return null;
4004
4247
  try {
4005
- fs17.unlinkSync(sockPath);
4248
+ fs19.unlinkSync(sockPath);
4006
4249
  } catch {
4007
4250
  }
4008
4251
  }
@@ -4055,7 +4298,7 @@ function probeActive(sockPath) {
4055
4298
  }
4056
4299
  function readPortFile(base) {
4057
4300
  try {
4058
- const raw = fs17.readFileSync(portFilePath(base), "utf8");
4301
+ const raw = fs19.readFileSync(portFilePath(base), "utf8");
4059
4302
  return Number.parseInt(raw.trim(), 10) || null;
4060
4303
  } catch {
4061
4304
  return null;
@@ -4063,7 +4306,7 @@ function readPortFile(base) {
4063
4306
  }
4064
4307
  function writePortFile(base, port) {
4065
4308
  try {
4066
- fs17.writeFileSync(portFilePath(base), String(port));
4309
+ fs19.writeFileSync(portFilePath(base), String(port));
4067
4310
  } catch {
4068
4311
  }
4069
4312
  }
@@ -4072,7 +4315,7 @@ function writePortFile(base, port) {
4072
4315
  async function runDaemon() {
4073
4316
  const data = dataDir();
4074
4317
  const runtime = runtimeDir();
4075
- ensureDirSync(path19.join(data, "logs"));
4318
+ ensureDirSync(path20.join(data, "logs"));
4076
4319
  const logger = createDaemonLogger(data);
4077
4320
  logger.log("daemon starting", { pid: process.pid, version: VERSION, platform: process.platform });
4078
4321
  const lock = acquireLock(runtime);
@@ -4287,8 +4530,8 @@ var METHODS = {
4287
4530
  const files = paths.err === null ? [paths.out] : [paths.out, paths.err];
4288
4531
  for (const f of files) {
4289
4532
  try {
4290
- ensureDirSync(path19.dirname(f));
4291
- fs18.writeFileSync(f, "");
4533
+ ensureDirSync(path20.dirname(f));
4534
+ fs20.writeFileSync(f, "");
4292
4535
  } catch (err) {
4293
4536
  logger.log(`flush ${rec.name}: cannot truncate ${f}: ${err.message}`);
4294
4537
  }
@@ -4321,7 +4564,7 @@ var METHODS = {
4321
4564
  for (const f of files) {
4322
4565
  if (!pathExists(f)) continue;
4323
4566
  try {
4324
- const stat = fs18.statSync(f);
4567
+ const stat = fs20.statSync(f);
4325
4568
  if (stat.size >= max) {
4326
4569
  rotateFile(f, n);
4327
4570
  rotated.push(f);
@@ -4375,11 +4618,11 @@ var METHODS = {
4375
4618
  },
4376
4619
  doctor: (_params, { store, base, transport }) => {
4377
4620
  const checks = [];
4378
- const snapshotPath = path19.join(base, "state", "snapshot.json");
4621
+ const snapshotPath = path20.join(base, "state", "snapshot.json");
4379
4622
  const stateIssues = [];
4380
4623
  if (pathExists(snapshotPath)) {
4381
4624
  try {
4382
- const data = JSON.parse(fs18.readFileSync(snapshotPath, "utf8"));
4625
+ const data = JSON.parse(fs20.readFileSync(snapshotPath, "utf8"));
4383
4626
  if (data.schema === 1 && typeof data.apps === "object") {
4384
4627
  stateIssues.push({
4385
4628
  severity: "ok",
@@ -4412,11 +4655,11 @@ var METHODS = {
4412
4655
  ok: !stateIssues.some((i) => i.severity === "error"),
4413
4656
  issues: stateIssues
4414
4657
  });
4415
- const lockFile = path19.join(runtimeDir(), "daemon.lock");
4658
+ const lockFile = path20.join(runtimeDir(), "daemon.lock");
4416
4659
  const lockIssues = [];
4417
4660
  if (pathExists(lockFile)) {
4418
4661
  try {
4419
- const lock = JSON.parse(fs18.readFileSync(lockFile, "utf8"));
4662
+ const lock = JSON.parse(fs20.readFileSync(lockFile, "utf8"));
4420
4663
  if (typeof lock.pid === "number" && isAlive(lock.pid)) {
4421
4664
  lockIssues.push({
4422
4665
  severity: "ok",
@@ -4527,7 +4770,7 @@ async function startApps(target, ctx) {
4527
4770
  if (target.interpreter !== void 0) input.interpreter = target.interpreter;
4528
4771
  if (target.cwd !== void 0) input.cwd = target.cwd;
4529
4772
  if (target.cliOverrides) Object.assign(input, target.cliOverrides);
4530
- const single = normalizeSingleApp(input, path19.dirname(target.path), target.envName);
4773
+ const single = normalizeSingleApp(input, path20.dirname(target.path), target.envName);
4531
4774
  apps = [single.app];
4532
4775
  warnings = single.warnings;
4533
4776
  }
@@ -4592,7 +4835,7 @@ function expandTargets(store, name) {
4592
4835
  function applyCliOverrides(app, overrides) {
4593
4836
  if (overrides.instances !== void 0) {
4594
4837
  const v = overrides.instances;
4595
- if (v === "max" || v === "MAX") app.instances = os8.cpus().length || 1;
4838
+ if (v === "max" || v === "MAX") app.instances = os9.cpus().length || 1;
4596
4839
  else {
4597
4840
  const n = Number(v);
4598
4841
  if (Number.isFinite(n) && n >= 1) app.instances = Math.round(n);
@@ -4652,15 +4895,15 @@ function rotateFile(file, retain) {
4652
4895
  const newName = `${file}.${i + 1}.gz`;
4653
4896
  if (pathExists(oldName)) {
4654
4897
  if (i + 1 >= retain) {
4655
- fs18.unlinkSync(oldName);
4898
+ fs20.unlinkSync(oldName);
4656
4899
  } else {
4657
4900
  if (i === 0) {
4658
- const content = fs18.readFileSync(file);
4659
- fs18.unlinkSync(file);
4901
+ const content = fs20.readFileSync(file);
4902
+ fs20.unlinkSync(file);
4660
4903
  const gz = zlib.gzipSync(content);
4661
- fs18.writeFileSync(newName, gz);
4904
+ fs20.writeFileSync(newName, gz);
4662
4905
  } else {
4663
- fs18.renameSync(oldName, newName);
4906
+ fs20.renameSync(oldName, newName);
4664
4907
  }
4665
4908
  }
4666
4909
  }