pyre-cli 10.1.0 → 10.2.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 (46) hide show
  1. package/README.md +195 -494
  2. package/dist/{checkCmd-RKW6QGDR.js → checkCmd-WQAHAUWO.js} +1 -1
  3. package/dist/{chunk-GHRDW646.js → chunk-23YWCI3O.js} +375 -135
  4. package/dist/{service-Q74BBILO.js → chunk-KFPTD266.js} +43 -3
  5. package/dist/{doctor-GYHFTNZQ.js → chunk-KVUMOA6C.js} +7 -6
  6. package/dist/{chunk-53O2RGSL.js → chunk-QM5L43EK.js} +19 -2
  7. package/dist/{chunk-6UHZP4QP.js → chunk-XTOOEKLM.js} +24 -12
  8. package/dist/{collectors-H43PN4PS.js → collectors-JDN5Y7EV.js} +1 -1
  9. package/dist/{completions-JKYEJNGR.js → completions-ZVTNE52B.js} +15 -11
  10. package/dist/{config-GYGJXU2Q.js → config-J4YMADEF.js} +3 -1
  11. package/dist/doctor-LHFW4UJP.js +10 -0
  12. package/dist/firebaseSync-ON3RZ6KX.js +91 -0
  13. package/dist/index.js +4243 -735
  14. package/dist/monitors/platform/windows-ui.ps1 +42 -0
  15. package/dist/{netUsageCmd-WKBTW2YG.js → netUsageCmd-67IBZZYN.js} +2 -2
  16. package/dist/{pipeCmd-7REDBLBU.js → pipeCmd-2N4JPKYE.js} +1 -1
  17. package/dist/{prometheusCmd-PL64BLN5.js → prometheusCmd-LEUM5WCZ.js} +1 -1
  18. package/dist/serveCmd-L7LVJOZN.js +353 -0
  19. package/dist/service-7GIX3YNC.js +16 -0
  20. package/dist/syncCmd-42IRF633.js +246 -0
  21. package/dist/{topoCmd-DU2HHZNH.js → topoCmd-DZ3YS5IL.js} +1 -1
  22. package/dist/{watchdogCmd-UF53JPZB.js → watchdogCmd-6GR4UN6B.js} +7 -7
  23. package/dist/{wizard-TD7BGX6O.js → wizard-JQMQWFAW.js} +1 -1
  24. package/package.json +4 -3
  25. package/parity.md +2 -0
  26. package/src/completions.ts +15 -11
  27. package/src/easy_features.test.ts +149 -0
  28. package/src/firebaseSync.ts +157 -0
  29. package/src/formatters/html_template.ts +3086 -598
  30. package/src/formatters/render.ts +25 -12
  31. package/src/index.ts +516 -23
  32. package/src/live/input.ts +501 -148
  33. package/src/live/render.ts +163 -18
  34. package/src/live/state.ts +52 -2
  35. package/src/live/types.ts +1 -1
  36. package/src/monitors/collectors.ts +3 -10
  37. package/src/monitors/platform/windows-ui.ps1 +42 -0
  38. package/src/monitors/platform/windows.ts +442 -147
  39. package/src/monitors/types.ts +8 -0
  40. package/src/serveCmd.ts +17 -2
  41. package/src/service.ts +49 -0
  42. package/src/state/config.ts +48 -0
  43. package/src/syncCmd.ts +159 -11
  44. package/src/web/dashboard.ts +205 -0
  45. package/dist/serveCmd-2H5VLF3P.js +0 -153
  46. package/dist/syncCmd-A2W3QB3S.js +0 -126
@@ -33,6 +33,43 @@ function uninstallService() {
33
33
  console.log(chalk.yellow(`\u26A0 Background service uninstallation not supported on ${platform}.`));
34
34
  }
35
35
  }
36
+ function isServiceInstalled() {
37
+ const platform = process.platform;
38
+ if (platform === "darwin") {
39
+ return fs.existsSync(PLIST_PATH);
40
+ } else if (platform === "linux") {
41
+ return fs.existsSync(SYSTEMD_UNIT_PATH);
42
+ } else if (platform === "win32") {
43
+ try {
44
+ execSync(`schtasks /query /tn "${TASK_NAME}"`, { stdio: "pipe" });
45
+ return true;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+ function getServiceStatus() {
53
+ const platform = process.platform;
54
+ const installed = isServiceInstalled();
55
+ let serviceLabel = SERVICE_LABEL;
56
+ let servicePath = PLIST_PATH;
57
+ const logPath = path.join(os.homedir(), ".config", "pyre", "web.log");
58
+ if (platform === "linux") {
59
+ serviceLabel = "pyre-web.service";
60
+ servicePath = SYSTEMD_UNIT_PATH;
61
+ } else if (platform === "win32") {
62
+ serviceLabel = TASK_NAME;
63
+ servicePath = "Windows Task Scheduler (onlogon)";
64
+ }
65
+ return {
66
+ installed,
67
+ platform,
68
+ serviceLabel,
69
+ path: servicePath,
70
+ logPath
71
+ };
72
+ }
36
73
  var installLaunchdAgent = installService;
37
74
  var uninstallLaunchdAgent = uninstallService;
38
75
  function installLaunchd(port) {
@@ -173,9 +210,12 @@ function uninstallWindowsTask() {
173
210
  console.log(chalk.yellow(` No scheduled task '${TASK_NAME}' found or deletion failed: ${err.message}`));
174
211
  }
175
212
  }
213
+
176
214
  export {
177
- installLaunchdAgent,
178
215
  installService,
179
- uninstallLaunchdAgent,
180
- uninstallService
216
+ uninstallService,
217
+ isServiceInstalled,
218
+ getServiceStatus,
219
+ installLaunchdAgent,
220
+ uninstallLaunchdAgent
181
221
  };
@@ -1,10 +1,10 @@
1
- import {
2
- CONFIG_FILE,
3
- readConfig
4
- } from "./chunk-53O2RGSL.js";
5
1
  import {
6
2
  run
7
3
  } from "./chunk-MZO5QKUR.js";
4
+ import {
5
+ CONFIG_FILE,
6
+ readConfig
7
+ } from "./chunk-QM5L43EK.js";
8
8
 
9
9
  // src/doctor.ts
10
10
  import fs from "fs";
@@ -381,7 +381,8 @@ function printDoctorReport(checks) {
381
381
  }
382
382
  console.log();
383
383
  }
384
+
384
385
  export {
385
- printDoctorReport,
386
- runDoctor
386
+ runDoctor,
387
+ printDoctorReport
387
388
  };
@@ -36,6 +36,8 @@ var DEFAULT_CONFIG = {
36
36
  aiModel: "expert-rules-v1",
37
37
  aiApiKey: "",
38
38
  dockerModeConfirmed: false,
39
+ firebaseSyncEnabled: false,
40
+ firebaseHostId: "",
39
41
  visiblePanels: {
40
42
  cpu: true,
41
43
  mem: true,
@@ -52,7 +54,17 @@ var DEFAULT_CONFIG = {
52
54
  blender: true,
53
55
  tree: false
54
56
  },
55
- panelLayout: ["mem", "disk", "net"]
57
+ panelLayout: ["mem", "disk", "net"],
58
+ bookmarks: [],
59
+ graphZoomLevel: 1,
60
+ compact: false,
61
+ alwaysOnTop: false,
62
+ exposeLan: false,
63
+ port: 3e3,
64
+ historyDuration: 60,
65
+ tableDensity: "standard",
66
+ defaultFilter: "all",
67
+ defaultSort: "cpu"
56
68
  };
57
69
  function getConfigDir() {
58
70
  return process.env.PYRE_CONFIG_DIR || path.join(os.homedir(), ".config", "pyre");
@@ -133,6 +145,10 @@ function deepMerge(target, source) {
133
145
  function getConfigPath() {
134
146
  return getConfigFile();
135
147
  }
148
+ function resetConfig() {
149
+ writeConfig(DEFAULT_CONFIG);
150
+ return { ...DEFAULT_CONFIG };
151
+ }
136
152
 
137
153
  export {
138
154
  DEFAULT_CONFIG,
@@ -147,5 +163,6 @@ export {
147
163
  saveProfile,
148
164
  loadProfile,
149
165
  listProfiles,
150
- getConfigPath
166
+ getConfigPath,
167
+ resetConfig
151
168
  };
@@ -580,10 +580,11 @@ var TAB_DEFS = [
580
580
  { id: "power", label: "Power", key: "4" },
581
581
  { id: "battery", label: "Battery", key: "5" },
582
582
  { id: "thermal", label: "Thermal", key: "6" },
583
- { id: "packets", label: "Conns", key: "7" },
584
- { id: "tasks", label: "Tasks", key: "8" },
585
- { id: "disk", label: "Disk", key: "9" },
586
- { id: "process", label: "Process", key: "0" },
583
+ { id: "network", label: "Network", key: "7" },
584
+ { id: "packets", label: "Conns", key: "8" },
585
+ { id: "tasks", label: "Tasks", key: "9" },
586
+ { id: "disk", label: "Disk", key: "0" },
587
+ { id: "process", label: "Process", key: "P" },
587
588
  { id: "p2p", label: "P2P", key: "R" },
588
589
  { id: "anomalies", label: "Anomalies", key: "A" },
589
590
  { id: "containers", label: "Containers", key: "C" },
@@ -591,22 +592,24 @@ var TAB_DEFS = [
591
592
  ];
592
593
  var TAB_BAR_INDENT = 2;
593
594
  var TAB_SEPARATOR_WIDTH = 3;
594
- function getTabHitboxes() {
595
+ function getTabHitboxes(visible) {
596
+ const tabs = visible ? TAB_DEFS.filter((t) => visible[t.id] !== false) : TAB_DEFS;
595
597
  const boxes = [];
596
598
  let col = TAB_BAR_INDENT;
597
- TAB_DEFS.forEach((t, i) => {
599
+ tabs.forEach((t, i) => {
598
600
  const text = `[${t.key}]${t.label}`;
599
601
  const start = col;
600
602
  const end = col + text.length;
601
603
  boxes.push({ id: t.id, start, end });
602
604
  col = end;
603
- if (i < TAB_DEFS.length - 1) col += TAB_SEPARATOR_WIDTH;
605
+ if (i < tabs.length - 1) col += TAB_SEPARATOR_WIDTH;
604
606
  });
605
607
  return boxes;
606
608
  }
607
609
  var TAB_BAR_ROW = 4;
608
- function tabBar(activePanel, width, theme) {
609
- const rendered = TAB_DEFS.map((t) => {
610
+ function tabBar(activePanel, width, theme, visible) {
611
+ const visibleTabs = visible ? TAB_DEFS.filter((t) => visible[t.id] !== false) : TAB_DEFS;
612
+ const rendered = visibleTabs.map((t) => {
610
613
  const isActive = activePanel === t.id;
611
614
  const label = isActive ? chalk2.bold(t.label) : chalk2.dim(t.label);
612
615
  const keyHint = chalk2.dim(`[${t.key}]`);
@@ -1006,7 +1009,12 @@ function btopCpuBox(data, width, theme, opts) {
1006
1009
  function btopLeftColumn(data, width, theme, opts) {
1007
1010
  const contentWidth = width - 4;
1008
1011
  const out = [];
1009
- const layout = opts.panelLayout || ["mem", "disk", "net"];
1012
+ const rawLayout = opts.panelLayout || ["mem", "disk", "net"];
1013
+ const visible = opts.visible || {};
1014
+ const layout = rawLayout.filter((name) => {
1015
+ const key = name === "net" ? "network" : name;
1016
+ return visible[key] !== false;
1017
+ });
1010
1018
  for (const panelName of layout) {
1011
1019
  if (panelName === "mem") {
1012
1020
  const memLines = [];
@@ -1128,7 +1136,11 @@ function btopProcBox(data, width, targetHeight, theme, opts) {
1128
1136
  }
1129
1137
  function formatTable(data, opts = {}) {
1130
1138
  const width = clampWidth(opts.width);
1131
- const activePanel = opts.activePanel || "grid";
1139
+ let activePanel = opts.activePanel || "grid";
1140
+ const visible = opts.visible || {};
1141
+ if (activePanel !== "grid" && visible[activePanel] === false) {
1142
+ activePanel = "grid";
1143
+ }
1132
1144
  const themeName = opts.theme || "default";
1133
1145
  const theme = THEMES[themeName] || THEMES.default;
1134
1146
  const out = [];
@@ -1139,7 +1151,7 @@ function formatTable(data, opts = {}) {
1139
1151
  out.push(...anomalyPanel(anomalies, width, theme));
1140
1152
  out.push("");
1141
1153
  }
1142
- out.push(tabBar(activePanel, width, theme));
1154
+ out.push(tabBar(activePanel, width, theme, opts.visible));
1143
1155
  out.push("");
1144
1156
  if (opts.minimal) {
1145
1157
  const contentWidth = width - 4;
@@ -15,7 +15,7 @@ import {
15
15
  collectThermal,
16
16
  getDisplayInfo,
17
17
  getTimeMachineStatus
18
- } from "./chunk-GHRDW646.js";
18
+ } from "./chunk-23YWCI3O.js";
19
19
  import "./chunk-M73HCQD2.js";
20
20
  import "./chunk-VIACN2RU.js";
21
21
  import "./chunk-MZO5QKUR.js";
@@ -9,6 +9,8 @@ _pyre() {
9
9
  'info:Display static system overview'
10
10
  'doctor:Run system permissions and diagnostic checks'
11
11
  'web:Launch web dashboard server'
12
+ 'ui:Launch Activity Monitor UI window'
13
+ 'launch:Launch Activity Monitor UI window'
12
14
  'config:Show or reset configuration options'
13
15
  'p2p:P2P streaming server or client'
14
16
  'ssh:Stream metrics over SSH'
@@ -56,38 +58,38 @@ _pyre_completions() {
56
58
  COMPREPLY=()
57
59
  cur="\${COMP_WORDS[COMP_CWORD]}"
58
60
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
59
- commands="live info doctor web config p2p ssh bench completions"
61
+ commands="live info doctor web ui launch config p2p ssh bench completions"
60
62
  opts="--json --html --md --csv --tsv --detailed --theme --interval --once --out --export-dir --log --tree --sort --packets --limit --alert-cpu --alert-temp --temp-unit"
61
63
 
62
- case "\${prev}" in
64
+ case "${prev}" in
63
65
  --theme)
64
- COMPREPLY=( $(compgen -W "default dracula cyberpunk monochrome nord gruvbox" -- \${cur}) )
66
+ COMPREPLY=( $(compgen -W "default dracula cyberpunk monochrome nord gruvbox" -- ${cur}) )
65
67
  return 0
66
68
  ;;
67
69
  --sort)
68
- COMPREPLY=( $(compgen -W "cpu mem pid user command state threads runtime" -- \${cur}) )
70
+ COMPREPLY=( $(compgen -W "cpu mem pid user command state threads runtime" -- ${cur}) )
69
71
  return 0
70
72
  ;;
71
73
  --temp-unit)
72
- COMPREPLY=( $(compgen -W "c f" -- \${cur}) )
74
+ COMPREPLY=( $(compgen -W "c f" -- ${cur}) )
73
75
  return 0
74
76
  ;;
75
77
  config)
76
- COMPREPLY=( $(compgen -W "show reset" -- \${cur}) )
78
+ COMPREPLY=( $(compgen -W "show reset" -- ${cur}) )
77
79
  return 0
78
80
  ;;
79
81
  p2p)
80
- COMPREPLY=( $(compgen -W "server connect" -- \${cur}) )
82
+ COMPREPLY=( $(compgen -W "server connect" -- ${cur}) )
81
83
  return 0
82
84
  ;;
83
85
  esac
84
86
 
85
- if [[ \${cur} == -* ]] ; then
86
- COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
87
+ if [[ ${cur} == -* ]] ; then
88
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
87
89
  return 0
88
90
  fi
89
91
 
90
- COMPREPLY=( $(compgen -W "\${commands}" -- \${cur}) )
92
+ COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
91
93
  }
92
94
 
93
95
  complete -F _pyre_completions pyre
@@ -100,6 +102,8 @@ complete -c pyre -n "__fish_use_subcommand" -a live -d "Start interactive live m
100
102
  complete -c pyre -n "__fish_use_subcommand" -a info -d "Display static system overview"
101
103
  complete -c pyre -n "__fish_use_subcommand" -a doctor -d "Run system diagnostics"
102
104
  complete -c pyre -n "__fish_use_subcommand" -a web -d "Launch web dashboard server"
105
+ complete -c pyre -n "__fish_use_subcommand" -a ui -d "Launch Activity Monitor UI window"
106
+ complete -c pyre -n "__fish_use_subcommand" -a launch -d "Launch Activity Monitor UI window"
103
107
  complete -c pyre -n "__fish_use_subcommand" -a config -d "Show or reset configuration options"
104
108
  complete -c pyre -n "__fish_use_subcommand" -a p2p -d "P2P streaming mode"
105
109
  complete -c pyre -n "__fish_use_subcommand" -a completions -d "Generate shell completions"
@@ -119,7 +123,7 @@ function generatePowerShellCompletions() {
119
123
  return `# PowerShell completion for pyre
120
124
  Register-ArgumentCompleter -Native -CommandName pyre -ScriptBlock {
121
125
  param($wordToComplete, $commandAst, $cursorPosition)
122
- $commands = @('live', 'check', 'pipe', 'stress', 'ui', 'web', 'ssh', 'fleet', 'bench', 'benchmark', 'anomalies', 'doctor', 'extensions', 'brew', 'update', 'profile', 'config', 'history', 'diff', 'info', 'completions', 'p2p', 'blender', 'prometheus', 'smart', 'replay', 'explain')
126
+ $commands = @('live', 'check', 'pipe', 'stress', 'ui', 'launch', 'web', 'ssh', 'fleet', 'bench', 'benchmark', 'anomalies', 'doctor', 'extensions', 'brew', 'update', 'profile', 'config', 'history', 'diff', 'info', 'completions', 'p2p', 'blender', 'prometheus', 'smart', 'replay', 'explain')
123
127
  $options = @('--json', '--html', '--md', '--csv', '--tsv', '--detailed', '--theme', '--interval', '--once', '--out', '--export-dir', '--log', '--tree', '--sort', '--packets', '--limit', '--alert-cpu', '--alert-temp', '--temp-unit', '--plain', '--help', '--version')
124
128
 
125
129
  $elements = $commandAst.CommandElements
@@ -10,9 +10,10 @@ import {
10
10
  listProfiles,
11
11
  loadProfile,
12
12
  readConfig,
13
+ resetConfig,
13
14
  saveProfile,
14
15
  writeConfig
15
- } from "./chunk-53O2RGSL.js";
16
+ } from "./chunk-QM5L43EK.js";
16
17
  export {
17
18
  CONFIG_DIR,
18
19
  CONFIG_FILE,
@@ -25,6 +26,7 @@ export {
25
26
  listProfiles,
26
27
  loadProfile,
27
28
  readConfig,
29
+ resetConfig,
28
30
  saveProfile,
29
31
  writeConfig
30
32
  };
@@ -0,0 +1,10 @@
1
+ import {
2
+ printDoctorReport,
3
+ runDoctor
4
+ } from "./chunk-KVUMOA6C.js";
5
+ import "./chunk-MZO5QKUR.js";
6
+ import "./chunk-QM5L43EK.js";
7
+ export {
8
+ printDoctorReport,
9
+ runDoctor
10
+ };
@@ -0,0 +1,91 @@
1
+ // src/firebaseSync.ts
2
+ import crypto from "crypto";
3
+ import os from "os";
4
+ import { initializeApp, getApps } from "firebase/app";
5
+ import {
6
+ getFirestore,
7
+ doc,
8
+ setDoc,
9
+ getDoc,
10
+ getDocs,
11
+ collection,
12
+ query,
13
+ orderBy,
14
+ limit
15
+ } from "firebase/firestore";
16
+ var FIREBASE_CONFIG = {
17
+ apiKey: "AIzaSyAA86V_wRGJpzc27ZHZd1OBIt4Q-72ASdI",
18
+ authDomain: "dearborn-internal-tool.firebaseapp.com",
19
+ projectId: "dearborn-internal-tool",
20
+ storageBucket: "dearborn-internal-tool.firebasestorage.app",
21
+ messagingSenderId: "77553447918",
22
+ appId: "1:77553447918:web:5c9128660ceeb087c794ff"
23
+ };
24
+ var COLLECTION = "pyre-sync";
25
+ var _db = null;
26
+ function getDb() {
27
+ if (_db) return _db;
28
+ const app = getApps().length ? getApps()[0] : initializeApp(FIREBASE_CONFIG);
29
+ _db = getFirestore(app);
30
+ return _db;
31
+ }
32
+ function computeChecksum(config, profiles) {
33
+ const hash = crypto.createHash("sha256");
34
+ hash.update(JSON.stringify(config));
35
+ hash.update(JSON.stringify(profiles));
36
+ return hash.digest("hex").slice(0, 16);
37
+ }
38
+ function resolveHostId(hostId) {
39
+ return (hostId || os.hostname()).replace(/[^a-zA-Z0-9_-]/g, "-");
40
+ }
41
+ async function firebasePush(config, profiles, hostId) {
42
+ const db = getDb();
43
+ const id = resolveHostId(hostId);
44
+ const checksum = computeChecksum(config, profiles);
45
+ const lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
46
+ const payload = {
47
+ version: 1,
48
+ lastUpdated,
49
+ sourceHost: os.hostname(),
50
+ checksum,
51
+ profilesCount: Object.keys(profiles).length,
52
+ config,
53
+ profiles
54
+ };
55
+ await setDoc(doc(db, COLLECTION, id), payload);
56
+ return { hostId: id, checksum, lastUpdated };
57
+ }
58
+ async function firebasePull(hostId) {
59
+ const db = getDb();
60
+ if (hostId) {
61
+ const id = resolveHostId(hostId);
62
+ const snap = await getDoc(doc(db, COLLECTION, id));
63
+ if (!snap.exists()) return null;
64
+ return { doc: snap.data(), hostId: id };
65
+ }
66
+ const q = query(collection(db, COLLECTION), orderBy("lastUpdated", "desc"), limit(1));
67
+ const snaps = await getDocs(q);
68
+ if (snaps.empty) return null;
69
+ const first = snaps.docs[0];
70
+ return { doc: first.data(), hostId: first.id };
71
+ }
72
+ async function firebaseStatus() {
73
+ const db = getDb();
74
+ const q = query(collection(db, COLLECTION), orderBy("lastUpdated", "desc"));
75
+ const snaps = await getDocs(q);
76
+ return snaps.docs.map((d) => {
77
+ const data = d.data();
78
+ return {
79
+ hostId: d.id,
80
+ sourceHost: data.sourceHost,
81
+ lastUpdated: data.lastUpdated,
82
+ profilesCount: data.profilesCount,
83
+ checksum: data.checksum
84
+ };
85
+ });
86
+ }
87
+ export {
88
+ firebasePull,
89
+ firebasePush,
90
+ firebaseStatus
91
+ };