Add support for Windows

- Native support for Windows Terminal (cmd).
This commit is contained in:
SondreB
2026-06-07 16:36:37 +02:00
parent 2e8bbc9aa0
commit ae8bfb9d77
5 changed files with 65 additions and 26 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": {
+47 -17
View File
@@ -15,7 +15,7 @@ import {
deleteClientAction,
addClientAction,
} from "./utils/clients";
import { existsSync, mkdirSync } from "fs";
import { existsSync, mkdirSync, readFileSync, statSync } from "fs";
import { execSync } from "child_process";
import {
CONFIG_DIR,
@@ -429,6 +429,7 @@ program
const installHint = config.cocodPath
? `Configured cocod executable was not found: ${config.cocodPath}`
: "cocod is not installed. Run 'routstrd onboard' first to install cocod.";
console.error(installHint);
logger.error(installHint);
process.exit(1);
}
@@ -1650,6 +1651,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")
@@ -1675,24 +1710,19 @@ 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);
await followLogFile(todayFile, lines);
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 {
ModelManager,
ProviderManager,
@@ -135,7 +135,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";
@@ -68,13 +69,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 -4
View File
@@ -1,6 +1,8 @@
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`;
@@ -39,10 +41,11 @@ 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(" ")}`;
const proc = Bun.spawn(["sh", "-c", shellCmd], {
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 proc = Bun.spawn(["bun", daemonScript, ...args], {
stdout: "ignore",
stderr: "ignore",
stdin: "ignore",