From 445c6ca2fb5131149f1420d23f06fafcbd08ce63 Mon Sep 17 00:00:00 2001 From: Fisto Date: Thu, 17 Sep 2026 08:42:09 +0200 Subject: [PATCH] Keep daemon logs after a button restart --- CURRENT.md | 3 +++ docs/daemon.md | 8 ++++++++ poc/src/control-bridge.ts | 9 +++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CURRENT.md b/CURRENT.md index 592d7ba..5d1e261 100644 --- a/CURRENT.md +++ b/CURRENT.md @@ -40,6 +40,9 @@ mobile draft at 390px. - [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`: `previewed: 2`; thumbs served over `/api/media///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 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 diff --git a/docs/daemon.md b/docs/daemon.md index 98a2b8f..1a51cbd 100644 --- a/docs/daemon.md +++ b/docs/daemon.md @@ -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, `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 `GET /health` returns the core connection state, the socket path, the owner ID, and the query count. diff --git a/poc/src/control-bridge.ts b/poc/src/control-bridge.ts index fe08f0e..b3ac1f3 100644 --- a/poc/src/control-bridge.ts +++ b/poc/src/control-bridge.ts @@ -10,6 +10,7 @@ */ import { spawn } from "node:child_process"; +import { closeSync, openSync } from "node:fs"; import { config as loadEnv } from "dotenv"; import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.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 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}`], { detached: true, - stdio: "ignore", + stdio: ["ignore", logFd, logFd], cwd: process.cwd(), env: { ...process.env, VESKSLOP_DAEMON_RESTART: "1" } }); child.unref(); + closeSync(logFd); setTimeout(() => shutdown(true), 300); }