pyre-cli 9.5.1 → 10.0.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 (94) hide show
  1. package/README.md +84 -14
  2. package/dist/alertChannels-TPSQIJ7S.js +8 -0
  3. package/dist/blender-FGH3ASEQ.js +7 -0
  4. package/dist/brewHealth-P2GW5Y5C.js +176 -0
  5. package/dist/buildCmd-CDPZ2ZFA.js +38 -0
  6. package/dist/{checkCmd-EWFNLBWE.js → checkCmd-RKW6QGDR.js} +10 -11
  7. package/dist/{chunk-RVGKEJXT.js → chunk-53O2RGSL.js} +47 -17
  8. package/dist/{chunk-MOAEGMFP.js → chunk-6UHZP4QP.js} +204 -46
  9. package/dist/chunk-GHRDW646.js +2506 -0
  10. package/dist/{prometheus-LFYHF6VO.js → chunk-HT4VQ4OH.js} +1 -0
  11. package/dist/chunk-M73HCQD2.js +95 -0
  12. package/dist/chunk-MZO5QKUR.js +32 -0
  13. package/dist/chunk-VIACN2RU.js +133 -0
  14. package/dist/chunk-Z2YV5HKV.js +87 -0
  15. package/dist/chunk-ZLVIVFSO.js +110 -0
  16. package/dist/{collectors-WD6VKH6M.js → collectors-H43PN4PS.js} +8 -2
  17. package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
  18. package/dist/{config-4ZLF4HTS.js → config-GYGJXU2Q.js} +9 -1
  19. package/dist/doctor-GYHFTNZQ.js +387 -0
  20. package/dist/explainCmd-MF6PIMLW.js +222 -0
  21. package/dist/extensions-XJGHG7X3.js +134 -0
  22. package/dist/index.js +841 -206
  23. package/dist/ioreport.py +72 -0
  24. package/dist/netUsageCmd-WKBTW2YG.js +90 -0
  25. package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-7REDBLBU.js} +5 -3
  26. package/dist/profileProcCmd-N4H6MCBT.js +107 -0
  27. package/dist/prometheus-5BL5PVLC.js +6 -0
  28. package/dist/prometheusCmd-PL64BLN5.js +93 -0
  29. package/dist/replayCmd-Z3PYXY7V.js +164 -0
  30. package/dist/run-HOGQDDTA.js +8 -0
  31. package/dist/serveCmd-2H5VLF3P.js +153 -0
  32. package/dist/service-Q74BBILO.js +181 -0
  33. package/dist/smartCmd-ODWKSFNG.js +166 -0
  34. package/dist/syncCmd-A2W3QB3S.js +126 -0
  35. package/dist/topoCmd-DU2HHZNH.js +148 -0
  36. package/dist/watchdogCmd-UF53JPZB.js +161 -0
  37. package/dist/{wizard-GVIAHIVV.js → wizard-TD7BGX6O.js} +1 -1
  38. package/install.ps1 +29 -0
  39. package/install.sh +10 -9
  40. package/package.json +7 -3
  41. package/src/ai/models.ts +138 -0
  42. package/src/alertChannels.ts +119 -0
  43. package/src/anomalies.ts +114 -60
  44. package/src/brewHealth.ts +157 -40
  45. package/src/buildCmd.ts +48 -0
  46. package/src/checkCmd.ts +5 -5
  47. package/src/completions.ts +35 -0
  48. package/src/doctor.ts +235 -7
  49. package/src/easy_features.test.ts +40 -0
  50. package/src/explainCmd.ts +261 -0
  51. package/src/extensions.ts +71 -7
  52. package/src/formatters/index.ts +1 -1
  53. package/src/formatters/output.ts +100 -13
  54. package/src/formatters/render.ts +274 -38
  55. package/src/formatters/themes.ts +8 -0
  56. package/src/formatters/types.ts +6 -1
  57. package/src/index.ts +298 -56
  58. package/src/live/input.ts +122 -4
  59. package/src/live/render.ts +98 -0
  60. package/src/live/state.ts +70 -52
  61. package/src/live/types.ts +2 -2
  62. package/src/monitors/batteryPredictor.ts +143 -0
  63. package/src/monitors/blender.ts +144 -0
  64. package/src/monitors/buildTracker.ts +121 -0
  65. package/src/monitors/collectors.ts +558 -147
  66. package/src/monitors/dns_cache.ts +48 -0
  67. package/src/monitors/index.ts +3 -2
  68. package/src/monitors/ioreport.py +72 -0
  69. package/src/monitors/platform/linux.ts +756 -0
  70. package/src/monitors/platform/windows.ts +519 -0
  71. package/src/monitors/run.ts +27 -3
  72. package/src/monitors/smc.ts +21 -1
  73. package/src/monitors/types.ts +112 -14
  74. package/src/netUsageCmd.ts +121 -0
  75. package/src/platform.test.ts +99 -0
  76. package/src/plugins/index.ts +143 -0
  77. package/src/profileProcCmd.ts +133 -0
  78. package/src/prometheusCmd.ts +107 -0
  79. package/src/replayCmd.ts +186 -0
  80. package/src/serveCmd.ts +180 -0
  81. package/src/service.ts +125 -7
  82. package/src/smartCmd.ts +205 -0
  83. package/src/state/config.ts +64 -17
  84. package/src/syncCmd.ts +166 -0
  85. package/src/topoCmd.ts +176 -0
  86. package/src/watchdogCmd.ts +198 -0
  87. package/src/xbar.ts +22 -11
  88. package/dist/brewHealth-F2BXF5M6.js +0 -74
  89. package/dist/chunk-FH5WDJBF.js +0 -15
  90. package/dist/chunk-HGRBBPYX.js +0 -937
  91. package/dist/doctor-L6FZYA7F.js +0 -184
  92. package/dist/extensions-DVA5YLGH.js +0 -74
  93. package/dist/service-WTS3NBQU.js +0 -73
  94. /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
@@ -1,3 +1,7 @@
1
+ import {
2
+ explainAnomaliesLocally
3
+ } from "./chunk-ZLVIVFSO.js";
4
+
1
5
  // src/formatters/render.ts
2
6
  import chalk2 from "chalk";
3
7
 
@@ -106,7 +110,8 @@ var BUILTIN_THEMES = {
106
110
  network: chalk.hex("#60a5fa"),
107
111
  disk: chalk.hex("#f472b6"),
108
112
  graphs: chalk.hex("#22d3ee"),
109
- process: chalk.hex("#e5e7eb")
113
+ process: chalk.hex("#e5e7eb"),
114
+ blender: chalk.hex("#f97316")
110
115
  },
111
116
  dracula: {
112
117
  border: chalk.hex("#6272a4"),
@@ -119,7 +124,8 @@ var BUILTIN_THEMES = {
119
124
  network: chalk.hex("#ff79c6"),
120
125
  disk: chalk.hex("#ff5555"),
121
126
  graphs: chalk.hex("#8be9fd"),
122
- process: chalk.hex("#f8f8f2")
127
+ process: chalk.hex("#f8f8f2"),
128
+ blender: chalk.hex("#ffb86c")
123
129
  },
124
130
  cyberpunk: {
125
131
  border: chalk.hex("#ff0055"),
@@ -132,7 +138,8 @@ var BUILTIN_THEMES = {
132
138
  network: chalk.hex("#00ffff"),
133
139
  disk: chalk.hex("#ff007f"),
134
140
  graphs: chalk.hex("#00ffcc"),
135
- process: chalk.hex("#ffffff")
141
+ process: chalk.hex("#ffffff"),
142
+ blender: chalk.hex("#ff6600")
136
143
  },
137
144
  monochrome: {
138
145
  border: chalk.gray,
@@ -145,7 +152,8 @@ var BUILTIN_THEMES = {
145
152
  network: chalk.white.bold,
146
153
  disk: chalk.white.bold,
147
154
  graphs: chalk.white.bold,
148
- process: chalk.white.bold
155
+ process: chalk.white.bold,
156
+ blender: chalk.white.bold
149
157
  },
150
158
  nord: {
151
159
  border: chalk.hex("#4c566a"),
@@ -158,7 +166,8 @@ var BUILTIN_THEMES = {
158
166
  network: chalk.hex("#81a1c1"),
159
167
  disk: chalk.hex("#b48ead"),
160
168
  graphs: chalk.hex("#88c0d0"),
161
- process: chalk.hex("#eceff4")
169
+ process: chalk.hex("#eceff4"),
170
+ blender: chalk.hex("#d08770")
162
171
  },
163
172
  gruvbox: {
164
173
  border: chalk.hex("#928374"),
@@ -171,7 +180,8 @@ var BUILTIN_THEMES = {
171
180
  network: chalk.hex("#83a598"),
172
181
  disk: chalk.hex("#d3869b"),
173
182
  graphs: chalk.hex("#fb4934"),
174
- process: chalk.hex("#ebdbb2")
183
+ process: chalk.hex("#ebdbb2"),
184
+ blender: chalk.hex("#fe8019")
175
185
  }
176
186
  };
177
187
  function loadCustomThemes() {
@@ -198,7 +208,8 @@ function loadCustomThemes() {
198
208
  network: buildColor(data.network),
199
209
  disk: buildColor(data.disk),
200
210
  graphs: buildColor(data.graphs),
201
- process: buildColor(data.process)
211
+ process: buildColor(data.process),
212
+ blender: buildColor(data.blender)
202
213
  };
203
214
  } catch {
204
215
  }
@@ -440,8 +451,17 @@ function batteryCard(data, contentWidth) {
440
451
  if (data.battery.estimatedTimeToEmpty && data.battery.state !== "charged") {
441
452
  lines.push(statRow("Est. Empty", data.battery.estimatedTimeToEmpty));
442
453
  }
443
- if (data.battery.dischargeRatePerHour !== void 0 && data.battery.state !== "charged") {
444
- lines.push(statRow("Disch. Rate", `${data.battery.dischargeRatePerHour}%/h`));
454
+ if (data.battery.powerWatts !== void 0 && data.battery.powerWatts > 0 && data.battery.state !== "charged") {
455
+ lines.push(statRow("Power Draw", `${data.battery.powerWatts.toFixed(2)} W`));
456
+ }
457
+ if (data.battery.smartPrediction) {
458
+ const sp = data.battery.smartPrediction;
459
+ lines.push("");
460
+ lines.push(chalk2.bold("SMART RUNTIME FORECAST:"));
461
+ lines.push(statRow("Light Load", `${chalk2.green(sp.scenarios.idle.timeRemaining)} (~${sp.scenarios.idle.ratePerHour}%/h)`));
462
+ lines.push(statRow("Current Load", `${chalk2.yellow(sp.scenarios.current.timeRemaining)} (~${sp.scenarios.current.ratePerHour}%/h)`));
463
+ lines.push(statRow("Heavy Load", `${chalk2.red(sp.scenarios.heavy.timeRemaining)} (~${sp.scenarios.heavy.ratePerHour}%/h)`));
464
+ lines.push(chalk2.dim(`Advice: ${sp.recommendation}`));
445
465
  }
446
466
  return lines;
447
467
  }
@@ -458,11 +478,83 @@ function thermalCard(data, contentWidth) {
458
478
  }
459
479
  function networkCard(data, contentWidth) {
460
480
  const valW = Math.max(6, contentWidth - 8);
461
- return [
462
- statRow("Iface", truncatePlain(`${data.network.interface} (${data.network.ip})`, valW)),
463
- statRow("RX", `${formatBytes(data.network.rxBytes)} / ${data.network.rxPackets} pkt`),
464
- statRow("TX", `${formatBytes(data.network.txBytes)} / ${data.network.txPackets} pkt`)
465
- ];
481
+ const lines = [];
482
+ lines.push(statRow("Iface", truncatePlain(`${data.network.interface} (${data.network.ip})`, valW)));
483
+ const rxTotal = formatBytes(data.network.rxBytes);
484
+ const txTotal = formatBytes(data.network.txBytes);
485
+ const rxRateStr = formatBytes(data.network.rxRate);
486
+ const txRateStr = formatBytes(data.network.txRate);
487
+ lines.push(statRow("RX", `${rxTotal} \xB7 ${rxRateStr}/s`));
488
+ lines.push(statRow("TX", `${txTotal} \xB7 ${txRateStr}/s`));
489
+ const totalBytes = data.network.rxBytes + data.network.txBytes;
490
+ if (totalBytes > 0) {
491
+ const rxPct = Math.round(data.network.rxBytes / totalBytes * 100);
492
+ const barW = Math.max(4, contentWidth - 20);
493
+ const rxBar = gaugeBar(rxPct, barW);
494
+ lines.push(statRow("RX/TX", `${rxBar} ${rxPct}%`));
495
+ }
496
+ if (data.network.protocols) {
497
+ const p = data.network.protocols;
498
+ const totalConns = (p.tcp.connections || 0) + (p.udp.connections || 0);
499
+ if (totalConns > 0) {
500
+ const tcpPct = Math.round(p.tcp.connections / totalConns * 100);
501
+ lines.push(statRow("TCP", `${p.tcp.connections} conns \xB7 ${gaugeBar(tcpPct, Math.max(4, contentWidth - 20))} ${tcpPct}%`));
502
+ lines.push(statRow("UDP", `${p.udp.connections} conns`));
503
+ }
504
+ }
505
+ if (data.network.connectionStates) {
506
+ const cs = data.network.connectionStates;
507
+ const total = cs.established + cs.listening + cs.timeWait + cs.closeWait + cs.other;
508
+ if (total > 0) {
509
+ const estPct = Math.round(cs.established / total * 100);
510
+ lines.push(statRow("Active", `${cs.established} ESTABLISHED \xB7 ${estPct}%`));
511
+ lines.push(statRow("Listen", `${cs.listening} LISTEN`));
512
+ if (cs.timeWait > 0) lines.push(statRow("TimeWait", `${cs.timeWait}`));
513
+ if (cs.closeWait > 0) lines.push(statRow("CloseWait", `${cs.closeWait}`));
514
+ }
515
+ }
516
+ if (data.network.listeningPorts && data.network.listeningPorts.length > 0) {
517
+ const ports = data.network.listeningPorts.slice(0, 8).join(", ");
518
+ lines.push(statRow("Ports", truncatePlain(ports, valW)));
519
+ }
520
+ if (data.network.topRemoteHosts && data.network.topRemoteHosts.length > 0) {
521
+ for (const host of data.network.topRemoteHosts.slice(0, 3)) {
522
+ lines.push(statRow("Host", truncatePlain(`${host.host} (${host.connectionCount})`, valW)));
523
+ }
524
+ }
525
+ return lines;
526
+ }
527
+ function networkDetailCard(data, width, theme) {
528
+ const contentWidth = width - 4;
529
+ const lines = networkCard(data, contentWidth);
530
+ if (data.packets && data.packets.allProcesses && data.packets.allProcesses.length > 0) {
531
+ lines.push("");
532
+ lines.push(chalk2.bold("PROCESSES & BANDWIDTH USAGE:"));
533
+ lines.push(chalk2.dim("\u2500".repeat(contentWidth)));
534
+ const pWidth = 8;
535
+ const rxWidth = 12;
536
+ const txWidth = 12;
537
+ const totalWidth = 12;
538
+ const cmdWidth = Math.max(15, contentWidth - (pWidth + rxWidth + txWidth + totalWidth + 4 * 2));
539
+ lines.push(
540
+ chalk2.bold("PID".padEnd(pWidth)) + " " + chalk2.bold("COMMAND".padEnd(cmdWidth)) + " " + chalk2.bold("RX".padStart(rxWidth)) + " " + chalk2.bold("TX".padStart(txWidth)) + " " + chalk2.bold("TOTAL".padStart(totalWidth))
541
+ );
542
+ const sorted = [...data.packets.allProcesses].sort((a, b) => b.rxBytes + b.txBytes - (a.rxBytes + a.txBytes));
543
+ for (const proc of sorted) {
544
+ if (proc.rxBytes + proc.txBytes === 0) continue;
545
+ const rxStr = formatBytes(proc.rxBytes).padStart(rxWidth);
546
+ const txStr = formatBytes(proc.txBytes).padStart(txWidth);
547
+ const totalStr = formatBytes(proc.rxBytes + proc.txBytes).padStart(totalWidth);
548
+ const cmdStr = truncatePlain(proc.command, cmdWidth).padEnd(cmdWidth);
549
+ lines.push(
550
+ chalk2.cyan(proc.pid.toString().padEnd(pWidth)) + " " + cmdStr + " " + chalk2.yellow(rxStr) + " " + chalk2.yellow(txStr) + " " + chalk2.bold(totalStr)
551
+ );
552
+ }
553
+ } else {
554
+ lines.push("");
555
+ lines.push(chalk2.dim("No active network process data available."));
556
+ }
557
+ return lines;
466
558
  }
467
559
  function tasksCard(data, contentWidth) {
468
560
  if (!data.tasks || !data.tasks.length) return null;
@@ -488,13 +580,14 @@ var TAB_DEFS = [
488
580
  { id: "power", label: "Power", key: "4" },
489
581
  { id: "battery", label: "Battery", key: "5" },
490
582
  { id: "thermal", label: "Thermal", key: "6" },
491
- { id: "network", label: "Network", key: "7" },
492
- { id: "packets", label: "Packets", key: "8" },
493
- { id: "tasks", label: "Tasks", key: "9" },
494
- { id: "disk", label: "Disk", key: "0" },
495
- { id: "process", label: "Process", key: "P" },
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" },
496
587
  { id: "p2p", label: "P2P", key: "R" },
497
- { id: "anomalies", label: "Anomalies", key: "A" }
588
+ { id: "anomalies", label: "Anomalies", key: "A" },
589
+ { id: "containers", label: "Containers", key: "C" },
590
+ { id: "blender", label: "Blender", key: "B" }
498
591
  ];
499
592
  var TAB_BAR_INDENT = 2;
500
593
  var TAB_SEPARATOR_WIDTH = 3;
@@ -645,6 +738,31 @@ function diskTableLines(disks, contentWidth, borderAccent = THEMES.default.borde
645
738
  });
646
739
  return [head, borderAccent("\u2500".repeat(contentWidth)), ...rows];
647
740
  }
741
+ function containersTableLines(containers, contentWidth, borderAccent = THEMES.default.border) {
742
+ const idW = 14, cpuW = 8, memW = 8, memUsgW = 20, netW = 20, blockW = 20, pidsW = 6;
743
+ const nameW = Math.max(10, contentWidth - idW - cpuW - memW - memUsgW - netW - blockW - pidsW - 7);
744
+ const head = chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" CONTAINER ID ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" CPU% ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" MEM% ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" MEM USAGE ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" NET I/O ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" BLOCK I/O ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" PIDS ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" NAME".padEnd(nameW + 1));
745
+ const rows = containers.map((c) => {
746
+ const cpuStr = pctColor(c.cpuPercent)(`${c.cpuPercent.toFixed(1)}`.padEnd(cpuW));
747
+ const memStr = pctColor(c.memPercent)(`${c.memPercent.toFixed(1)}`.padEnd(memW));
748
+ return truncatePlain(c.id, idW - 1).padEnd(idW) + cpuStr + memStr + c.memUsage.padEnd(memUsgW) + c.netIO.padEnd(netW) + c.blockIO.padEnd(blockW) + String(c.pids).padEnd(pidsW) + truncatePlain(c.name, nameW);
749
+ });
750
+ return [head, borderAccent("\u2500".repeat(contentWidth)), ...rows];
751
+ }
752
+ function blenderCard(renders, contentWidth, theme) {
753
+ if (!renders.length) return ["No active Blender renders detected"];
754
+ const lines = [];
755
+ for (const r of renders.slice(0, 8)) {
756
+ const statusColor = r.status === "rendering" ? chalk2.green : r.status === "finalizing" ? chalk2.yellow : r.status === "loading" ? chalk2.cyan : chalk2.dim;
757
+ const pctStr = `${r.completionPercent}%`;
758
+ const barW = Math.max(4, contentWidth - 28);
759
+ const bar = gaugeBar(r.completionPercent, barW);
760
+ lines.push(`${theme.blender(`PID ${r.pid}`)} ${truncatePlain(r.blendFile, 18)} ${r.renderEngine}`);
761
+ lines.push(` Fra ${String(r.currentFrame).padStart(4)}/${r.totalFrames} ${bar} ${pctStr}`);
762
+ lines.push(` ${r.elapsedSec}s elapsed \xB7 ${statusColor(r.status)}${r.sampleCount !== void 0 ? ` \xB7 ${r.sampleCount} spp` : ""}`);
763
+ }
764
+ return lines;
765
+ }
648
766
  function detailPanelTitle(panel2, trackedPid) {
649
767
  const map = {
650
768
  cpu: "CPU",
@@ -657,7 +775,9 @@ function detailPanelTitle(panel2, trackedPid) {
657
775
  packets: "Packets",
658
776
  tasks: "Tasks",
659
777
  disk: "Disk",
660
- process: trackedPid !== void 0 && trackedPid !== null ? `Processes (Following PID ${trackedPid})` : "Processes"
778
+ process: trackedPid !== void 0 && trackedPid !== null ? `Processes (Following PID ${trackedPid})` : "Processes",
779
+ containers: "Containers",
780
+ blender: "Blender Renders"
661
781
  };
662
782
  return map[panel2] || panel2;
663
783
  }
@@ -676,37 +796,50 @@ function activeDetailLines(data, activePanel, width, opts = {}) {
676
796
  }
677
797
  case "power": {
678
798
  const power = powerCard(data);
679
- if (!power && !data.battery) return ["No power data available"];
680
- const lines = [];
681
- if (power) lines.push(...power);
682
- if (data.battery?.powerWatts !== void 0 && data.battery.powerWatts > 0) {
683
- lines.push(statRow("Battery", `${data.battery.powerWatts.toFixed(2)} W`));
684
- }
685
- return lines.length ? lines : ["No power data available"];
799
+ if (!power) return ["No power data available"];
800
+ return power;
686
801
  }
687
802
  case "battery":
688
803
  return data.battery ? batteryCard(data, contentWidth) : ["No battery data available"];
689
804
  case "thermal":
690
805
  return thermalCard(data, contentWidth);
691
806
  case "network":
692
- return networkCard(data, contentWidth);
807
+ return networkDetailCard(data, width, theme);
693
808
  case "packets": {
694
- if (!data.packets) return ["No packet data available"];
695
- const lines = [];
696
- lines.push(statRow("Total", `${data.packets.totalPackets} pkt`));
697
- lines.push(statRow("RX", `${data.packets.rxPackets} pkt`));
698
- lines.push(statRow("TX", `${data.packets.txPackets} pkt`));
699
- lines.push(statRow("Conns", `${data.packets.connections} TCP`));
700
- if (data.packets.interfaces && data.packets.interfaces.length) {
701
- lines.push(statRow("Ifaces", `${data.packets.interfaces.length} active`));
702
- for (const iface of data.packets.interfaces.slice(0, 8)) {
703
- lines.push(statRow(` ${iface.iface}`, `RX ${formatBytes(iface.rxBytes)} / TX ${formatBytes(iface.txBytes)}`));
704
- }
809
+ if (!data.packets || !data.packets.allProcesses || !data.packets.allProcesses.length) {
810
+ return ["No advanced network tracking data available."];
705
811
  }
706
- if (data.packets.allProcesses && data.packets.allProcesses.length) {
707
- lines.push(statRow("Net Procs", `${data.packets.allProcesses.length} total`));
708
- for (const proc of data.packets.allProcesses.slice(0, 10)) {
709
- lines.push(statRow(` ${proc.pid}`, `${truncatePlain(proc.command, 20)} TX ${formatBytes(proc.txBytes)}`));
812
+ const lines = [];
813
+ const pWidth = 8;
814
+ const cmdWidth = 15;
815
+ const protoWidth = 5;
816
+ const stateWidth = 11;
817
+ const rxWidth = 10;
818
+ const txWidth = 10;
819
+ const remaining = Math.max(20, width - (pWidth + cmdWidth + protoWidth + stateWidth + rxWidth + txWidth + 7 * 2));
820
+ const localWidth = Math.floor(remaining * 0.4);
821
+ const remoteWidth = remaining - localWidth;
822
+ const header2 = chalk2.bold("PID".padEnd(pWidth)) + " " + chalk2.bold("COMMAND".padEnd(cmdWidth)) + " " + chalk2.bold("PROTO".padEnd(protoWidth)) + " " + chalk2.bold("LOCAL".padEnd(localWidth)) + " " + chalk2.bold("REMOTE".padEnd(remoteWidth)) + " " + chalk2.bold("STATE".padEnd(stateWidth)) + " " + chalk2.bold("RX".padStart(rxWidth)) + " " + chalk2.bold("TX".padStart(txWidth));
823
+ lines.push(header2);
824
+ lines.push(chalk2.dim("\u2500".repeat(width)));
825
+ for (const proc of data.packets.allProcesses) {
826
+ if (!proc.connections || proc.connections.length === 0) continue;
827
+ const summaryCmd = truncatePlain(proc.command, cmdWidth + protoWidth + localWidth + remoteWidth + stateWidth + 8);
828
+ const summaryRx = formatBytes(proc.rxBytes).padStart(rxWidth);
829
+ const summaryTx = formatBytes(proc.txBytes).padStart(txWidth);
830
+ lines.push(
831
+ chalk2.cyan(proc.pid.toString().padEnd(pWidth)) + " " + chalk2.cyan(summaryCmd.padEnd(width - pWidth - rxWidth - txWidth - 6)) + " " + chalk2.yellow(summaryRx) + " " + chalk2.yellow(summaryTx)
832
+ );
833
+ for (const conn of proc.connections) {
834
+ const cProto = chalk2.dim(truncatePlain(conn.protocol, protoWidth).padEnd(protoWidth));
835
+ const cLocal = chalk2.dim(truncatePlain(conn.local, localWidth).padEnd(localWidth));
836
+ const cRemote = truncatePlain(conn.remote, remoteWidth).padEnd(remoteWidth);
837
+ const cStateStr = conn.state === "Established" ? chalk2.green("ESTABLISHED".padEnd(stateWidth)) : chalk2.dim(truncatePlain(conn.state, stateWidth).padEnd(stateWidth));
838
+ const cRx = chalk2.dim(formatBytes(conn.rxBytes).padStart(rxWidth));
839
+ const cTx = chalk2.dim(formatBytes(conn.txBytes).padStart(txWidth));
840
+ lines.push(
841
+ " ".repeat(pWidth) + " " + " ".repeat(cmdWidth) + " " + cProto + " " + cLocal + " " + cRemote + " " + cStateStr + " " + cRx + " " + cTx
842
+ );
710
843
  }
711
844
  }
712
845
  return lines;
@@ -760,15 +893,40 @@ function activeDetailLines(data, activePanel, width, opts = {}) {
760
893
  if (!opts.anomalyHistory || opts.anomalyHistory.length === 0) return ["No anomalies detected yet."];
761
894
  const lines = [];
762
895
  const sorted = [...opts.anomalyHistory].sort((a, b) => (b.timestamp?.getTime() || 0) - (a.timestamp?.getTime() || 0));
763
- for (const a of sorted) {
896
+ for (const a of sorted.slice(0, 10)) {
764
897
  const color = a.severity === "critical" ? chalk2.red.bold : chalk2.yellow;
765
898
  const direction = a.zScore > 0 ? "\u2191 spike" : "\u2193 drop";
766
899
  const valStr = a.metric.toLowerCase().includes("net") ? `${formatBytes(a.value)}/s` : `${a.value.toFixed(1)}%`;
767
900
  const timeStr = a.timestamp ? a.timestamp.toLocaleTimeString() : "";
768
901
  lines.push(color(` ${timeStr.padEnd(10)} \u26A0 ${a.metric.padEnd(8)}: ${valStr.padEnd(12)} (${direction}, \u03C3=${a.zScore.toFixed(1)})`));
769
902
  }
903
+ lines.push("");
904
+ const modelId = opts.aiModel || "expert-rules-v1";
905
+ const aiAnalysis = explainAnomaliesLocally(sorted, modelId);
906
+ for (const al of aiAnalysis.split("\n")) {
907
+ if (al.startsWith("Diagnosis Engine:")) {
908
+ lines.push(chalk2.bold.magenta(al));
909
+ } else if (al.startsWith("Tip:")) {
910
+ lines.push(chalk2.cyan.bold(al));
911
+ } else if (al.startsWith(" \u2022 Recommendation:")) {
912
+ lines.push(chalk2.green(al));
913
+ } else if (al.startsWith(" \u2022 Cause:")) {
914
+ lines.push(chalk2.yellow(al));
915
+ } else if (al.startsWith(" \u2022 Verdict:")) {
916
+ lines.push(chalk2.dim(al));
917
+ } else {
918
+ lines.push(chalk2.dim(al));
919
+ }
920
+ }
770
921
  return lines;
771
922
  }
923
+ case "containers": {
924
+ return data.containers && data.containers.length ? containersTableLines(data.containers, width - 4, theme.border) : ["No container data available"];
925
+ }
926
+ case "blender": {
927
+ const renders = data.blenderRenders && data.blenderRenders.length ? data.blenderRenders : [];
928
+ return blenderCard(renders, contentWidth, theme);
929
+ }
772
930
  default:
773
931
  return null;
774
932
  }
@@ -992,7 +1150,7 @@ function formatTable(data, opts = {}) {
992
1150
  lines.push(gaugeRow("GPU", data.gpu.utilization, contentWidth));
993
1151
  }
994
1152
  const tempC = data.cpu.temperature ?? data.thermal.temperatures?.cpu_die;
995
- if (tempC !== void 0) {
1153
+ if (tempC != null) {
996
1154
  const tempStr = formatTempUnit(tempC, opts.tempUnit);
997
1155
  lines.push(statRow("Temp", thermalColor(data.thermal.pressureLevel)(tempStr)));
998
1156
  }