cleanup processControl

This commit is contained in:
ThatBen 2025-04-14 13:56:55 +02:00
parent 7661d829cb
commit 5fcb55015a
No known key found for this signature in database
GPG Key ID: E020A7DDCD52E1AB
7 changed files with 100 additions and 75 deletions

View File

@ -15,6 +15,7 @@ export const mockConfigService = {
get: vi.fn(),
saveConfig: vi.fn(),
loadConfig: vi.fn(),
writeCodexConfigFile: vi.fn(),
};
export const mockFsService = {
@ -32,6 +33,7 @@ export const mockFsService = {
export const mockShellService = {
run: vi.fn(),
spawnDetachedProcess: vi.fn(),
};
export const mockOsService = {
@ -39,4 +41,10 @@ export const mockOsService = {
isDarwin: vi.fn(),
isLinux: vi.fn(),
getWorkingDir: vi.fn(),
listProcesses: vi.fn(),
};
export const mockCodexGlobals = {
getPublicIp: vi.fn(),
getTestnetSPRs: vi.fn(),
};

View File

@ -1,95 +1,49 @@
import fs from "fs";
import { spawn, exec } from "child_process";
import psList from 'ps-list';
export class ProcessControl {
constructor(configService, shellService, osService, fsService) {
constructor(configService, shellService, osService, fsService, codexGlobals) {
this.configService = configService;
this.config = configService.get();
this.shell = shellService;
this.os = osService;
this.fs = fsService;
this.codexGlobals;
}
getPublicIp = async () => {
getCodexProcesses = async () => {
const processes = await this.os.listProcesses();
if (this.os.isWindows()) {
const result = await this.shell.run(
"for /f \"delims=\" %a in ('curl -s --ssl-reqd ip.codex.storage') do @echo %a",
);
return result.trim();
return processes.filter((p) => p.name === "codex.exe");
} else {
return await this.shell.run("curl -s https://ip.codex.storage");
return processes.filter((p) => p.name === "codex");
}
};
detectThing = async () => {
console.log("detecting...");
getNumberOfCodexProcesses = async () => {
return (await this.getCodexProcesses()).length;
};
const processes = await psList();
const codexProcesses = processes.filter((p) => p.name === "codex.exe");
if (codexProcesses.length > 0) {
console.log("Codex is already running.");
codexProcesses.forEach((p) => {
console.log(`PID: ${JSON.stringify(p)}`);
});
console.log("Stopping codex...");
await this.stopThing(codexProcesses[0].pid);
await this.detectThing();
} else {
console.log("Codex is not running.");
}
}
stopThing = async (pid) => {
console.log("stopping process...");
stopCodexProcess = async () => {
const processes = await this.getCodexProcesses();
if (processes.length < 1) throw new Error("No codex process found");
const pid = processes[0].pid;
process.kill(pid, "SIGINT");
await new Promise((resolve) => setTimeout(resolve, 2000));
}
};
doThing = async () => {
if (this.config.dataDir.length < 1)
throw new Error("Missing config: dataDir");
if (this.config.logsDir.length < 1)
throw new Error("Missing config: logsDir");
startCodexProcess = async () => {
this.saveCodexConfigFile();
this.startCodex();
};
console.log("start a codex detached");
saveCodexConfigFile = async () => {
const publicIp = await this.codexGlobals.getPublicIp();
const bootstrapNodes = await this.codexGlobals.getTestnetSPRs();
this.configService.writeCodexConfigFile(publicIp, bootstrapNodes);
};
startCodex = async () => {
const executable = this.config.codexExe;
const args = [`--config-file=${this.config.codexConfigFilePath}`];
const bootstrapNodes = [
"spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P",
];
const publicIp = await this.getPublicIp();
this.configService.writeCodexConfigFile(publicIp, bootstrapNodes);
const command = `"${executable}" ${args.join(" ")}`;
console.log("command: " + command);
console.log("\n\n");
var child = spawn(executable, args, {
detached: true,
//stdio: ["ignore", "ignore", "ignore"],
});
child.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});
child.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});
child.on("close", (code) => {
console.log(`child process exited with code ${code}`);
});
child.unref();
await new Promise((resolve) => setTimeout(resolve, 2000));
return;
await this.shell.spawnDetachedProcess(executable, args);
};
}

View File

@ -35,6 +35,7 @@ import { Installer } from "./handlers/installer.js";
import { ShellService } from "./services/shellService.js";
import { OsService } from "./services/osService.js";
import { ProcessControl } from "./handlers/processControl.js";
import { CodexGlobals } from "./services/codexGlobals.js";
async function showNavigationMenu() {
console.log("\n");
@ -133,14 +134,20 @@ export async function main() {
new DataDirMover(fsService, uiService),
);
const codexGlobals = new CodexGlobals();
const processControl = new ProcessControl(
configService,
shellService,
osService,
fsService,
);
console.log("ip: " + (await codexGlobals.getPublicIp()));
console.log("spr: " + (await codexGlobals.getTestnetSprs()));
//await processControl.doThing();
await processControl.detectThing();
// await processControl.detectThing();
return;
await mainMenu.show();

View File

@ -0,0 +1,12 @@
import axios from "axios";
export class CodexGlobals {
getPublicIp = async () => {
return (await axios.get(`https://ip.codex.storage`)).data;
};
getTestnetSPRs = async () => {
const result = (await axios.get(`https://spr.codex.storage/testnet`)).data;
return result.split("\n").filter((line) => line.length > 0);
};
}

View File

@ -77,7 +77,23 @@ export class ConfigService {
return this.fs.pathJoin([this.config.logsDir, "codex.log"]);
};
missing = (name) => {
throw new Error(`Missing config value: ${name}`);
};
validateConfiguration = () => {
if (this.config.codexExe.length < 1) this.missing("codexExe");
if (this.config.codexConfigFilePath.length < 1)
this.missing("codexConfigFilePath");
if (this.config.dataDir.length < 1) this.missing("dataDir");
if (this.config.logsDir.length < 1) this.missing("logsDir");
if (this.config.storageQuota < 1024 * 1024 * 100)
throw new Error("Storage quota must be at least 100MB");
};
writeCodexConfigFile = (publicIp, bootstrapNodes) => {
this.validateConfiguration();
const nl = "\n";
const bootNodes = bootstrapNodes.map((v) => `"${v}"`).join(",");

View File

@ -1,4 +1,5 @@
import os from "os";
import psList from "ps-list";
export class OsService {
constructor() {
@ -20,4 +21,8 @@ export class OsService {
getWorkingDir = () => {
return process.cwd();
};
listProcesses = async () => {
await psList();
};
}

View File

@ -1,4 +1,4 @@
import { exec } from "child_process";
import { exec, spawn } from "child_process";
import { promisify } from "util";
export class ShellService {
@ -6,7 +6,7 @@ export class ShellService {
this.execAsync = promisify(exec);
}
async run(command) {
run = async (command) => {
try {
const { stdout, stderr } = await this.execAsync(command);
return stdout;
@ -14,5 +14,28 @@ export class ShellService {
console.error("Error:", error.message);
throw error;
}
}
};
spawnDetachedProcess = async (cmd, args) => {
var child = spawn(cmd, args, {
detached: true,
stdio: ["ignore", "ignore", "ignore"],
});
// child.stdout.on("data", (data) => {
// console.log(`stdout: ${data}`);
// });
// child.stderr.on("data", (data) => {
// console.error(`stderr: ${data}`);
// });
// child.on("close", (code) => {
// console.log(`child process exited with code ${code}`);
// });
child.unref();
await new Promise((resolve) => setTimeout(resolve, 2000));
};
}