@iletai/nzb 1.5.3 → 1.6.0
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/dist/daemon.js +60 -0
- package/dist/paths.js +2 -0
- package/dist/telegram/bot.js +79 -695
- package/dist/telegram/dedup.js +47 -0
- package/dist/telegram/formatter.js +109 -16
- package/dist/telegram/handlers/commands.js +151 -0
- package/dist/telegram/handlers/streaming.js +416 -0
- package/dist/telegram/menus.js +180 -0
- package/dist/telegram/safe-api.js +41 -0
- package/package.json +4 -2
package/dist/daemon.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
2
3
|
import { broadcastToSSE, startApiServer } from "./api/server.js";
|
|
3
4
|
import { config } from "./config.js";
|
|
4
5
|
import { getClient, stopClient } from "./copilot/client.js";
|
|
5
6
|
import { getWorkers, initOrchestrator, setMessageLogger, setProactiveNotify, setWorkerNotify, stopHealthCheck, } from "./copilot/orchestrator.js";
|
|
7
|
+
import { PID_FILE_PATH } from "./paths.js";
|
|
6
8
|
import { closeDb, getDb } from "./store/db.js";
|
|
7
9
|
import { createBot, sendProactiveMessage, sendWorkerNotification, startBot, stopBot } from "./telegram/bot.js";
|
|
8
10
|
import { checkForUpdate } from "./update.js";
|
|
@@ -14,8 +16,64 @@ function truncate(text, max = 200) {
|
|
|
14
16
|
const oneLine = text.replace(/\n/g, " ").trim();
|
|
15
17
|
return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a process with the given PID is alive.
|
|
21
|
+
* Sends signal 0 which doesn't kill but checks existence.
|
|
22
|
+
*/
|
|
23
|
+
function isProcessAlive(pid) {
|
|
24
|
+
try {
|
|
25
|
+
process.kill(pid, 0);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Acquire a PID lock file. Prevents multiple daemon instances.
|
|
34
|
+
* Returns true if lock acquired, false if another instance is running.
|
|
35
|
+
*/
|
|
36
|
+
function acquirePidLock() {
|
|
37
|
+
if (existsSync(PID_FILE_PATH)) {
|
|
38
|
+
try {
|
|
39
|
+
const existingPid = parseInt(readFileSync(PID_FILE_PATH, "utf-8").trim(), 10);
|
|
40
|
+
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
41
|
+
console.error(`[nzb] Another NZB instance is already running (PID ${existingPid}).`);
|
|
42
|
+
console.error(`[nzb] Stop it first, or remove ${PID_FILE_PATH} if the process is stale.`);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// Stale PID file — process is dead, remove it
|
|
46
|
+
console.log(`[nzb] Removed stale PID file (old PID ${existingPid} is no longer running).`);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Corrupt PID file — remove it
|
|
50
|
+
}
|
|
51
|
+
unlinkSync(PID_FILE_PATH);
|
|
52
|
+
}
|
|
53
|
+
writeFileSync(PID_FILE_PATH, String(process.pid), { mode: 0o644 });
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/** Release the PID lock file. */
|
|
57
|
+
function releasePidLock() {
|
|
58
|
+
try {
|
|
59
|
+
if (existsSync(PID_FILE_PATH)) {
|
|
60
|
+
const pid = parseInt(readFileSync(PID_FILE_PATH, "utf-8").trim(), 10);
|
|
61
|
+
// Only remove if it's our PID (in case a new instance took over)
|
|
62
|
+
if (pid === process.pid) {
|
|
63
|
+
unlinkSync(PID_FILE_PATH);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* best effort */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
17
71
|
async function main() {
|
|
18
72
|
console.log("[nzb] Starting NZB daemon...");
|
|
73
|
+
// Single-instance guard
|
|
74
|
+
if (!acquirePidLock()) {
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
19
77
|
if (config.selfEditEnabled) {
|
|
20
78
|
console.log("[nzb] Warning: Self-edit mode enabled — NZB can modify his own source code");
|
|
21
79
|
}
|
|
@@ -153,6 +211,7 @@ async function shutdown() {
|
|
|
153
211
|
/* best effort */
|
|
154
212
|
}
|
|
155
213
|
closeDb();
|
|
214
|
+
releasePidLock();
|
|
156
215
|
console.log("[nzb] Goodbye.");
|
|
157
216
|
process.exit(0);
|
|
158
217
|
}
|
|
@@ -184,6 +243,7 @@ export async function restartDaemon() {
|
|
|
184
243
|
/* best effort */
|
|
185
244
|
}
|
|
186
245
|
closeDb();
|
|
246
|
+
releasePidLock();
|
|
187
247
|
// Spawn a detached replacement process with the same args (include execArgv for tsx/loaders)
|
|
188
248
|
const child = spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
189
249
|
detached: true,
|
package/dist/paths.js
CHANGED
|
@@ -17,6 +17,8 @@ export const HISTORY_PATH = join(NZB_HOME, "tui_history");
|
|
|
17
17
|
export const TUI_DEBUG_LOG_PATH = join(NZB_HOME, "tui-debug.log");
|
|
18
18
|
/** Path to the API bearer token file */
|
|
19
19
|
export const API_TOKEN_PATH = join(NZB_HOME, "api-token");
|
|
20
|
+
/** Path to the PID lock file for single-instance enforcement */
|
|
21
|
+
export const PID_FILE_PATH = join(NZB_HOME, "nzb.pid");
|
|
20
22
|
/** Ensure ~/.nzb/ exists */
|
|
21
23
|
export function ensureNZBHome() {
|
|
22
24
|
mkdirSync(NZB_HOME, { recursive: true });
|