mslxdff 0.1.8 → 0.1.10

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/bin/mslxdff.js CHANGED
@@ -8,7 +8,7 @@ import { createRouter } from "../src/routes.js";
8
8
  import { createUpstreamClient } from "../src/upstream.js";
9
9
  import { createModelsService } from "../src/models.js";
10
10
  import { loadToken, refreshToken, setPort, getPort, loadGroupsJoined, saveGroupsJoined, loadModelErrors } from "../src/state.js";
11
- import { startDaemon, stopDaemon, writePid, pidFile, logFile, readPid } from "../src/daemon.js";
11
+ import { startDaemon, stopDaemon, writePid, pidFile, logFile, readPid, readPidVersion, isPidAlive } from "../src/daemon.js";
12
12
  import { createAutoSelector } from "../src/auto.js";
13
13
  import { createPeersService } from "../src/peers.js";
14
14
  import { createGroupsService, createBansService, refreshGroupMembers, syncPeersFromMembers } from "../src/groups.js";
@@ -303,6 +303,26 @@ async function syncAllJoinedGroups({ peers, groups }) {
303
303
  return results;
304
304
  }
305
305
 
306
+ // Upgrade-aware handling of a running daemon: keep it when it already runs
307
+ // our version, otherwise stop it so the spawn below takes over. Cleans up
308
+ // stale pid files (daemon died without -stop).
309
+ function stopDaemonIfOutdated() {
310
+ const pid = readPid();
311
+ if (!pid) return;
312
+ if (!isPidAlive(pid)) {
313
+ console.log(`daemon pid ${pid} is stale (not running) — starting fresh`);
314
+ return;
315
+ }
316
+ const runningVersion = readPidVersion();
317
+ if (runningVersion === VERSION) return;
318
+ console.log(
319
+ runningVersion
320
+ ? `daemon running v${runningVersion} — upgrading to v${VERSION}, restarting...`
321
+ : `daemon version unknown — restarting with v${VERSION}...`
322
+ );
323
+ stopDaemon();
324
+ }
325
+
306
326
  // -port N: persist the port, then restart the daemon on it if one is running.
307
327
  // Skip when we ARE the daemon child (it already carries the port via args).
308
328
  const portArg = argValue("-port", "--port");
@@ -330,6 +350,7 @@ if (args.includes("-d") || args.includes("--daemon")) {
330
350
  if (!process.env.MSLXDFF_DAEMON) {
331
351
  // foreground: spawn the detached background instance, then wait for health
332
352
  const port = effectivePort();
353
+ stopDaemonIfOutdated();
333
354
  const spawnedPid = startDaemon(args.filter((a) => a !== "-d" && a !== "--daemon"));
334
355
  await waitForHealth(port, 4000);
335
356
  console.log(`mslxdff daemon started (pid ${spawnedPid})`);
@@ -343,12 +364,14 @@ if (args.includes("-d") || args.includes("--daemon")) {
343
364
  // Bare run: show status + help when the daemon is already up; otherwise spawn it
344
365
  // as a background daemon and exit — never holds the terminal (npx-friendly).
345
366
  if (!process.env.MSLXDFF_DAEMON) {
346
- if (readPid()) {
367
+ const pid = readPid();
368
+ if (pid && isPidAlive(pid) && readPidVersion() === VERSION) {
347
369
  await printStatus();
348
370
  printHelp();
349
371
  process.exit(0);
350
372
  }
351
373
  const port = effectivePort();
374
+ stopDaemonIfOutdated();
352
375
  const spawnedPid = startDaemon([]);
353
376
  await waitForHealth(port, 4000);
354
377
  console.log(`mslxdff v${VERSION} started as a background daemon (pid ${spawnedPid})`);
@@ -388,7 +411,7 @@ const srv = startServer({ router });
388
411
  await srv.ready();
389
412
  models.startAutoRefresh();
390
413
  if (process.env.MSLXDFF_DAEMON) {
391
- writePid(process.pid);
414
+ writePid(process.pid, VERSION);
392
415
  }
393
416
  const addr = srv.server.address();
394
417
  const host = addr.address === "0.0.0.0" || addr.address === "::" ? "localhost" : addr.address;
@@ -656,6 +679,11 @@ function fmtStatus(id, statuses) {
656
679
  return `${e.status}${when}${code}`;
657
680
  }
658
681
 
682
+ function fmtDur(ms) {
683
+ if (!Number.isFinite(ms)) return "?";
684
+ return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
685
+ }
686
+
659
687
  function fmtEvent(e) {
660
688
  const t = e?.ts ? new Date(e.ts).toISOString().slice(11, 19) : "--:--:--";
661
689
  const head = `[${t}]`;
@@ -672,7 +700,7 @@ function fmtEvent(e) {
672
700
  case "peer-error":
673
701
  return `${head} peer err ${e.peer} ${e.status ? `HTTP ${e.status}` : "network"}: ${m(e.message)}`;
674
702
  case "result":
675
- return `${head} result ${e.status} ${m(e.model)} via=${e.via} ${e.durationMs ?? "?"}ms`;
703
+ return `${head} result ${e.status} ${m(e.model)} via=${e.via} 响应耗时 ${fmtDur(e.durationMs)}`;
676
704
  default:
677
705
  return `${head} ${e?.type || "?"} ${JSON.stringify(e || {})}`;
678
706
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mslxdff",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "测试项目,请勿使用。",
5
5
  "type": "module",
6
6
  "bin": {
package/src/daemon.js CHANGED
@@ -33,19 +33,36 @@ export function startDaemon(args = []) {
33
33
  return child.pid;
34
34
  }
35
35
 
36
- export function writePid(pid) {
36
+ export function writePid(pid, version) {
37
37
  const dir = daemonDir();
38
38
  mkdirSync(dir, { recursive: true });
39
- writeFileSync(pidFile(), String(pid), { mode: 0o600 });
39
+ writeFileSync(pidFile(), version ? `${pid}\n${version}` : String(pid), { mode: 0o600 });
40
40
  }
41
41
 
42
42
  export function readPid() {
43
43
  if (!existsSync(pidFile())) return null;
44
44
  const raw = readFileSync(pidFile(), "utf8").trim();
45
- const n = Number(raw);
45
+ const n = Number(raw.split("\n")[0]);
46
46
  return Number.isInteger(n) && n > 0 ? n : null;
47
47
  }
48
48
 
49
+ export function readPidVersion() {
50
+ if (!existsSync(pidFile())) return null;
51
+ const raw = readFileSync(pidFile(), "utf8");
52
+ const lines = raw.split("\n");
53
+ return lines.length > 1 && lines[1].trim() ? lines[1].trim() : null;
54
+ }
55
+
56
+ // Best-effort liveness check (signal 0); ESRCH means the process is gone.
57
+ export function isPidAlive(pid) {
58
+ try {
59
+ process.kill(pid, 0);
60
+ return true;
61
+ } catch (err) {
62
+ return err.code !== "ESRCH";
63
+ }
64
+ }
65
+
49
66
  export function stopDaemon() {
50
67
  const pid = readPid();
51
68
  if (!pid) return { stopped: false, reason: "no pid file" };