259 lines
6.6 KiB
TypeScript
259 lines
6.6 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as paths from './paths.js';
|
|
import { ProcessMonitor, type IMonitorConfig } from './classes.processmonitor.js';
|
|
import { TspmConfig } from './classes.config.js';
|
|
|
|
export interface IProcessConfig extends IMonitorConfig {
|
|
id: string; // Unique identifier for the process
|
|
autorestart: boolean; // Whether to restart the process automatically on crash
|
|
watch?: boolean; // Whether to watch for file changes and restart
|
|
watchPaths?: string[]; // Paths to watch for changes
|
|
}
|
|
|
|
export interface IProcessInfo {
|
|
id: string;
|
|
pid?: number;
|
|
status: 'online' | 'stopped' | 'errored';
|
|
memory: number;
|
|
cpu?: number;
|
|
uptime?: number;
|
|
restarts: number;
|
|
}
|
|
|
|
export interface IProcessLog {
|
|
timestamp: Date;
|
|
type: 'stdout' | 'stderr' | 'system';
|
|
message: string;
|
|
}
|
|
|
|
export class Tspm {
|
|
private processes: Map<string, ProcessMonitor> = new Map();
|
|
private processConfigs: Map<string, IProcessConfig> = new Map();
|
|
private processInfo: Map<string, IProcessInfo> = new Map();
|
|
private config: TspmConfig;
|
|
private configStorageKey = 'processes';
|
|
|
|
constructor() {
|
|
this.config = new TspmConfig();
|
|
this.loadProcessConfigs();
|
|
}
|
|
|
|
/**
|
|
* Start a new process with the given configuration
|
|
*/
|
|
public async start(config: IProcessConfig): Promise<void> {
|
|
// Check if process with this id already exists
|
|
if (this.processes.has(config.id)) {
|
|
throw new Error(`Process with id '${config.id}' already exists`);
|
|
}
|
|
|
|
// Create and store process config
|
|
this.processConfigs.set(config.id, config);
|
|
|
|
// Initialize process info
|
|
this.processInfo.set(config.id, {
|
|
id: config.id,
|
|
status: 'stopped',
|
|
memory: 0,
|
|
restarts: 0
|
|
});
|
|
|
|
// Create and start process monitor
|
|
const monitor = new ProcessMonitor({
|
|
name: config.name || config.id,
|
|
projectDir: config.projectDir,
|
|
command: config.command,
|
|
args: config.args,
|
|
memoryLimitBytes: config.memoryLimitBytes,
|
|
monitorIntervalMs: config.monitorIntervalMs
|
|
});
|
|
|
|
this.processes.set(config.id, monitor);
|
|
monitor.start();
|
|
|
|
// Update process info
|
|
this.updateProcessInfo(config.id, { status: 'online' });
|
|
|
|
// Save updated configs
|
|
await this.saveProcessConfigs();
|
|
}
|
|
|
|
/**
|
|
* Stop a process by id
|
|
*/
|
|
public async stop(id: string): Promise<void> {
|
|
const monitor = this.processes.get(id);
|
|
if (!monitor) {
|
|
throw new Error(`Process with id '${id}' not found`);
|
|
}
|
|
|
|
monitor.stop();
|
|
this.updateProcessInfo(id, { status: 'stopped' });
|
|
|
|
// Don't remove from the maps, just mark as stopped
|
|
// This allows it to be restarted later
|
|
}
|
|
|
|
/**
|
|
* Restart a process by id
|
|
*/
|
|
public async restart(id: string): Promise<void> {
|
|
const monitor = this.processes.get(id);
|
|
const config = this.processConfigs.get(id);
|
|
|
|
if (!monitor || !config) {
|
|
throw new Error(`Process with id '${id}' not found`);
|
|
}
|
|
|
|
// Stop and then start the process
|
|
monitor.stop();
|
|
|
|
// Create a new monitor instance
|
|
const newMonitor = new ProcessMonitor({
|
|
name: config.name || config.id,
|
|
projectDir: config.projectDir,
|
|
command: config.command,
|
|
args: config.args,
|
|
memoryLimitBytes: config.memoryLimitBytes,
|
|
monitorIntervalMs: config.monitorIntervalMs
|
|
});
|
|
|
|
this.processes.set(id, newMonitor);
|
|
newMonitor.start();
|
|
|
|
// Update restart count
|
|
const info = this.processInfo.get(id);
|
|
if (info) {
|
|
this.updateProcessInfo(id, {
|
|
status: 'online',
|
|
restarts: info.restarts + 1
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a process by id
|
|
*/
|
|
public async delete(id: string): Promise<void> {
|
|
// Stop the process if it's running
|
|
try {
|
|
await this.stop(id);
|
|
} catch (error) {
|
|
// Ignore errors if the process is not running
|
|
}
|
|
|
|
// Remove from all maps
|
|
this.processes.delete(id);
|
|
this.processConfigs.delete(id);
|
|
this.processInfo.delete(id);
|
|
|
|
// Save updated configs
|
|
await this.saveProcessConfigs();
|
|
}
|
|
|
|
/**
|
|
* Get a list of all process infos
|
|
*/
|
|
public list(): IProcessInfo[] {
|
|
return Array.from(this.processInfo.values());
|
|
}
|
|
|
|
/**
|
|
* Get detailed info for a specific process
|
|
*/
|
|
public describe(id: string): { config: IProcessConfig; info: IProcessInfo } | null {
|
|
const config = this.processConfigs.get(id);
|
|
const info = this.processInfo.get(id);
|
|
|
|
if (!config || !info) {
|
|
return null;
|
|
}
|
|
|
|
return { config, info };
|
|
}
|
|
|
|
/**
|
|
* Get process logs
|
|
*/
|
|
public getLogs(id: string, limit?: number): IProcessLog[] {
|
|
const monitor = this.processes.get(id);
|
|
if (!monitor) {
|
|
return [];
|
|
}
|
|
|
|
return monitor.getLogs(limit);
|
|
}
|
|
|
|
/**
|
|
* Start all saved processes
|
|
*/
|
|
public async startAll(): Promise<void> {
|
|
for (const [id, config] of this.processConfigs.entries()) {
|
|
if (!this.processes.has(id)) {
|
|
await this.start(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop all running processes
|
|
*/
|
|
public async stopAll(): Promise<void> {
|
|
for (const id of this.processes.keys()) {
|
|
await this.stop(id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restart all processes
|
|
*/
|
|
public async restartAll(): Promise<void> {
|
|
for (const id of this.processes.keys()) {
|
|
await this.restart(id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the info for a process
|
|
*/
|
|
private updateProcessInfo(id: string, update: Partial<IProcessInfo>): void {
|
|
const info = this.processInfo.get(id);
|
|
if (info) {
|
|
this.processInfo.set(id, { ...info, ...update });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save all process configurations to config storage
|
|
*/
|
|
private async saveProcessConfigs(): Promise<void> {
|
|
const configs = Array.from(this.processConfigs.values());
|
|
await this.config.writeKey(this.configStorageKey, JSON.stringify(configs));
|
|
}
|
|
|
|
/**
|
|
* Load process configurations from config storage
|
|
*/
|
|
private async loadProcessConfigs(): Promise<void> {
|
|
try {
|
|
const configsJson = await this.config.readKey(this.configStorageKey);
|
|
if (configsJson) {
|
|
const configs = JSON.parse(configsJson) as IProcessConfig[];
|
|
for (const config of configs) {
|
|
this.processConfigs.set(config.id, config);
|
|
|
|
// Initialize process info
|
|
this.processInfo.set(config.id, {
|
|
id: config.id,
|
|
status: 'stopped',
|
|
memory: 0,
|
|
restarts: 0
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// If no configs found or error reading, just continue with empty configs
|
|
console.log('No saved process configurations found');
|
|
}
|
|
}
|
|
} |