Merge branch 'main' of github.com-red:Routstr/routstrd into coco-integration

# Conflicts:
#	src/cli.ts
This commit is contained in:
redshift
2026-07-30 20:59:46 +01:00
5 changed files with 68 additions and 25 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
"monitor": "bun src/index.ts monitor",
"lint": "tsc --noEmit",
"test": "bun test",
"build": "mkdir -p dist/daemon && bun build src/index.ts --target=bun --outfile=dist/index.js && bun build src/daemon/index.ts --target=bun --outfile=dist/daemon/index.js",
"build": "bun build src/index.ts --target=bun --outfile=dist/index.js --external better-sqlite3 && bun build src/daemon/index.ts --target=bun --outfile=dist/daemon/index.js --external better-sqlite3",
"prepublishOnly": "bun run build"
},
"devDependencies": {
+50 -17
View File
@@ -15,7 +15,7 @@ import {
deleteClientAction,
addClientAction,
} from "./utils/clients";
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "fs";
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
import { execSync } from "child_process";
import {
CONFIG_DIR,
@@ -1955,6 +1955,40 @@ function getLogFileForDate(date: Date = new Date()): string {
return `${LOGS_DIR}/${year}-${month}-${day}.log`;
}
function readLastLines(file: string, lines: number): string {
const content = readFileSync(file, "utf8");
const allLines = content.replace(/\r\n/g, "\n").split("\n");
if (allLines.at(-1) === "") allLines.pop();
return allLines.slice(-lines).join("\n");
}
async function followLogFile(file: string, lines: number): Promise<void> {
const initial = readLastLines(file, lines);
if (initial) {
console.log(initial);
}
let position = statSync(file).size;
while (true) {
await new Promise((resolve) => setTimeout(resolve, 1000));
if (!existsSync(file)) {
continue;
}
const size = statSync(file).size;
if (size < position) {
position = 0;
}
if (size === position) {
continue;
}
const text = await Bun.file(file).slice(position, size).text();
process.stdout.write(text);
position = size;
}
}
program
.command("logs")
.description("View daemon logs")
@@ -1980,24 +2014,23 @@ program
});
if (options.follow) {
const proc = Bun.spawn(["tail", "-n", String(lines), "-f", todayFile], {
stdout: "inherit",
stderr: "inherit",
stdin: "inherit",
});
const exitCode = await proc.exited;
process.exit(exitCode);
if (existsSync(todayFile)) {
await followLogFile(todayFile, lines);
} else {
console.log("No log file for today to follow.");
}
return;
}
const proc = Bun.spawn(["tail", "-n", String(lines), ...logFiles], {
stdout: "inherit",
stderr: "inherit",
stdin: "inherit",
});
const exitCode = await proc.exited;
process.exit(exitCode);
for (const file of logFiles) {
if (logFiles.length > 1) {
console.log(`==> ${file} <==`);
}
const output = readLastLines(file, lines);
if (output) {
console.log(output);
}
}
});
export function cli(args: string[]) {
+2 -2
View File
@@ -1,5 +1,5 @@
import { createServer } from "http";
import { existsSync } from "fs";
import { existsSync, unlinkSync } from "fs";
import {
ProviderManager,
createStorageAdapterFromStore,
@@ -182,7 +182,7 @@ async function main(): Promise<void> {
try {
if (existsSync(SOCKET_PATH)) {
Bun.spawn(["rm", SOCKET_PATH]);
unlinkSync(SOCKET_PATH);
}
} catch {
// Ignore
+8 -2
View File
@@ -1,5 +1,6 @@
import { existsSync } from "fs";
import { createHash } from "crypto";
import { isAbsolute } from "path";
import { logger } from "../../utils/logger";
import { withCrossProcessLock } from "../../utils/process-lock";
import type { HistoryEntry } from "@cashu/coco-core";
@@ -101,13 +102,18 @@ export async function isCocodInstalled(
): Promise<boolean> {
const executable = resolveCocodExecutable(cocodPath);
if (executable.includes("/")) {
if (
isAbsolute(executable) ||
executable.includes("/") ||
executable.includes("\\")
) {
return existsSync(executable);
}
try {
const command = process.platform === "win32" ? "where.exe" : "which";
const proc = Bun.spawn({
cmd: ["which", executable],
cmd: [command, executable],
stdout: "ignore",
stderr: "ignore",
});
+7 -3
View File
@@ -8,6 +8,8 @@ import {
import { logger } from "./utils/logger";
import { CONFIG_DIR, LOGS_DIR } from "./utils/config";
import { withCrossProcessLock } from "./utils/process-lock";
import { fileURLToPath } from "url";
import { existsSync } from "fs";
const DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`;
const DEBUG_LOG_PATH = `${CONFIG_DIR}/debug.log`;
@@ -146,15 +148,17 @@ async function startDaemonUnlocked(
args.push("--provider", options.provider);
}
const daemonScript = new URL("./daemon/index.js", import.meta.url).pathname;
const shellCmd = `bun run "${daemonScript}" ${args.map((a) => `'${a}'`).join(" ")}`;
let daemonScript = fileURLToPath(new URL("./daemon/index.js", import.meta.url));
if (!existsSync(daemonScript)) {
daemonScript = fileURLToPath(new URL("./daemon/index.ts", import.meta.url));
}
const debugLogOffset = existsSync(DEBUG_LOG_PATH)
? fileSize(DEBUG_LOG_PATH)
: 0;
const debugLogFd = openSync(DEBUG_LOG_PATH, "a");
const proc = Bun.spawn(["sh", "-c", shellCmd], {
const proc = Bun.spawn(["bun", daemonScript, ...args], {
stdout: debugLogFd,
stderr: debugLogFd,
stdin: "ignore",