pyre-cli 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import fs4 from "fs";
5
+ import os from "os";
5
6
  import readline2 from "readline";
6
7
  import { Command } from "commander";
7
8
  import chalk9 from "chalk";
@@ -504,15 +505,14 @@ async function collectProcesses(limit) {
504
505
  try {
505
506
  const isMac = process.platform === "darwin";
506
507
  const sortArg = isMac ? "-r" : "--sort=-pcpu";
507
- const threadsArg = isMac ? "threads" : "nlwp";
508
508
  const headClause = limit && limit > 0 ? ` | head -n ${limit + 1}` : "";
509
- const raw = (await run(`ps -eo pid,ppid,user,pcpu,pmem,state,${threadsArg},time,comm ${sortArg}${headClause}`)).trim();
509
+ const raw = (await run(`ps -eo pid,ppid,user,pcpu,pmem,state,time,comm ${sortArg}${headClause}`)).trim();
510
510
  const lines = raw.split("\n").slice(1);
511
511
  return lines.map((line) => {
512
- const parts = line.match(/\s*(\d+)\s+(\d+)\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+(\d+)\s+([\d:.]+)\s+(.+)/);
512
+ const parts = line.match(/\s*(\d+)\s+(\d+)\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+([\d:.]+)\s+(.+)/);
513
513
  if (!parts) return null;
514
- const runtimeSec = parts[8].split(":").reduce((acc, val, idx) => acc + parseInt(val) * Math.pow(60, 2 - idx), 0);
515
- return { pid: parseInt(parts[1]), ppid: parseInt(parts[2]), user: parts[3], cpu: parseFloat(parts[4]), mem: parseFloat(parts[5]), state: parts[6], threads: parseInt(parts[7]), runtime: runtimeSec, command: parts[9] };
514
+ const runtimeSec = parts[7].split(":").reduce((acc, val, idx) => acc + parseInt(val) * Math.pow(60, 2 - idx), 0);
515
+ return { pid: parseInt(parts[1]), ppid: parseInt(parts[2]), user: parts[3], cpu: parseFloat(parts[4]), mem: parseFloat(parts[5]), state: parts[6], threads: 0, runtime: runtimeSec, command: parts[8] };
516
516
  }).filter((p) => p !== null && p.pid > 0);
517
517
  } catch {
518
518
  return [];
@@ -3222,8 +3222,45 @@ function stripAnsi(s) {
3222
3222
  function sanitizeHost2(host) {
3223
3223
  return host.trim().replace(/%$/, "");
3224
3224
  }
3225
+ function detectLocalIP() {
3226
+ const ifaces = os.networkInterfaces();
3227
+ for (const [, addrs] of Object.entries(ifaces)) {
3228
+ if (!addrs) continue;
3229
+ for (const addr of addrs) {
3230
+ if (addr.family === "IPv4" && !addr.internal) {
3231
+ return addr.address;
3232
+ }
3233
+ }
3234
+ }
3235
+ return null;
3236
+ }
3237
+ async function runServerCommand() {
3238
+ const ip = detectLocalIP();
3239
+ if (!ip) {
3240
+ console.log(chalk9.red("Could not detect a non-loopback IP address. Connect to a network first."));
3241
+ process.exit(1);
3242
+ }
3243
+ const port = 9876;
3244
+ const password = "mysecret";
3245
+ console.log(chalk9.bold(`
3246
+ pyre server`));
3247
+ console.log(chalk9.dim(` Detected IP: ${ip}`));
3248
+ console.log(chalk9.dim(` Port: ${port}`));
3249
+ console.log(chalk9.dim(` Password: ${password}`));
3250
+ console.log();
3251
+ console.log(chalk9.bold("Run this on THIS machine (server):"));
3252
+ console.log(chalk9.hex("#ff6a39")(`pyre p2p server --p2p-host 0.0.0.0 --p2p-port ${port} --p2p-password ${password}`));
3253
+ console.log();
3254
+ console.log(chalk9.bold("Run this on the OTHER machine (client):"));
3255
+ console.log(chalk9.hex("#ff6a39")(`pyre p2p connect --p2p-host ${ip} --p2p-port ${port} --p2p-password ${password}`));
3256
+ console.log();
3257
+ }
3225
3258
  async function main() {
3226
3259
  const cmd = program.args[0];
3260
+ if (cmd === "server") {
3261
+ await runServerCommand();
3262
+ return;
3263
+ }
3227
3264
  if (cmd === "live" || !isExportMode() && !opts.once && cmd !== "p2p") {
3228
3265
  if (cmd === "live") {
3229
3266
  program.args.shift();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyre-cli",
3
- "version": "4.13.0",
3
+ "version": "4.15.0",
4
4
  "description": "CLI Mac monitoring tool: temps, system stats, live dashboard, packet monitor, battery predictor, export",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * P2P mode enables live data streaming between two systems over TCP.
8
8
  */
9
9
  import fs from 'node:fs';
10
+ import os from 'node:os';
10
11
  import readline from 'node:readline';
11
12
  import { Command } from 'commander';
12
13
  import chalk from 'chalk';
@@ -69,9 +70,53 @@ function sanitizeHost(host: string): string {
69
70
  return host.trim().replace(/%$/, '');
70
71
  }
71
72
 
73
+ function detectLocalIP(): string | null {
74
+ const ifaces = os.networkInterfaces();
75
+ for (const [, addrs] of Object.entries(ifaces)) {
76
+ if (!addrs) continue;
77
+ for (const addr of addrs) {
78
+ if (addr.family === 'IPv4' && !addr.internal) {
79
+ return addr.address;
80
+ }
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+
86
+ async function runServerCommand(): Promise<void> {
87
+ const ip = detectLocalIP();
88
+
89
+ if (!ip) {
90
+ console.log(chalk.red('Could not detect a non-loopback IP address. Connect to a network first.'));
91
+ process.exit(1);
92
+ }
93
+
94
+ const port = 9876;
95
+ const password = 'mysecret';
96
+
97
+ console.log(chalk.bold(`\n pyre server`));
98
+ console.log(chalk.dim(` Detected IP: ${ip}`));
99
+ console.log(chalk.dim(` Port: ${port}`));
100
+ console.log(chalk.dim(` Password: ${password}`));
101
+ console.log();
102
+
103
+ console.log(chalk.bold('Run this on THIS machine (server):'));
104
+ console.log(chalk.hex('#ff6a39')(`pyre p2p server --p2p-host 0.0.0.0 --p2p-port ${port} --p2p-password ${password}`));
105
+ console.log();
106
+
107
+ console.log(chalk.bold('Run this on the OTHER machine (client):'));
108
+ console.log(chalk.hex('#ff6a39')(`pyre p2p connect --p2p-host ${ip} --p2p-port ${port} --p2p-password ${password}`));
109
+ console.log();
110
+ }
111
+
72
112
  async function main() {
73
113
  const cmd = program.args[0];
74
114
 
115
+ if (cmd === 'server') {
116
+ await runServerCommand();
117
+ return;
118
+ }
119
+
75
120
  if (cmd === 'live' || (!isExportMode() && !opts.once && cmd !== 'p2p')) {
76
121
  if (cmd === 'live') {
77
122
  program.args.shift();
@@ -606,20 +606,19 @@ export async function collectNetwork(): Promise<NetworkData> {
606
606
 
607
607
  export async function collectProcesses(limit?: number): Promise<ProcessData[]> {
608
608
  try {
609
- const isMac = process.platform === 'darwin';
610
- const sortArg = isMac ? '-r' : '--sort=-pcpu';
611
- const threadsArg = isMac ? 'threads' : 'nlwp';
612
- const headClause = limit && limit > 0 ? ` | head -n ${limit + 1}` : '';
613
- const raw = (await run(`ps -eo pid,ppid,user,pcpu,pmem,state,${threadsArg},time,comm ${sortArg}${headClause}`)).trim();
614
- const lines = raw.split('\n').slice(1);
615
- return lines
616
- .map(line => {
617
- const parts = line.match(/\s*(\d+)\s+(\d+)\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+(\d+)\s+([\d:.]+)\s+(.+)/);
618
- if (!parts) return null;
619
- const runtimeSec = parts[8].split(':').reduce((acc, val, idx) => acc + parseInt(val) * Math.pow(60, 2 - idx), 0);
620
- return { pid: parseInt(parts[1]), ppid: parseInt(parts[2]), user: parts[3], cpu: parseFloat(parts[4]), mem: parseFloat(parts[5]), state: parts[6], threads: parseInt(parts[7]), runtime: runtimeSec, command: parts[9] };
621
- })
622
- .filter((p): p is ProcessData => p !== null && p.pid > 0);
609
+ const isMac = process.platform === 'darwin';
610
+ const sortArg = isMac ? '-r' : '--sort=-pcpu';
611
+ const headClause = limit && limit > 0 ? ` | head -n ${limit + 1}` : '';
612
+ const raw = (await run(`ps -eo pid,ppid,user,pcpu,pmem,state,time,comm ${sortArg}${headClause}`)).trim();
613
+ const lines = raw.split('\n').slice(1);
614
+ return lines
615
+ .map(line => {
616
+ const parts = line.match(/\s*(\d+)\s+(\d+)\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+([\d:.]+)\s+(.+)/);
617
+ if (!parts) return null;
618
+ const runtimeSec = parts[7].split(':').reduce((acc, val, idx) => acc + parseInt(val) * Math.pow(60, 2 - idx), 0);
619
+ return { pid: parseInt(parts[1]), ppid: parseInt(parts[2]), user: parts[3], cpu: parseFloat(parts[4]), mem: parseFloat(parts[5]), state: parts[6], threads: 0, runtime: runtimeSec, command: parts[8] };
620
+ })
621
+ .filter((p): p is ProcessData => p !== null && p.pid > 0);
623
622
  } catch {
624
623
  return [];
625
624
  }