feat(processmonitor): Add CPU monitoring and display CPU in process list
This commit is contained in:
		@@ -1,5 +1,13 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## 2025-08-30 - 5.6.0 - feat(processmonitor)
 | 
			
		||||
Add CPU monitoring and display CPU in process list
 | 
			
		||||
 | 
			
		||||
- CLI: show a CPU column in the `tspm list` output (adds formatting and placeholder name display)
 | 
			
		||||
- Daemon: ProcessMonitor now collects CPU usage for the process group in addition to memory
 | 
			
		||||
- Daemon: ProcessMonitor exposes getLastCpuUsage() and ProcessManager syncs CPU values into IProcessInfo
 | 
			
		||||
- Non-breaking: UI and internal stats enriched to surface CPU metrics for processes
 | 
			
		||||
 | 
			
		||||
## 2025-08-30 - 5.5.0 - feat(logs)
 | 
			
		||||
Improve logs streaming and backlog delivery; add CLI filters and ndjson output
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,6 @@
 | 
			
		||||
 */
 | 
			
		||||
export const commitinfo = {
 | 
			
		||||
  name: '@git.zone/tspm',
 | 
			
		||||
  version: '5.5.0',
 | 
			
		||||
  version: '5.6.0',
 | 
			
		||||
  description: 'a no fuzz process manager'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,13 +20,13 @@ export function registerListCommand(smartcli: plugins.smartcli.Smartcli) {
 | 
			
		||||
 | 
			
		||||
      console.log('Process List:');
 | 
			
		||||
      console.log(
 | 
			
		||||
        '┌─────────┬─────────────┬───────────┬───────────┬──────────┬──────────┐',
 | 
			
		||||
        '┌─────────┬─────────────┬───────────┬───────────┬──────────┬──────────┬─────────┐',
 | 
			
		||||
      );
 | 
			
		||||
      console.log(
 | 
			
		||||
        '│ ID      │ Name        │ Status    │ PID       │ Memory   │ Restarts │',
 | 
			
		||||
        '│ ID      │ Name        │ Status    │ PID       │ Memory   │ CPU      │ Restarts │',
 | 
			
		||||
      );
 | 
			
		||||
      console.log(
 | 
			
		||||
        '├─────────┼─────────────┼───────────┼───────────┼──────────┼──────────┤',
 | 
			
		||||
        '├─────────┼─────────────┼───────────┼───────────┼──────────┼──────────┼──────────┤',
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      for (const proc of processes) {
 | 
			
		||||
@@ -38,13 +38,18 @@ export function registerListCommand(smartcli: plugins.smartcli.Smartcli) {
 | 
			
		||||
              : '\x1b[33m';
 | 
			
		||||
        const resetColor = '\x1b[0m';
 | 
			
		||||
 | 
			
		||||
        const cpuStr = typeof proc.cpu === 'number' && isFinite(proc.cpu)
 | 
			
		||||
          ? `${proc.cpu.toFixed(1)}%`
 | 
			
		||||
          : '-';
 | 
			
		||||
        // Name is not part of IProcessInfo; show ID as placeholder for now
 | 
			
		||||
        const nameDisplay = String(proc.id);
 | 
			
		||||
        console.log(
 | 
			
		||||
          `│ ${pad(String(proc.id), 7)} │ ${pad(String(proc.id), 11)} │ ${statusColor}${pad(proc.status, 9)}${resetColor} │ ${pad((proc.pid || '-').toString(), 9)} │ ${pad(formatMemory(proc.memory), 8)} │ ${pad(proc.restarts.toString(), 8)} │`,
 | 
			
		||||
          `│ ${pad(String(proc.id), 7)} │ ${pad(nameDisplay, 11)} │ ${statusColor}${pad(proc.status, 9)}${resetColor} │ ${pad((proc.pid || '-').toString(), 9)} │ ${pad(formatMemory(proc.memory), 8)} │ ${pad(cpuStr, 8)} │ ${pad(proc.restarts.toString(), 8)} │`,
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      console.log(
 | 
			
		||||
        '└─────────┴─────────────┴───────────┴───────────┴──────────┴──────────┘',
 | 
			
		||||
        '└─────────┴─────────────┴───────────┴───────────┴──────────┴──────────┴──────────┘',
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    { actionLabel: 'list processes' },
 | 
			
		||||
 
 | 
			
		||||
@@ -438,6 +438,13 @@ export class ProcessManager extends EventEmitter {
 | 
			
		||||
          info.uptime = uptime;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Update memory and cpu from latest monitor readings
 | 
			
		||||
        info.memory = monitor.getLastMemoryUsage();
 | 
			
		||||
        const cpu = monitor.getLastCpuUsage();
 | 
			
		||||
        if (Number.isFinite(cpu)) {
 | 
			
		||||
          info.cpu = cpu;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Update restart count
 | 
			
		||||
        info.restarts = monitor.getRestartCount();
 | 
			
		||||
        
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,8 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
  private lastRetryAt: number | null = null;
 | 
			
		||||
  private readonly MAX_RETRIES = 10;
 | 
			
		||||
  private readonly RESET_WINDOW_MS = 60 * 60 * 1000; // 1 hour
 | 
			
		||||
  private lastMemoryUsage: number = 0;
 | 
			
		||||
  private lastCpuUsage: number = 0;
 | 
			
		||||
 | 
			
		||||
  constructor(config: IMonitorConfig & { id?: ProcessId }) {
 | 
			
		||||
    super();
 | 
			
		||||
@@ -260,12 +262,16 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
    memoryLimit: number,
 | 
			
		||||
  ): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      const memoryUsage = await this.getProcessGroupMemory(pid);
 | 
			
		||||
      const { memory: memoryUsage, cpu: cpuUsage } = await this.getProcessGroupStats(pid);
 | 
			
		||||
 | 
			
		||||
      this.logger.debug(
 | 
			
		||||
        `Memory usage for PID ${pid}: ${this.humanReadableBytes(memoryUsage)} (${memoryUsage} bytes)`,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // Store latest readings
 | 
			
		||||
      this.lastMemoryUsage = memoryUsage;
 | 
			
		||||
      this.lastCpuUsage = cpuUsage;
 | 
			
		||||
 | 
			
		||||
      // Only log memory usage in debug mode to avoid spamming
 | 
			
		||||
      if (process.env.TSPM_DEBUG) {
 | 
			
		||||
        this.log(
 | 
			
		||||
@@ -303,7 +309,7 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the total memory usage (in bytes) for the process group (the main process and its children).
 | 
			
		||||
   */
 | 
			
		||||
  private getProcessGroupMemory(pid: number): Promise<number> {
 | 
			
		||||
  private getProcessGroupStats(pid: number): Promise<{ memory: number; cpu: number }> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      this.logger.debug(
 | 
			
		||||
        `Getting memory usage for process group with PID ${pid}`,
 | 
			
		||||
@@ -333,7 +339,7 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
          plugins.pidusage(
 | 
			
		||||
            pids,
 | 
			
		||||
            (err: Error | null, stats: Record<string, { memory: number }>) => {
 | 
			
		||||
            (err: Error | null, stats: Record<string, { memory: number; cpu: number }>) => {
 | 
			
		||||
              if (err) {
 | 
			
		||||
                const processError = new ProcessError(
 | 
			
		||||
                  `Failed to get process usage stats: ${err.message}`,
 | 
			
		||||
@@ -345,14 +351,16 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              let totalMemory = 0;
 | 
			
		||||
              let totalCpu = 0;
 | 
			
		||||
              for (const key in stats) {
 | 
			
		||||
                totalMemory += stats[key].memory;
 | 
			
		||||
                totalCpu += Number.isFinite(stats[key].cpu) ? stats[key].cpu : 0;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              this.logger.debug(
 | 
			
		||||
                `Total memory for process group: ${this.humanReadableBytes(totalMemory)}`,
 | 
			
		||||
              );
 | 
			
		||||
              resolve(totalMemory);
 | 
			
		||||
              resolve({ memory: totalMemory, cpu: totalCpu });
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
@@ -448,6 +456,20 @@ export class ProcessMonitor extends EventEmitter {
 | 
			
		||||
    return this.processWrapper?.isRunning() || false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get last measured memory usage for the process group (bytes)
 | 
			
		||||
   */
 | 
			
		||||
  public getLastMemoryUsage(): number {
 | 
			
		||||
    return this.lastMemoryUsage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get last measured CPU usage for the process group (sum of group, percent)
 | 
			
		||||
   */
 | 
			
		||||
  public getLastCpuUsage(): number {
 | 
			
		||||
    return this.lastCpuUsage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper method for logging messages with the instance name.
 | 
			
		||||
   */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user