Keep daemon logs after a button restart

This commit is contained in:
Fisto
2026-09-17 08:42:09 +02:00
parent d886d22d89
commit 445c6ca2fb
3 changed files with 18 additions and 2 deletions
+3
View File
@@ -40,6 +40,9 @@ mobile draft at 390px.
- [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB - [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB
cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`: cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`:
`previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200). `previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200).
- [x] C41 Restart keeps its logs. A button restart spawned the replacement with `stdio: ignore`,
so anything after the restart vanished. The replacement now appends to `daemon.log` beside the
token file (`VESKSLOP_DAEMON_LOG` overrides); documented in [docs/daemon.md](docs/daemon.md).
- [x] C40 Stale scrape states. Two `general_chut` scrapes showed `running` because the daemons - [x] C40 Stale scrape states. Two `general_chut` scrapes showed `running` because the daemons
that owned them were killed mid-job; the state file kept the last written value. On daemon that owned them were killed mid-job; the state file kept the last written value. On daemon
start the engine now reconciles `running`/`queued` scrapes to `paused`, and History has a start the engine now reconciles `running`/`queued` scrapes to `paused`, and History has a
+8
View File
@@ -145,6 +145,14 @@ Core tab and the `/api/core/*` routes use it.
Verified live on 2026-09-13: the web UI started the client under auto-Xvfb, the bridge connected, Verified live on 2026-09-13: the web UI started the client under auto-Xvfb, the bridge connected,
`state.get` returned the real account and guilds, and stop exited with code 0. `state.get` returned the real account and guilds, and stop exited with code 0.
## Self-restart
`POST /api/daemon/restart` (Settings → Server → **Restart daemon**) replaces the daemon process
without touching the client. The replacement detaches, so the terminal or parent shell can close.
It appends stdout and stderr to `daemon.log` next to the token file (`poc/daemon.log` by default;
`VESKSLOP_DAEMON_LOG` overrides). On start it pauses scrapes still marked `running` by a previous
daemon, so a hard kill never leaves ghost jobs.
## Health endpoint ## Health endpoint
`GET /health` returns the core connection state, the socket path, the owner ID, and the query count. `GET /health` returns the core connection state, the socket path, the owner ID, and the query count.
+7 -2
View File
@@ -10,6 +10,7 @@
*/ */
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import { closeSync, openSync } from "node:fs";
import { config as loadEnv } from "dotenv"; import { config as loadEnv } from "dotenv";
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -1051,14 +1052,18 @@ function restartDaemon(): void {
const args = [...process.execArgv, ...process.argv.slice(1)]; const args = [...process.execArgv, ...process.argv.slice(1)];
const command = `${quote(process.execPath)} ${args.map(quote).join(" ")}`; const command = `${quote(process.execPath)} ${args.map(quote).join(" ")}`;
console.log("[daemon] restart requested: spawning a replacement"); const logPath = process.env.VESKSLOP_DAEMON_LOG ?? join(dirname(tokenFile), "daemon.log");
console.log(`[daemon] restart requested: spawning a replacement, appending to ${logPath}`);
const logFd = openSync(logPath, "a");
const child = spawn("sh", ["-c", `sleep 2; exec ${command}`], { const child = spawn("sh", ["-c", `sleep 2; exec ${command}`], {
detached: true, detached: true,
stdio: "ignore", stdio: ["ignore", logFd, logFd],
cwd: process.cwd(), cwd: process.cwd(),
env: { ...process.env, VESKSLOP_DAEMON_RESTART: "1" } env: { ...process.env, VESKSLOP_DAEMON_RESTART: "1" }
}); });
child.unref(); child.unref();
closeSync(logFd);
setTimeout(() => shutdown(true), 300); setTimeout(() => shutdown(true), 300);
} }