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 +32 -4
- package/package.json +1 -1
- package/src/daemon.js +20 -3
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
|
-
|
|
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
|
|
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
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" };
|