mslxdff 0.1.34 → 0.1.35

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.
Files changed (2) hide show
  1. package/bin/mslxdff.js +116 -25
  2. package/package.json +1 -1
package/bin/mslxdff.js CHANGED
@@ -804,36 +804,85 @@ if (broadbandGroups().length) {
804
804
  }
805
805
 
806
806
  // Auto-update: periodically check npm for a newer mslxdff and restart.
807
- // Enable with MSLXDFF_AUTO_UPDATE=1 (hourly) or MSLXDFF_AUTO_UPDATE_MS=<ms>.
808
- // Uses the same npm view/install path as `mslxdff -update`, but runs inside
809
- // the daemon so no manual intervention is needed.
807
+ // Default: hourly (no env needed). Disable with MSLXDFF_AUTO_UPDATE=0/off/false.
808
+ // Tuning: MSLXDFF_AUTO_UPDATE=1/true hourly, or MSLXDFF_AUTO_UPDATE_MS=<ms>.
810
809
  const autoUpdateMs = autoUpdateIntervalMs();
810
+ function emitAutoUpdate(type, data = {}) {
811
+ const entry = { ts: Date.now(), type, ...data };
812
+ try { bus?.emit(entry); } catch {}
813
+ try { logs?.appendEvent?.(entry); } catch {}
814
+ // also to daemon.log for tail
815
+ const line = `[auto-update] ${type} ${JSON.stringify(data)}`;
816
+ console.log(line);
817
+ }
811
818
  if (autoUpdateMs) {
812
819
  console.log(`auto-update enabled: checking every ${Math.round(autoUpdateMs / 60000)}m`);
820
+ emitAutoUpdate("auto-update-enabled", { intervalMs: autoUpdateMs, current: VERSION });
821
+ // run once shortly after start (30s) so a newly deployed fix is picked up quickly,
822
+ // then on the regular interval
823
+ setTimeout(() => {
824
+ emitAutoUpdate("auto-update-check", { current: VERSION });
825
+ checkAndAutoUpdate().catch((err) => {
826
+ console.log(`auto-update check failed: ${errMsg(err)}`);
827
+ emitAutoUpdate("auto-update-failed", { error: errMsg(err) });
828
+ });
829
+ }, 30_000).unref?.();
813
830
  const autoUpdateTimer = setInterval(() => {
814
- checkAndAutoUpdate().catch((err) => console.log(`auto-update check failed: ${errMsg(err)}`));
831
+ emitAutoUpdate("auto-update-check", { current: VERSION });
832
+ checkAndAutoUpdate().catch((err) => {
833
+ console.log(`auto-update check failed: ${errMsg(err)}`);
834
+ emitAutoUpdate("auto-update-failed", { error: errMsg(err) });
835
+ });
815
836
  }, autoUpdateMs);
816
837
  autoUpdateTimer.unref();
838
+ } else {
839
+ console.log(`auto-update disabled (set MSLXDFF_AUTO_UPDATE=1 to enable hourly)`);
840
+ emitAutoUpdate("auto-update-disabled", { current: VERSION });
817
841
  }
818
842
 
819
843
  async function checkAndAutoUpdate() {
820
- const info = await run(npmCmd(), ["view", "mslxdff", "version", "dist-tags.latest"]);
821
- if (info.err) throw new Error(info.err.message);
822
- const parts = (info.stdout || "").trim().split(/\s+/).filter(Boolean);
823
- const latest = parts[parts.length - 1];
824
- if (!latest || latest === VERSION) return;
825
- // simple semver compare: skip if latest is not newer
826
- if (compareSemver(latest, VERSION) <= 0) return;
844
+ emitAutoUpdate("auto-update-query", { current: VERSION });
845
+ const info = await run(npmCmd(), ["view", "mslxdff", "dist-tags.latest", "--json"]);
846
+ if (info.err) {
847
+ emitAutoUpdate("auto-update-query-failed", { error: info.err.message || String(info.stderr || "").slice(0, 500) });
848
+ throw new Error(info.err.message || String(info.stderr || "").slice(0, 500));
849
+ }
850
+ let latest = "";
851
+ try {
852
+ latest = JSON.parse(String(info.stdout || "").trim());
853
+ if (Array.isArray(latest)) latest = latest[latest.length - 1];
854
+ latest = String(latest || "").replace(/^v/, "").trim();
855
+ } catch {
856
+ const raw = String(info.stdout || "").trim();
857
+ const m = raw.match(/(\d+\.\d+\.\d+[^\s'"]*)/);
858
+ latest = m ? m[1] : raw.split(/\s+/).pop()?.replace(/['"]/g, "") || "";
859
+ }
860
+ latest = latest.replace(/['"]/g, "").trim();
861
+ emitAutoUpdate("auto-update-queried", { current: VERSION, latest, stdout: String(info.stdout || "").trim().slice(0, 200) });
862
+ if (!latest || latest === VERSION) {
863
+ emitAutoUpdate("auto-update-noop", { current: VERSION, latest });
864
+ return;
865
+ }
866
+ if (compareSemver(latest, VERSION) <= 0) {
867
+ emitAutoUpdate("auto-update-noop", { current: VERSION, latest, reason: "not newer" });
868
+ return;
869
+ }
870
+ emitAutoUpdate("auto-update-found", { current: VERSION, latest });
827
871
  console.log(`auto-update: v${VERSION} -> v${latest}, installing...`);
872
+ emitAutoUpdate("auto-update-installing", { current: VERSION, latest });
828
873
  const up = await run(npmCmd(), ["install", "-g", `mslxdff@${latest}`]);
829
- if (up.err) throw new Error(up.err.message);
874
+ if (up.err) {
875
+ emitAutoUpdate("auto-update-install-failed", { current: VERSION, latest, error: up.err.message || String(up.stderr || "").slice(0, 500) });
876
+ throw new Error(up.err.message || String(up.stderr || "").slice(0, 500));
877
+ }
878
+ emitAutoUpdate("auto-update-installed", { current: VERSION, latest, stdout: String(up.stdout || "").slice(0, 500) });
830
879
  console.log(`auto-update: installed v${latest}, restarting daemon...`);
831
- try { stopDaemon(); } catch {}
832
- // startDaemon re-reads VERSION from the newly installed package on next boot;
833
- // for the current process we just respawn with the new code.
880
+ emitAutoUpdate("auto-update-restarting", { current: VERSION, latest });
881
+ try { stopDaemon(); } catch (e) { emitAutoUpdate("auto-update-stop-failed", { error: errMsg(e) }); }
834
882
  const newPid = startDaemon([]);
835
883
  await waitForHealth(resolvePort(), 8000);
836
884
  console.log(`auto-update: restarted as v${latest} (pid ${newPid})`);
885
+ emitAutoUpdate("auto-update-restarted", { current: VERSION, latest, newPid });
837
886
  }
838
887
 
839
888
  function compareSemver(a, b) {
@@ -906,10 +955,13 @@ function groupSyncIntervalMs() {
906
955
 
907
956
  function autoUpdateIntervalMs() {
908
957
  const raw = process.env.MSLXDFF_AUTO_UPDATE_MS ?? process.env.MSLXDFF_AUTO_UPDATE;
909
- if (raw === undefined || raw === null || raw === "") return 0;
910
- if (raw === "1" || String(raw).toLowerCase() === "true") return 60 * 60 * 1000;
958
+ // default: hourly when env not set; explicit 0/off/false disables
959
+ if (raw === undefined || raw === null || raw === "") return 60 * 60 * 1000;
960
+ const s = String(raw).trim().toLowerCase();
961
+ if (s === "0" || s === "off" || s === "false" || s === "no" || s === "disable" || s === "disabled") return 0;
962
+ if (s === "1" || s === "true" || s === "on" || s === "yes" || s === "enable" || s === "enabled") return 60 * 60 * 1000;
911
963
  const n = Number(raw);
912
- return Number.isInteger(n) && n > 0 ? n : 0;
964
+ return Number.isInteger(n) && n > 0 ? n : 60 * 60 * 1000;
913
965
  }
914
966
 
915
967
  function banWindowMs() {
@@ -979,7 +1031,7 @@ Environment:
979
1031
  MSLXDFF_MAX_HOPS max peer-forwarding depth (default 3)
980
1032
  MSLXDFF_BAN_THRESHOLD failed joins before an ip is banned (default 5)
981
1033
  MSLXDFF_BAN_WINDOW_MS ban duration after too many failures (default 48h)
982
- MSLXDFF_AUTO_UPDATE auto-update: 1/true=hourly, or ms interval (0=off)
1034
+ MSLXDFF_AUTO_UPDATE auto-update: hourly by default, 0/off/false to disable, 1/true or ms
983
1035
  MSLXDFF_AUTO_UPDATE_MS same as above, explicit ms (overrides AUTO_UPDATE)
984
1036
  `);
985
1037
  }
@@ -1148,6 +1200,32 @@ function fmtEvent(e) {
1148
1200
  return `${head} client abort ${m(e.model)} total=${fmtDur(e.totalMs)}`;
1149
1201
  case "result":
1150
1202
  return `${head} result ${e.status} ${m(e.model)} via=${e.via} 响应耗时 ${fmtDur(e.durationMs)}`;
1203
+ case "auto-update-enabled":
1204
+ return `${head} auto-update enabled every ${Math.round((e.intervalMs||0)/60000)}m current=${e.current}`;
1205
+ case "auto-update-disabled":
1206
+ return `${head} auto-update disabled current=${e.current}`;
1207
+ case "auto-update-check":
1208
+ return `${head} auto-update checking current=${e.current}`;
1209
+ case "auto-update-query":
1210
+ return `${head} auto-update querying npm current=${e.current}`;
1211
+ case "auto-update-queried":
1212
+ return `${head} auto-update queried current=${e.current} latest=${e.latest} raw=${(e.stdout||"").slice(0,80)}`;
1213
+ case "auto-update-noop":
1214
+ return `${head} auto-update noop current=${e.current} latest=${e.latest}${e.reason?` reason=${e.reason}`:""}`;
1215
+ case "auto-update-found":
1216
+ return `${head} auto-update NEW v${e.current} -> v${e.latest} 发现新版本`;
1217
+ case "auto-update-installing":
1218
+ return `${head} auto-update installing v${e.latest}...`;
1219
+ case "auto-update-installed":
1220
+ return `${head} auto-update installed v${e.latest}`;
1221
+ case "auto-update-restarting":
1222
+ return `${head} auto-update restarting daemon to v${e.latest}...`;
1223
+ case "auto-update-restarted":
1224
+ return `${head} auto-update restarted pid=${e.newPid} v${e.current} -> v${e.latest} 升级完成`;
1225
+ case "auto-update-failed":
1226
+ case "auto-update-query-failed":
1227
+ case "auto-update-install-failed":
1228
+ return `${head} auto-update failed ${e.type} error=${e.error||""}`;
1151
1229
  default:
1152
1230
  return `${head} ${e?.type || "?"} ${JSON.stringify(e || {})}`;
1153
1231
  }
@@ -1167,22 +1245,35 @@ function npmCmd() {
1167
1245
  }
1168
1246
 
1169
1247
  function run(cmd, args, opts = {}) {
1248
+ const isWin = process.platform === "win32";
1170
1249
  return new Promise((resolve) => {
1171
- execFile(cmd, args, { timeout: 120_000, ...opts }, (err, stdout, stderr) => resolve({ err, stdout, stderr }));
1250
+ // npm.cmd is a batch file on Windows execFile needs shell:true to find it via PATHEXT
1251
+ const execOpts = isWin ? { shell: true, windowsHide: true } : {};
1252
+ execFile(cmd, args, { timeout: 120_000, ...execOpts, ...opts }, (err, stdout, stderr) => resolve({ err, stdout, stderr }));
1172
1253
  });
1173
1254
  }
1174
1255
 
1175
1256
  async function updateSelf() {
1176
1257
  console.log(`mslxdff v${VERSION} — checking for updates…`);
1177
- const info = await run(npmCmd(), ["view", "mslxdff", "version", "dist-tags.latest"]);
1258
+ const info = await run(npmCmd(), ["view", "mslxdff", "dist-tags.latest", "--json"]);
1178
1259
  if (info.err) {
1179
- console.error(`could not query npm: ${info.err.message}`);
1260
+ console.error(`could not query npm: ${info.err.message || String(info.stderr || "").slice(0, 500)}`);
1180
1261
  process.exit(1);
1181
1262
  }
1182
- const [version, latest] = (info.stdout || "").trim().split(/\s+/);
1263
+ let latest = "";
1264
+ try {
1265
+ latest = JSON.parse(String(info.stdout || "").trim());
1266
+ if (Array.isArray(latest)) latest = latest[latest.length - 1];
1267
+ latest = String(latest || "").trim();
1268
+ } catch {
1269
+ const m = String(info.stdout || "").trim().match(/(\d+\.\d+\.\d+[^\s'"]*)/);
1270
+ latest = m ? m[1] : "";
1271
+ }
1272
+ latest = String(latest || "").replace(/['"]/g, "").trim();
1273
+ const version = VERSION;
1183
1274
  console.log(` installed: ${version}`);
1184
- console.log(` latest: ${latest}`);
1185
- if (version === latest) {
1275
+ console.log(` latest: ${latest || "unknown"}`);
1276
+ if (!latest || version === latest || compareSemver(latest, version) <= 0) {
1186
1277
  console.log("already up to date");
1187
1278
  process.exit(0);
1188
1279
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mslxdff",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "测试项目,请勿使用。",
5
5
  "type": "module",
6
6
  "bin": {