pyre-cli 10.3.0 → 10.5.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.
Files changed (88) hide show
  1. package/README.md +41 -0
  2. package/dist/{alertChannels-TPSQIJ7S.js → alertChannels-MNX4XH6J.js} +1 -0
  3. package/dist/{anomaliesCmd-25OFKYPK.js → anomaliesCmd-HHTY5LYL.js} +2 -0
  4. package/dist/benchGitCmd-HIXUC5EK.js +87 -0
  5. package/dist/{benchmarkCmd-A3ETK6VY.js → benchmarkCmd-B7DLP2VC.js} +2 -0
  6. package/dist/{blender-FGH3ASEQ.js → blender-6ZX5SPYC.js} +3 -1
  7. package/dist/{brewHealth-P2GW5Y5C.js → brewHealth-VFW33QB3.js} +1 -0
  8. package/dist/budgetCmd-TGO3RYLN.js +11 -0
  9. package/dist/{buildCmd-CDPZ2ZFA.js → buildCmd-E7KUCFYG.js} +3 -1
  10. package/dist/{checkCmd-WQAHAUWO.js → checkCmd-QLYTP77Y.js} +5 -3
  11. package/dist/chunk-3RG5ZIWI.js +10 -0
  12. package/dist/{chunk-VIACN2RU.js → chunk-FYCBGUTW.js} +41 -19
  13. package/dist/chunk-JH7P6KLN.js +143 -0
  14. package/dist/{chunk-QM5L43EK.js → chunk-LDJB5V4N.js} +5 -1
  15. package/dist/chunk-NMGQVRCA.js +111 -0
  16. package/dist/chunk-OCL7P7N7.js +936 -0
  17. package/dist/{chunk-23YWCI3O.js → chunk-QEH4LR6P.js} +28 -686
  18. package/dist/{chunk-KVUMOA6C.js → chunk-RKPGPVTO.js} +77 -4
  19. package/dist/chunk-UF3XQ3R3.js +3800 -0
  20. package/dist/{collectors-JDN5Y7EV.js → collectors-V7SCIIDH.js} +5 -3
  21. package/dist/{completions-ZVTNE52B.js → completions-FTPXDSR2.js} +44 -3
  22. package/dist/{config-J4YMADEF.js → config-5WW5M63G.js} +2 -1
  23. package/dist/containersCmd-FSMYOBG2.js +84 -0
  24. package/dist/{doctor-LHFW4UJP.js → doctor-O4DIAMHK.js} +3 -2
  25. package/dist/{explainCmd-MF6PIMLW.js → explainCmd-7PBPA7O5.js} +1 -0
  26. package/dist/{extensions-XJGHG7X3.js → extensions-QQEV7U3D.js} +1 -0
  27. package/dist/{firebaseSync-ON3RZ6KX.js → firebaseSync-LRN67UTV.js} +2 -0
  28. package/dist/flightRecorder-GJIAQOXC.js +162 -0
  29. package/dist/index.js +736 -4125
  30. package/dist/inspectCmd-5FXJBJTK.js +137 -0
  31. package/dist/menu-AJFGDLBP.js +1233 -0
  32. package/dist/monitors/platform/windows-ui.ps1 +31 -29
  33. package/dist/{netUsageCmd-67IBZZYN.js → netUsageCmd-JP6YKBPF.js} +7 -5
  34. package/dist/optimizeCmd-WZKLFYZU.js +1110 -0
  35. package/dist/{pipeCmd-2N4JPKYE.js → pipeCmd-CPK6FVKB.js} +5 -3
  36. package/dist/playbookCmd-FRROIEXR.js +145 -0
  37. package/dist/{profileProcCmd-N4H6MCBT.js → profileProcCmd-5Y3733E7.js} +64 -13
  38. package/dist/{prometheus-5BL5PVLC.js → prometheus-CYNUR3QT.js} +1 -0
  39. package/dist/{prometheusCmd-LEUM5WCZ.js → prometheusCmd-G2CSEZRQ.js} +5 -3
  40. package/dist/{replayCmd-Z3PYXY7V.js → replayCmd-ODB5SPOQ.js} +25 -6
  41. package/dist/{run-HOGQDDTA.js → run-UJQYT2TO.js} +1 -0
  42. package/dist/{serveCmd-L7LVJOZN.js → serveCmd-KDPWLVQI.js} +5 -3
  43. package/dist/{service-7GIX3YNC.js → service-LIYYR3UI.js} +1 -0
  44. package/dist/{smartCmd-ODWKSFNG.js → smartCmd-2BJJ37GV.js} +130 -5
  45. package/dist/statuslineCmd-EUKYRR2G.js +75 -0
  46. package/dist/{stressCmd-MFXMFXYX.js → stressCmd-GKHGY26X.js} +2 -0
  47. package/dist/{syncCmd-42IRF633.js → syncCmd-MIBFKX24.js} +16 -5
  48. package/dist/topoCmd-RTXQJ6XA.js +189 -0
  49. package/dist/{updateCheck-52X6LJG5.js → updateCheck-LJHEMG3G.js} +2 -0
  50. package/dist/{watchdogCmd-6GR4UN6B.js → watchdogCmd-XM6OPTKM.js} +41 -6
  51. package/dist/{wizard-JQMQWFAW.js → wizard-PRDOJROQ.js} +2 -1
  52. package/package.json +2 -2
  53. package/src/benchGitCmd.ts +110 -0
  54. package/src/budgetCmd.ts +199 -0
  55. package/src/completions.ts +42 -3
  56. package/src/containersCmd.ts +106 -0
  57. package/src/doctor.ts +86 -6
  58. package/src/easy_features.test.ts +83 -0
  59. package/src/flightRecorder.ts +214 -0
  60. package/src/index.ts +181 -8
  61. package/src/inspectCmd.ts +165 -0
  62. package/src/live/input.ts +70 -3
  63. package/src/live/render.ts +1 -1
  64. package/src/live/state.ts +2 -1
  65. package/src/menu/commands.ts +754 -0
  66. package/src/menu/index.ts +358 -0
  67. package/src/menu/render.ts +272 -0
  68. package/src/menu/types.ts +63 -0
  69. package/src/menu.test.ts +110 -0
  70. package/src/monitors/blender.ts +49 -19
  71. package/src/monitors/buildTracker.ts +72 -54
  72. package/src/monitors/platform/windows-ui.ps1 +31 -29
  73. package/src/monitors/platform/windows.ts +636 -280
  74. package/src/optimizeCmd.ts +1248 -0
  75. package/src/platform.test.ts +146 -0
  76. package/src/playbookCmd.ts +156 -0
  77. package/src/power_features.test.ts +251 -0
  78. package/src/profileProcCmd.ts +68 -21
  79. package/src/replayCmd.ts +15 -6
  80. package/src/smartCmd.ts +149 -10
  81. package/src/state/config.ts +13 -0
  82. package/src/statuslineCmd.ts +87 -0
  83. package/src/syncCmd.ts +12 -0
  84. package/src/topoCmd.ts +83 -50
  85. package/src/watchdogCmd.ts +40 -3
  86. package/src/xbar.ts +195 -17
  87. package/dist/chunk-M73HCQD2.js +0 -95
  88. package/dist/topoCmd-DZ3YS5IL.js +0 -148
@@ -0,0 +1,110 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ALL_COMMANDS, CATEGORIES, filterCommands } from './menu/commands.js';
4
+ import { renderMenuScreen } from './menu/render.js';
5
+ import type { MenuState } from './menu/types.js';
6
+
7
+ describe('TUI Command Center & Tool Navigator', () => {
8
+ it('should define a comprehensive catalog of Pyre commands', () => {
9
+ assert.ok(ALL_COMMANDS.length >= 30, `Expected at least 30 commands, got ${ALL_COMMANDS.length}`);
10
+ const ids = ALL_COMMANDS.map((c) => c.id);
11
+ assert.ok(ids.includes('live'));
12
+ assert.ok(ids.includes('check'));
13
+ assert.ok(ids.includes('doctor'));
14
+ assert.ok(ids.includes('smart'));
15
+ assert.ok(ids.includes('anomalies'));
16
+ assert.ok(ids.includes('benchmark'));
17
+ assert.ok(ids.includes('optimize'));
18
+ assert.ok(ids.includes('brew'));
19
+ assert.ok(ids.includes('netusage'));
20
+ assert.ok(ids.includes('ui'));
21
+ assert.ok(ids.includes('web'));
22
+ });
23
+
24
+ it('should categorize every command into a valid category', () => {
25
+ const validCatIds = CATEGORIES.map((c) => c.id);
26
+ for (const cmd of ALL_COMMANDS) {
27
+ assert.ok(
28
+ validCatIds.includes(cmd.category),
29
+ `Command ${cmd.id} has invalid category: ${cmd.category}`
30
+ );
31
+ assert.ok(cmd.name.length > 0);
32
+ assert.ok(cmd.cli.startsWith('pyre '));
33
+ assert.ok(cmd.description.length > 0);
34
+ assert.ok(Array.isArray(cmd.tags));
35
+ assert.ok(cmd.tags.length > 0);
36
+ }
37
+ });
38
+
39
+ it('should filter commands by category', () => {
40
+ const healthCmds = filterCommands(ALL_COMMANDS, 'health', '');
41
+ assert.ok(healthCmds.length >= 4);
42
+ assert.ok(healthCmds.some((c) => c.id === 'doctor'));
43
+ assert.ok(healthCmds.some((c) => c.id === 'check'));
44
+ assert.ok(healthCmds.some((c) => c.id === 'smart'));
45
+ assert.ok(healthCmds.every((c) => c.category === 'health'));
46
+
47
+ const perfCmds = filterCommands(ALL_COMMANDS, 'performance', '');
48
+ assert.ok(perfCmds.some((c) => c.id === 'benchmark'));
49
+ assert.ok(perfCmds.some((c) => c.id === 'stress'));
50
+ });
51
+
52
+ it('should fuzzy search commands by name, tag, or description', () => {
53
+ // Search by exact name
54
+ const docResults = filterCommands(ALL_COMMANDS, 'all', 'doctor');
55
+ assert.strictEqual(docResults[0].id, 'doctor');
56
+
57
+ // Search by tag
58
+ const nvmeResults = filterCommands(ALL_COMMANDS, 'all', 'nvme');
59
+ assert.ok(nvmeResults.some((c) => c.id === 'smart'));
60
+
61
+ // Search by keyword in description
62
+ const idleResults = filterCommands(ALL_COMMANDS, 'all', 'idle');
63
+ assert.ok(idleResults.some((c) => c.id === 'optimize'));
64
+ });
65
+
66
+ it('should render menu screen without crashing', () => {
67
+ const state: MenuState = {
68
+ category: 'all',
69
+ categoryIndex: 0,
70
+ searchQuery: '',
71
+ selectedIndex: 0,
72
+ scrollOffset: 0,
73
+ inputMode: 'search',
74
+ argPromptValue: '',
75
+ activeCommand: null,
76
+ statusMessage: '',
77
+ termWidth: 100,
78
+ termHeight: 30,
79
+ theme: 'default',
80
+ };
81
+
82
+ const rendered = renderMenuScreen(state, ALL_COMMANDS);
83
+ assert.ok(typeof rendered === 'string');
84
+ assert.ok(rendered.includes('Command Center'));
85
+ assert.ok(rendered.includes('Search:'));
86
+ assert.ok(rendered.includes('pyre live'));
87
+ });
88
+
89
+ it('should render argument prompt modal when inputMode is prompt-args', () => {
90
+ const benchCmd = ALL_COMMANDS.find((c) => c.id === 'bench')!;
91
+ const state: MenuState = {
92
+ category: 'performance',
93
+ categoryIndex: 3,
94
+ searchQuery: '',
95
+ selectedIndex: 0,
96
+ scrollOffset: 0,
97
+ inputMode: 'prompt-args',
98
+ argPromptValue: 'npm test',
99
+ activeCommand: benchCmd,
100
+ statusMessage: '',
101
+ termWidth: 100,
102
+ termHeight: 30,
103
+ theme: 'default',
104
+ };
105
+
106
+ const rendered = renderMenuScreen(state, [benchCmd]);
107
+ assert.ok(rendered.includes('Execute:'));
108
+ assert.ok(rendered.includes('npm test'));
109
+ });
110
+ });
@@ -1,13 +1,17 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
1
4
  import { run } from './run.js';
2
5
  import type { BlenderRenderData } from './types.js';
6
+ import { collectWindowsProcesses } from './platform/windows.js';
3
7
 
4
- const RENDER_LOG_DIR = '/tmp/blender_render_logs';
8
+ const RENDER_LOG_DIR = path.join(os.tmpdir(), 'blender_render_logs');
5
9
 
6
10
  function blendFileFromArgs(args: string[]): string {
7
11
  const nonFlag = args.filter(a => !a.startsWith('-'));
8
12
  for (const a of nonFlag) {
9
13
  if (a.endsWith('.blend') || a.includes('.blend')) {
10
- return a.split('/').pop() || a;
14
+ return a.split(/[/\\]/).pop() || a;
11
15
  }
12
16
  }
13
17
  return 'unknown.blend';
@@ -25,15 +29,17 @@ function outputPathFromArgs(args: string[]): string {
25
29
  const oIdx = args.indexOf('-o');
26
30
  if (oIdx !== -1 && args[oIdx + 1]) {
27
31
  let p = args[oIdx + 1];
28
- if (p.endsWith('/')) p = p.slice(0, -1);
29
- return p.split('/').slice(0, -1).join('/') || '/tmp';
32
+ if (p.endsWith('/') || p.endsWith('\\')) p = p.slice(0, -1);
33
+ const parts = p.split(/[/\\]/);
34
+ return parts.slice(0, -1).join('/') || os.tmpdir();
30
35
  }
31
36
  const fFlag = args.find(a => a.startsWith('-o='));
32
37
  if (fFlag) {
33
38
  const p = fFlag.slice(3);
34
- return p.endsWith('/') ? p.slice(0, -1).split('/').slice(0, -1).join('/') || '/tmp' : p.split('/').slice(0, -1).join('/') || '/tmp';
39
+ const parts = p.split(/[/\\]/);
40
+ return parts.slice(0, -1).join('/') || os.tmpdir();
35
41
  }
36
- return '/tmp';
42
+ return os.tmpdir();
37
43
  }
38
44
 
39
45
  function frameRangeFromArgs(args: string[]): { start: number; end: number } {
@@ -57,11 +63,17 @@ function frameRangeFromArgs(args: string[]): { start: number; end: number } {
57
63
  async function parseRenderLog(blendName: string): Promise<{ framesRendered: number; totalFrames: number; startTime: number; samples?: number } | null> {
58
64
  const baseName = blendName.replace(/\.blend$/i, '');
59
65
  try {
60
- const entries = (await run(`ls -t "${RENDER_LOG_DIR}" 2>/dev/null | grep "^${baseName}_" | head -5`, '')).trim().split('\n').filter(Boolean);
66
+ if (!fs.existsSync(RENDER_LOG_DIR)) return null;
67
+ const entries = fs.readdirSync(RENDER_LOG_DIR)
68
+ .filter(f => f.startsWith(`${baseName}_`))
69
+ .map(f => ({ name: f, time: fs.statSync(path.join(RENDER_LOG_DIR, f)).mtimeMs }))
70
+ .sort((a, b) => b.time - a.time)
71
+ .slice(0, 5);
72
+
61
73
  for (const entry of entries) {
62
- const logPath = `${RENDER_LOG_DIR}/${entry}`;
74
+ const logPath = path.join(RENDER_LOG_DIR, entry.name);
63
75
  try {
64
- const content = await run(`cat "${logPath}" 2>/dev/null`, '', 500);
76
+ const content = fs.readFileSync(logPath, 'utf-8');
65
77
  if (!content) continue;
66
78
  const lines = content.split('\n');
67
79
  const rendered = lines.filter(l => /Fra:(\d+)/i.test(l)).length;
@@ -86,21 +98,39 @@ async function parseRenderLog(blendName: string): Promise<{ framesRendered: numb
86
98
  export async function collectBlenderRenders(): Promise<BlenderRenderData[]> {
87
99
  const renders: BlenderRenderData[] = [];
88
100
  try {
89
- const psRaw = await run("ps -eo pid,pcpu,pmem,state,command 2>/dev/null | grep -iE '[b]lender'", '', 2000);
90
- if (!psRaw) return renders;
101
+ interface BlenderProc {
102
+ pid: number;
103
+ cpuPct: number;
104
+ command: string;
105
+ }
106
+ const procList: BlenderProc[] = [];
91
107
 
92
- const lines = psRaw.split('\n');
93
- for (const line of lines) {
94
- const parts = line.trim().match(/\s*(\d+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+(.+)/);
95
- if (!parts) continue;
108
+ if (process.platform === 'win32') {
109
+ const procs = await collectWindowsProcesses();
110
+ for (const p of procs) {
111
+ if (/blender/i.test(p.command)) {
112
+ procList.push({ pid: p.pid, cpuPct: p.cpu, command: p.command });
113
+ }
114
+ }
115
+ } else {
116
+ const psRaw = await run("ps -eo pid,pcpu,pmem,state,command 2>/dev/null | grep -iE '[b]lender'", '', 2000);
117
+ if (psRaw) {
118
+ const lines = psRaw.split('\n');
119
+ for (const line of lines) {
120
+ const parts = line.trim().match(/\s*(\d+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+(.+)/);
121
+ if (parts) {
122
+ procList.push({ pid: parseInt(parts[1], 10), cpuPct: parseFloat(parts[2]), command: parts[5] });
123
+ }
124
+ }
125
+ }
126
+ }
96
127
 
97
- const pid = parseInt(parts[1], 10);
98
- const cpuPct = parseFloat(parts[2]);
99
- const command = parts[5];
128
+ for (const p of procList) {
129
+ const { pid, cpuPct, command } = p;
100
130
  const args = command.split(/\s+/);
101
131
 
102
132
  const isRender = /-b|--background|-f\s/.test(command) || /render/i.test(command);
103
- if (!isRender && !command.includes('blender')) continue;
133
+ if (!isRender && !command.toLowerCase().includes('blender')) continue;
104
134
 
105
135
  const blendFile = blendFileFromArgs(args);
106
136
  const engine = engineFromArgs(args);
@@ -1,14 +1,15 @@
1
1
  /**
2
2
  * Build System Tracker.
3
3
  *
4
- * Scans active processes for compiler invocations (xcodebuild, cargo, rustc,
5
- * clang, swift-frontend, make, ninja, bazel, go build), tracking compiler
6
- * parallelism, CPU/RAM usage, and estimated elapsed time.
4
+ * Scans active processes for compiler invocations (xcodebuild, msbuild, cargo, rustc,
5
+ * clang, cl.exe, make, ninja, bazel, cmake, go build), tracking compiler
6
+ * parallelism, CPU/RAM usage, and estimated elapsed time across macOS, Linux, and Windows.
7
7
  */
8
8
  import { run } from './run.js';
9
+ import { collectWindowsProcesses } from './platform/windows.js';
9
10
 
10
11
  export interface ActiveBuild {
11
- tool: 'xcodebuild' | 'cargo' | 'swift' | 'make' | 'ninja' | 'bazel' | 'go' | 'clang' | 'compiler';
12
+ tool: 'xcodebuild' | 'cargo' | 'swift' | 'make' | 'ninja' | 'bazel' | 'go' | 'clang' | 'compiler' | 'msbuild' | 'cmake';
12
13
  rootPid: number;
13
14
  command: string;
14
15
  compilerProcesses: number;
@@ -20,52 +21,79 @@ export interface ActiveBuild {
20
21
 
21
22
  export async function collectBuilds(): Promise<ActiveBuild[]> {
22
23
  try {
23
- const raw = await run('ps -eo pid,ppid,pcpu,pmem,etime,command -r', '');
24
- if (!raw) return [];
25
-
26
- const lines = raw.split('\n').slice(1);
27
24
  const builds: Map<string, ActiveBuild> = new Map();
28
25
 
29
26
  const buildToolPatterns: { name: ActiveBuild['tool']; match: RegExp }[] = [
27
+ { name: 'msbuild', match: /\b(msbuild|devenv)\b/i },
30
28
  { name: 'xcodebuild', match: /xcodebuild/i },
31
- { name: 'cargo', match: /\bcargo\s+(build|check|test|run)\b/i },
29
+ { name: 'cargo', match: /\bcargo\s+(build|check|test|run)?\b/i },
32
30
  { name: 'swift', match: /\bswift\s+(build|test)\b/i },
33
- { name: 'make', match: /\bmake\b/i },
31
+ { name: 'make', match: /\b(make|nmake)\b/i },
34
32
  { name: 'ninja', match: /\bninja\b/i },
33
+ { name: 'cmake', match: /\bcmake\b/i },
35
34
  { name: 'bazel', match: /\bbazel\b/i },
36
35
  { name: 'go', match: /\bgo\s+(build|test|run)\b/i },
37
36
  ];
38
37
 
39
- const compilerMatch = /(clang|swift-frontend|rustc|go tool compile|gcc|g\+\+|ghc)/i;
38
+ const compilerMatch = /(clang|swift-frontend|rustc|go tool compile|gcc|g\+\+|ghc|cl\.exe|csc\.exe|link\.exe)/i;
40
39
 
41
- // First pass: identify build roots
42
- for (const line of lines) {
43
- if (!line.trim()) continue;
44
- const parts = line.trim().split(/\s+/);
45
- if (parts.length < 6) continue;
40
+ interface ProcEntry {
41
+ pid: number;
42
+ cpu: number;
43
+ mem: number;
44
+ elapsedSec: number;
45
+ cmd: string;
46
+ }
46
47
 
47
- const pid = parseInt(parts[0], 10);
48
- const cpu = parseFloat(parts[2]) || 0;
49
- const mem = parseFloat(parts[3]) || 0;
50
- const etimeStr = parts[4];
51
- const cmd = parts.slice(5).join(' ');
48
+ const procList: ProcEntry[] = [];
52
49
 
53
- for (const pattern of buildToolPatterns) {
54
- if (pattern.match.test(cmd)) {
55
- let elapsedSec = 0;
56
- const etimeParts = etimeStr.split(':').map(Number);
57
- if (etimeParts.length === 2) elapsedSec = etimeParts[0] * 60 + etimeParts[1];
58
- else if (etimeParts.length === 3) elapsedSec = etimeParts[0] * 3600 + etimeParts[1] * 60 + etimeParts[2];
50
+ if (process.platform === 'win32') {
51
+ const winProcs = await collectWindowsProcesses();
52
+ for (const p of winProcs) {
53
+ procList.push({
54
+ pid: p.pid,
55
+ cpu: p.cpu,
56
+ mem: p.mem,
57
+ elapsedSec: p.runtime || 0,
58
+ cmd: p.command,
59
+ });
60
+ }
61
+ } else {
62
+ const raw = await run('ps -eo pid,ppid,pcpu,pmem,etime,command -r 2>/dev/null', '');
63
+ if (!raw) return [];
64
+ const lines = raw.split('\n').slice(1);
65
+ for (const line of lines) {
66
+ if (!line.trim()) continue;
67
+ const parts = line.trim().split(/\s+/);
68
+ if (parts.length < 6) continue;
69
+ const pid = parseInt(parts[0], 10);
70
+ const cpu = parseFloat(parts[2]) || 0;
71
+ const mem = parseFloat(parts[3]) || 0;
72
+ const etimeStr = parts[4];
73
+ const cmd = parts.slice(5).join(' ');
74
+
75
+ let elapsedSec = 0;
76
+ const etimeParts = etimeStr.split(':').map(Number);
77
+ if (etimeParts.length === 2) elapsedSec = etimeParts[0] * 60 + etimeParts[1];
78
+ else if (etimeParts.length === 3) elapsedSec = etimeParts[0] * 3600 + etimeParts[1] * 60 + etimeParts[2];
59
79
 
80
+ procList.push({ pid, cpu, mem, elapsedSec, cmd });
81
+ }
82
+ }
83
+
84
+ // First pass: identify build roots
85
+ for (const p of procList) {
86
+ for (const pattern of buildToolPatterns) {
87
+ if (pattern.match.test(p.cmd)) {
60
88
  builds.set(pattern.name, {
61
89
  tool: pattern.name,
62
- rootPid: pid,
63
- command: cmd.slice(0, 80),
90
+ rootPid: p.pid,
91
+ command: p.cmd.slice(0, 80),
64
92
  compilerProcesses: 1,
65
- totalCpu: cpu,
66
- totalMem: mem,
67
- elapsedSec,
68
- target: cmd.split(' ').slice(1, 4).join(' '),
93
+ totalCpu: p.cpu,
94
+ totalMem: p.mem,
95
+ elapsedSec: p.elapsedSec,
96
+ target: p.cmd.split(' ').slice(1, 4).join(' '),
69
97
  });
70
98
  break;
71
99
  }
@@ -73,41 +101,31 @@ export async function collectBuilds(): Promise<ActiveBuild[]> {
73
101
  }
74
102
 
75
103
  // Second pass: associate child compiler processes
76
- for (const line of lines) {
77
- if (!line.trim()) continue;
78
- const parts = line.trim().split(/\s+/);
79
- if (parts.length < 6) continue;
80
-
81
- const cpu = parseFloat(parts[2]) || 0;
82
- const mem = parseFloat(parts[3]) || 0;
83
- const cmd = parts.slice(5).join(' ');
84
-
85
- if (compilerMatch.test(cmd)) {
86
- // If an active build was found, associate this compiler worker
104
+ for (const p of procList) {
105
+ if (compilerMatch.test(p.cmd)) {
87
106
  if (builds.size > 0) {
88
107
  const firstBuild = builds.values().next().value;
89
108
  if (firstBuild) {
90
109
  firstBuild.compilerProcesses += 1;
91
- firstBuild.totalCpu += cpu;
92
- firstBuild.totalMem += mem;
110
+ firstBuild.totalCpu += p.cpu;
111
+ firstBuild.totalMem += p.mem;
93
112
  }
94
113
  } else {
95
- // Standalone compiler invocation
96
114
  const key = 'compiler';
97
115
  const existing = builds.get(key);
98
116
  if (existing) {
99
117
  existing.compilerProcesses += 1;
100
- existing.totalCpu += cpu;
101
- existing.totalMem += mem;
118
+ existing.totalCpu += p.cpu;
119
+ existing.totalMem += p.mem;
102
120
  } else {
103
121
  builds.set(key, {
104
122
  tool: 'compiler',
105
- rootPid: parseInt(parts[0], 10),
106
- command: cmd.slice(0, 80),
123
+ rootPid: p.pid,
124
+ command: p.cmd.slice(0, 80),
107
125
  compilerProcesses: 1,
108
- totalCpu: cpu,
109
- totalMem: mem,
110
- elapsedSec: 0,
126
+ totalCpu: p.cpu,
127
+ totalMem: p.mem,
128
+ elapsedSec: p.elapsedSec,
111
129
  });
112
130
  }
113
131
  }
@@ -1,42 +1,44 @@
1
1
  <#
2
2
  .SYNOPSIS
3
- Launches a native Pyre UI window on Windows using WPF WebBrowser control.
3
+ Launches a native standalone Pyre UI window on Windows.
4
+ Prefers Microsoft Edge or Chromium in standalone desktop application mode (--app)
5
+ for modern HTML5/ES6/Chart.js rendering without browser tabs or address bars.
4
6
  #>
5
7
  param(
6
8
  [Parameter(Mandatory=$true)]
7
9
  [int]$Port
8
10
  )
9
11
 
10
- $ErrorActionPreference = 'Stop'
12
+ $ErrorActionPreference = 'SilentlyContinue'
11
13
 
12
- Add-Type -AssemblyName System.Windows.Forms
13
- Add-Type -AssemblyName System.Drawing
14
- Add-Type -AssemblyName PresentationFramework
14
+ $url = "http://localhost:$Port/"
15
15
 
16
- $regPath = "HKCU:\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION"
17
- if (-not (Test-Path $regPath)) {
18
- New-Item -Path $regPath -Force | Out-Null
19
- }
20
- $scriptName = [System.IO.Path]::GetFileName($PSCommandPath)
21
- Set-ItemProperty -Path $regPath -Name $scriptName -Value 11001 -Type DWord -ErrorAction SilentlyContinue
22
-
23
- $form = New-Object System.Windows.Forms.Form
24
- $form.Text = "Pyre Live Dashboard"
25
- $form.Width = 1200
26
- $form.Height = 800
27
- $form.StartPosition = "CenterScreen"
28
- $form.MinimumSize = New-Object System.Drawing.Size(860, 600)
29
- $form.BackColor = [System.Drawing.Color]::FromArgb(10, 10, 15)
30
- $form.ForeColor = [System.Drawing.Color]::FromArgb(245, 245, 247)
16
+ # 1. Search for Edge or Chrome executable paths
17
+ $browsers = @(
18
+ "$env:ProgramFiles (x86)\Microsoft\Edge\Application\msedge.exe",
19
+ "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe",
20
+ "$env:LocalAppData\Microsoft\Edge\Application\msedge.exe",
21
+ "$env:ProgramFiles\Google\Chrome\Application\chrome.exe",
22
+ "$env:ProgramFiles (x86)\Google\Chrome\Application\chrome.exe",
23
+ "$env:LocalAppData\Google\Chrome\Application\chrome.exe",
24
+ "$env:ProgramFiles\BraveSoftware\Brave-Browser\Application\brave.exe"
25
+ )
31
26
 
32
- $browser = New-Object System.Windows.Forms.WebBrowser
33
- $browser.Dock = "Fill"
34
- $browser.ScriptErrorsSuppressed = $true
35
- $browser.Navigate("http://localhost:$Port/")
36
- $form.Controls.Add($browser)
27
+ $targetBrowser = $null
28
+ foreach ($b in $browsers) {
29
+ if (Test-Path $b) {
30
+ $targetBrowser = $b
31
+ break
32
+ }
33
+ }
37
34
 
38
- $form.Add_Shown({
39
- $form.Activate()
40
- })
35
+ if ($targetBrowser) {
36
+ # Launch in standalone application window mode (frameless app window)
37
+ $args = "--app=$url --window-size=1200,800"
38
+ $proc = Start-Process -FilePath $targetBrowser -ArgumentList $args -PassThru -Wait
39
+ exit 0
40
+ }
41
41
 
42
- $form.ShowDialog()
42
+ # 2. Fallback: Launch default system browser
43
+ Start-Process $url
44
+ exit 0