opencode-system-metrics-tui 0.1.10 → 0.1.12
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/README.md +18 -13
- package/dist/tui.js +393 -57
- package/package.json +2 -2
- package/scripts/build.ts +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Install globally in OpenCode
|
|
4
4
|
|
|
5
5
|
```powershell
|
|
6
|
-
opencode plugin -g opencode-system-metrics-tui@0.1.
|
|
6
|
+
opencode plugin -g opencode-system-metrics-tui@0.1.12 --force
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
This is the primary installation path. The version must match `package.json`; `--force` asks OpenCode to refresh its package cache. The helper below skips OpenCode when the expected artifact is already current. Verify the actual installed artifact afterward:
|
|
@@ -40,7 +40,8 @@ The README is available in **Español**, **English**, and **Português**.
|
|
|
40
40
|
- Soporte para Linux, macOS y Windows cuando el sistema expone las métricas.
|
|
41
41
|
- En macOS, la utilización de GPU usa `ioreg` y las temperaturas usan el helper de Stats si está instalado; si cualquiera de ellos falta, esa métrica queda como no disponible sin interrumpir el plugin. En Intel se conserva el fallback de GPU discreta de `systeminformation`.
|
|
42
42
|
- En Windows, la temperatura de CPU requiere LibreHardwareMonitor ejecutándose como administrador con **Remote Web Server** activo en `http://127.0.0.1:8085`.
|
|
43
|
-
- En Windows, la velocidad de red usa
|
|
43
|
+
- En Windows, la velocidad de red usa una llamada nativa `GetIfTable2` de `iphlpapi.dll` mediante Koffi; la ruta PowerShell queda como fallback acotado si la API nativa no está disponible.
|
|
44
|
+
- La agregación nativa incluye interfaces de hardware activas, excluye interfaces de filtro, loopback y túnel, y suma sus contadores `InOctets`/`OutOctets`.
|
|
44
45
|
- Las lecturas opcionales de Windows se hacen bajo demanda, con timeout y caché; no hay timers globales de PowerShell.
|
|
45
46
|
- La detección de Nerd Font en Linux/macOS está limitada a rutas conocidas, 2 niveles de profundidad y 256 entradas por directorio; una ruta ausente o inaccesible usa Unicode. Windows no escanea fuentes salvo con `OPENCODE_MONITOR_NERD_FONT=1`.
|
|
46
47
|
- Si una fuente de métricas no responde a tiempo, su intento en caché se abandona de forma segura para permitir reintentos sin cancelar APIs que no aceptan `AbortSignal`.
|
|
@@ -93,7 +94,7 @@ npm publish --access public
|
|
|
93
94
|
En otro equipo:
|
|
94
95
|
|
|
95
96
|
```bash
|
|
96
|
-
opencode plugin opencode-system-metrics-tui@0.1.
|
|
97
|
+
opencode plugin opencode-system-metrics-tui@0.1.12
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
El paquete exporta tanto la raíz (`opencode-system-metrics-tui`) como `./tui`; ambas rutas cargan el mismo bundle TUI.
|
|
@@ -103,8 +104,8 @@ El instalador de OpenCode configura el plugin TUI automáticamente. Reinicia Ope
|
|
|
103
104
|
Publicar en npm e instalar en OpenCode son pasos distintos:
|
|
104
105
|
|
|
105
106
|
- `npm publish --access public` publica el paquete en npm.
|
|
106
|
-
- `opencode plugin opencode-system-metrics-tui@0.1.
|
|
107
|
-
- `opencode plugin -g opencode-system-metrics-tui@0.1.
|
|
107
|
+
- `opencode plugin opencode-system-metrics-tui@0.1.12` lo instala solo en el proyecto actual.
|
|
108
|
+
- `opencode plugin -g opencode-system-metrics-tui@0.1.12 --force` lo instala globalmente para todos los proyectos.
|
|
108
109
|
|
|
109
110
|
### Verificación
|
|
110
111
|
|
|
@@ -133,9 +134,12 @@ npm run build
|
|
|
133
134
|
- Windows uses Unicode icons by default (installed-font detection does not change selection); Linux/macOS use Nerd Font only when installed and retain Unicode fallback.
|
|
134
135
|
- On Windows, visible Unicode icons are used by default because OpenCode cannot detect the terminal's active font.
|
|
135
136
|
- Supports Linux, macOS, and Windows when the operating system exposes the metrics.
|
|
137
|
+
- CPU and RAM use native, read-only paths without subprocesses during normal operation: Linux reads `/proc/stat` and `/proc/meminfo`; Windows uses `GetSystemTimes` and `GlobalMemoryStatusEx` through the lazy Koffi integration; macOS uses Mach host statistics and `hw.memsize` on both Intel and Apple Silicon. If native loading or a read fails, the existing `systeminformation` path is used with the existing timeout and cache protections.
|
|
138
|
+
- Native CPU percentages are deltas between consecutive samples; the first sample is intentionally unavailable. Counter resets, malformed data, and API failures fall back safely. Apple Silicon retains unified-memory semantics and does not treat system RAM as discrete VRAM.
|
|
136
139
|
- On macOS, GPU utilization uses `ioreg` and temperatures use the Stats helper when installed; missing helpers leave only that metric unavailable without interrupting the plugin. Intel retains the discrete-GPU `systeminformation` fallback.
|
|
137
140
|
- On Windows, CPU temperature requires LibreHardwareMonitor running as administrator with **Remote Web Server** enabled at `http://127.0.0.1:8085`.
|
|
138
|
-
- On Windows, network speed uses
|
|
141
|
+
- On Windows, network speed uses one native `GetIfTable2` call from `iphlpapi.dll` through Koffi; the PowerShell path remains only as a bounded fallback when the native API cannot load.
|
|
142
|
+
- Native aggregation includes active hardware interfaces, excludes filter, loopback, and tunnel interfaces, and sums their `InOctets`/`OutOctets` counters.
|
|
139
143
|
- Optional Windows reads are on demand, timeout-bounded, and cached; no global PowerShell timers are used.
|
|
140
144
|
- Linux/macOS Nerd Font detection is limited to known paths, two directory levels, and 256 entries per directory; missing or inaccessible paths safely fall back to Unicode. Windows does not scan fonts unless `OPENCODE_MONITOR_NERD_FONT=1` is set.
|
|
141
145
|
- When a metric source does not respond in time, its cache attempt is safely abandoned so retries remain possible without cancelling APIs that do not accept `AbortSignal`.
|
|
@@ -188,7 +192,7 @@ npm publish --access public
|
|
|
188
192
|
On another machine:
|
|
189
193
|
|
|
190
194
|
```bash
|
|
191
|
-
opencode plugin opencode-system-metrics-tui@0.1.
|
|
195
|
+
opencode plugin opencode-system-metrics-tui@0.1.12
|
|
192
196
|
```
|
|
193
197
|
|
|
194
198
|
The package exports both the root (`opencode-system-metrics-tui`) and `./tui`; both paths load the same TUI bundle.
|
|
@@ -198,8 +202,8 @@ The OpenCode installer configures the TUI plugin automatically. Restart OpenCode
|
|
|
198
202
|
Publishing to npm and installing in OpenCode are separate steps:
|
|
199
203
|
|
|
200
204
|
- `npm publish --access public` publishes the package to npm.
|
|
201
|
-
- `opencode plugin opencode-system-metrics-tui@0.1.
|
|
202
|
-
- `opencode plugin -g opencode-system-metrics-tui@0.1.
|
|
205
|
+
- `opencode plugin opencode-system-metrics-tui@0.1.12` installs it for the current project only.
|
|
206
|
+
- `opencode plugin -g opencode-system-metrics-tui@0.1.12 --force` installs it globally for all projects.
|
|
203
207
|
|
|
204
208
|
### Verification
|
|
205
209
|
|
|
@@ -229,7 +233,8 @@ npm run build
|
|
|
229
233
|
- No Windows, ícones Unicode visíveis são usados por padrão porque o OpenCode não consegue detectar a fonte ativa do terminal.
|
|
230
234
|
- Suporte para Linux, macOS e Windows quando o sistema disponibiliza as métricas.
|
|
231
235
|
- No Windows, a temperatura da CPU requer o LibreHardwareMonitor executado como administrador com o **Remote Web Server** ativo em `http://127.0.0.1:8085`.
|
|
232
|
-
- No Windows, a velocidade da rede usa o PowerShell
|
|
236
|
+
- No Windows, a velocidade da rede usa uma chamada nativa `GetIfTable2` de `iphlpapi.dll` via Koffi; o PowerShell permanece apenas como fallback limitado quando a API nativa não pode ser carregada.
|
|
237
|
+
- A agregação nativa inclui interfaces de hardware ativas, exclui interfaces de filtro, loopback e túnel, e soma os contadores `InOctets`/`OutOctets`.
|
|
233
238
|
- As leituras opcionais do Windows são sob demanda, têm timeout e cache; não há timers globais do PowerShell.
|
|
234
239
|
- A detecção de Nerd Font no Linux/macOS é limitada a caminhos conhecidos, dois níveis de profundidade e 256 entradas por diretório; caminhos ausentes ou inacessíveis usam Unicode. O Windows não verifica fontes sem `OPENCODE_MONITOR_NERD_FONT=1`.
|
|
235
240
|
- Quando uma fonte de métricas não responde a tempo, a tentativa em cache é abandonada com segurança para permitir novas tentativas sem cancelar APIs que não aceitam `AbortSignal`.
|
|
@@ -282,7 +287,7 @@ npm publish --access public
|
|
|
282
287
|
Em outro computador:
|
|
283
288
|
|
|
284
289
|
```bash
|
|
285
|
-
opencode plugin opencode-system-metrics-tui@0.1.
|
|
290
|
+
opencode plugin opencode-system-metrics-tui@0.1.12
|
|
286
291
|
```
|
|
287
292
|
|
|
288
293
|
O pacote exporta tanto a raiz (`opencode-system-metrics-tui`) quanto `./tui`; os dois caminhos carregam o mesmo bundle TUI.
|
|
@@ -292,8 +297,8 @@ O instalador do OpenCode configura o plugin TUI automaticamente. Reinicie o Open
|
|
|
292
297
|
Publicar no npm e instalar no OpenCode são etapas diferentes:
|
|
293
298
|
|
|
294
299
|
- `npm publish --access public` publica o pacote no npm.
|
|
295
|
-
- `opencode plugin opencode-system-metrics-tui@0.1.
|
|
296
|
-
- `opencode plugin -g opencode-system-metrics-tui@0.1.
|
|
300
|
+
- `opencode plugin opencode-system-metrics-tui@0.1.12` instala o plugin apenas no projeto atual.
|
|
301
|
+
- `opencode plugin -g opencode-system-metrics-tui@0.1.12 --force` instala o plugin globalmente para todos os projetos.
|
|
297
302
|
|
|
298
303
|
### Verificação
|
|
299
304
|
|
package/dist/tui.js
CHANGED
|
@@ -6,7 +6,7 @@ import { insertNode as _$insertNode } from "@opentui/solid";
|
|
|
6
6
|
import { insert as _$insert } from "@opentui/solid";
|
|
7
7
|
import { setProp as _$setProp } from "@opentui/solid";
|
|
8
8
|
import { createElement as _$createElement } from "@opentui/solid";
|
|
9
|
-
import { createMemo, createSignal, onCleanup, onMount } from "solid-js";
|
|
9
|
+
import { Show, createMemo, createSignal, onCleanup, onMount } from "solid-js";
|
|
10
10
|
import { TextAttributes } from "@opentui/core";
|
|
11
11
|
|
|
12
12
|
// src/format.ts
|
|
@@ -39,6 +39,311 @@ function formatRate(bytesPerSecond) {
|
|
|
39
39
|
import { execFile } from "node:child_process";
|
|
40
40
|
import { promisify } from "node:util";
|
|
41
41
|
import si from "systeminformation";
|
|
42
|
+
|
|
43
|
+
// src/windows-network.ts
|
|
44
|
+
var IF_TYPE_SOFTWARE_LOOPBACK = 24;
|
|
45
|
+
var IF_TYPE_TUNNEL = 131;
|
|
46
|
+
var IF_OPER_STATUS_UP = 1;
|
|
47
|
+
var NET_IF_ACCESS_LOOPBACK = 1;
|
|
48
|
+
var ROWS_OFFSET = 8;
|
|
49
|
+
var nativeReader;
|
|
50
|
+
function toUint64(value) {
|
|
51
|
+
if (typeof value === "bigint" && value >= 0n)
|
|
52
|
+
return value;
|
|
53
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0)
|
|
54
|
+
return BigInt(value);
|
|
55
|
+
return 0n;
|
|
56
|
+
}
|
|
57
|
+
function mapWindowsNetworkRows(rows) {
|
|
58
|
+
return rows.reduce((total, row) => {
|
|
59
|
+
const flags = row.InterfaceAndOperStatusFlags;
|
|
60
|
+
const isHardware = typeof flags === "number" ? (flags & 1) !== 0 : flags?.HardwareInterface === 1;
|
|
61
|
+
const isFilter = typeof flags === "number" ? (flags & 2) !== 0 : flags?.FilterInterface === 1;
|
|
62
|
+
const isUsable = row.OperStatus === IF_OPER_STATUS_UP && row.AccessType !== NET_IF_ACCESS_LOOPBACK && row.Type !== IF_TYPE_SOFTWARE_LOOPBACK && row.Type !== IF_TYPE_TUNNEL;
|
|
63
|
+
if (!isHardware || isFilter || !isUsable)
|
|
64
|
+
return total;
|
|
65
|
+
return { rx: total.rx + toUint64(row.InOctets), tx: total.tx + toUint64(row.OutOctets) };
|
|
66
|
+
}, { rx: 0n, tx: 0n });
|
|
67
|
+
}
|
|
68
|
+
async function readWithFallback(primary, fallback) {
|
|
69
|
+
try {
|
|
70
|
+
return { value: await primary(), usedFallback: false };
|
|
71
|
+
} catch {
|
|
72
|
+
return { value: await fallback(), usedFallback: true };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function networkRate(current, previous, elapsedMs) {
|
|
76
|
+
if (previous === undefined)
|
|
77
|
+
return { rx: undefined, tx: undefined };
|
|
78
|
+
const seconds = Math.max(elapsedMs / 1000, 0.001);
|
|
79
|
+
const rx = current.rx >= previous.rx ? current.rx - previous.rx : 0n;
|
|
80
|
+
const tx = current.tx >= previous.tx ? current.tx - previous.tx : 0n;
|
|
81
|
+
return { rx: Number(rx) / seconds, tx: Number(tx) / seconds };
|
|
82
|
+
}
|
|
83
|
+
function defineNativeReader(koffi) {
|
|
84
|
+
const guid = koffi.struct("MIB_GUID", {
|
|
85
|
+
Data1: "uint32",
|
|
86
|
+
Data2: "uint16",
|
|
87
|
+
Data3: "uint16",
|
|
88
|
+
Data4: koffi.array("uint8", 8)
|
|
89
|
+
});
|
|
90
|
+
const row = koffi.struct("MIB_IF_ROW2", {
|
|
91
|
+
InterfaceLuid: "uint64",
|
|
92
|
+
InterfaceIndex: "uint32",
|
|
93
|
+
InterfaceGuid: guid,
|
|
94
|
+
Alias: koffi.array("char16_t", 257),
|
|
95
|
+
Description: koffi.array("char16_t", 257),
|
|
96
|
+
PhysicalAddressLength: "uint32",
|
|
97
|
+
PhysicalAddress: koffi.array("uint8", 32),
|
|
98
|
+
PermanentPhysicalAddress: koffi.array("uint8", 32),
|
|
99
|
+
Mtu: "uint32",
|
|
100
|
+
Type: "uint32",
|
|
101
|
+
TunnelType: "uint32",
|
|
102
|
+
MediaType: "uint32",
|
|
103
|
+
PhysicalMediumType: "uint32",
|
|
104
|
+
AccessType: "uint32",
|
|
105
|
+
DirectionType: "uint32",
|
|
106
|
+
InterfaceAndOperStatusFlags: "uint8",
|
|
107
|
+
OperStatus: "uint32",
|
|
108
|
+
AdminStatus: "uint32",
|
|
109
|
+
MediaConnectState: "uint32",
|
|
110
|
+
NetworkGuid: guid,
|
|
111
|
+
ConnectionType: "uint32",
|
|
112
|
+
TransmitLinkSpeed: "uint64",
|
|
113
|
+
ReceiveLinkSpeed: "uint64",
|
|
114
|
+
InOctets: "uint64",
|
|
115
|
+
InUcastPkts: "uint64",
|
|
116
|
+
InNUcastPkts: "uint64",
|
|
117
|
+
InDiscards: "uint64",
|
|
118
|
+
InErrors: "uint64",
|
|
119
|
+
InUnknownProtos: "uint64",
|
|
120
|
+
InUcastOctets: "uint64",
|
|
121
|
+
InMulticastOctets: "uint64",
|
|
122
|
+
InBroadcastOctets: "uint64",
|
|
123
|
+
OutOctets: "uint64",
|
|
124
|
+
OutUcastPkts: "uint64",
|
|
125
|
+
OutNUcastPkts: "uint64",
|
|
126
|
+
OutDiscards: "uint64",
|
|
127
|
+
OutErrors: "uint64",
|
|
128
|
+
OutUcastOctets: "uint64",
|
|
129
|
+
OutMulticastOctets: "uint64",
|
|
130
|
+
OutBroadcastOctets: "uint64",
|
|
131
|
+
OutQLen: "uint64"
|
|
132
|
+
});
|
|
133
|
+
if (koffi.sizeof(row) !== 1352)
|
|
134
|
+
throw new Error(`Unexpected MIB_IF_ROW2 size: ${koffi.sizeof(row)}`);
|
|
135
|
+
const iphlpapi = koffi.load("iphlpapi.dll");
|
|
136
|
+
const getIfTable2 = iphlpapi.func("uint32 __stdcall GetIfTable2(_Out_ void **Table)");
|
|
137
|
+
const freeMibTable = iphlpapi.func("void __stdcall FreeMibTable(void *Memory)");
|
|
138
|
+
return () => {
|
|
139
|
+
const output = [null];
|
|
140
|
+
const result = getIfTable2(output);
|
|
141
|
+
if (result !== 0 || output[0] === null)
|
|
142
|
+
throw new Error(`GetIfTable2 failed: ${result}`);
|
|
143
|
+
const table = output[0];
|
|
144
|
+
try {
|
|
145
|
+
const count = Number(koffi.decode(table, 0, "uint32"));
|
|
146
|
+
if (!Number.isSafeInteger(count) || count < 0 || count > 4096)
|
|
147
|
+
throw new Error(`Invalid interface count: ${count}`);
|
|
148
|
+
return Array.from({ length: count }, (_, index) => koffi.decode(table, ROWS_OFFSET + index * koffi.sizeof(row), row));
|
|
149
|
+
} finally {
|
|
150
|
+
freeMibTable(table);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async function loadNativeReader() {
|
|
155
|
+
const loaded = await import("koffi");
|
|
156
|
+
return defineNativeReader(loaded.default ?? loaded);
|
|
157
|
+
}
|
|
158
|
+
async function readWindowsNetworkCounters() {
|
|
159
|
+
nativeReader ??= loadNativeReader();
|
|
160
|
+
return mapWindowsNetworkRows((await nativeReader)());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/native-metrics.ts
|
|
164
|
+
import { readFile } from "node:fs/promises";
|
|
165
|
+
function parseLinuxCpuCounters(stat) {
|
|
166
|
+
const line = stat.split(/\r?\n/).find((item) => /^cpu(?:\s|$)/.test(item));
|
|
167
|
+
if (line === undefined)
|
|
168
|
+
return;
|
|
169
|
+
const fields = line.trim().split(/\s+/).slice(1);
|
|
170
|
+
if (fields.length < 4)
|
|
171
|
+
return;
|
|
172
|
+
const values = fields.map((value) => {
|
|
173
|
+
try {
|
|
174
|
+
const parsed = BigInt(value);
|
|
175
|
+
return parsed >= 0n ? parsed : undefined;
|
|
176
|
+
} catch {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (values.some((value) => value === undefined))
|
|
181
|
+
return;
|
|
182
|
+
const numbers = values;
|
|
183
|
+
return { total: numbers.reduce((sum, value) => sum + value, 0n), idle: numbers[3] + (numbers[4] ?? 0n) };
|
|
184
|
+
}
|
|
185
|
+
function cpuPercentFromCounters(current, previous) {
|
|
186
|
+
if (previous === undefined || current.total < previous.total || current.idle < previous.idle)
|
|
187
|
+
return;
|
|
188
|
+
const totalDelta = current.total - previous.total;
|
|
189
|
+
const idleDelta = current.idle - previous.idle;
|
|
190
|
+
if (totalDelta <= 0n || idleDelta > totalDelta)
|
|
191
|
+
return;
|
|
192
|
+
return Number((totalDelta - idleDelta) * 10000n / totalDelta) / 100;
|
|
193
|
+
}
|
|
194
|
+
function parseLinuxMemoryInfo(meminfo) {
|
|
195
|
+
const values = new Map;
|
|
196
|
+
for (const line of meminfo.split(/\r?\n/)) {
|
|
197
|
+
const match = /^(MemTotal|MemAvailable|MemFree|Buffers|Cached):\s+(\d+)(?:\s+kB)?\s*$/.exec(line);
|
|
198
|
+
if (match !== null)
|
|
199
|
+
values.set(match[1], Number(match[2]) * 1024);
|
|
200
|
+
}
|
|
201
|
+
const total = values.get("MemTotal");
|
|
202
|
+
const hasAvailable = values.has("MemAvailable") || values.has("MemFree") || values.has("Buffers") || values.has("Cached");
|
|
203
|
+
const available = values.get("MemAvailable") ?? (values.get("MemFree") ?? 0) + (values.get("Buffers") ?? 0) + (values.get("Cached") ?? 0);
|
|
204
|
+
if (total === undefined || !hasAvailable || !Number.isSafeInteger(total) || !Number.isSafeInteger(available) || total <= 0 || available < 0)
|
|
205
|
+
return;
|
|
206
|
+
return { totalBytes: total, availableBytes: Math.min(total, available) };
|
|
207
|
+
}
|
|
208
|
+
function mapMemoryBytes(totalBytes, availableBytes) {
|
|
209
|
+
if (!Number.isSafeInteger(totalBytes) || !Number.isSafeInteger(availableBytes) || totalBytes <= 0 || availableBytes < 0)
|
|
210
|
+
return;
|
|
211
|
+
return { memoryTotalBytes: totalBytes, memoryAvailableBytes: Math.min(totalBytes, availableBytes) };
|
|
212
|
+
}
|
|
213
|
+
var reader;
|
|
214
|
+
async function readNativeMetrics() {
|
|
215
|
+
reader ??= (async () => {
|
|
216
|
+
if (process.platform === "linux")
|
|
217
|
+
return createLinuxReader();
|
|
218
|
+
if (process.platform === "win32")
|
|
219
|
+
return createWindowsReader();
|
|
220
|
+
if (process.platform === "darwin")
|
|
221
|
+
return createMacReader();
|
|
222
|
+
throw new Error(`Native CPU/RAM metrics are unsupported on ${process.platform}`);
|
|
223
|
+
})();
|
|
224
|
+
return (await reader)();
|
|
225
|
+
}
|
|
226
|
+
function createLinuxReader() {
|
|
227
|
+
let previous;
|
|
228
|
+
return async () => {
|
|
229
|
+
const [stat, meminfo] = await Promise.all([readFile("/proc/stat", "utf8"), readFile("/proc/meminfo", "utf8")]);
|
|
230
|
+
const counters = parseLinuxCpuCounters(stat);
|
|
231
|
+
const memory = parseLinuxMemoryInfo(meminfo);
|
|
232
|
+
if (counters === undefined || memory === undefined)
|
|
233
|
+
throw new Error("Malformed Linux CPU/RAM data");
|
|
234
|
+
const cpuPercent = cpuPercentFromCounters(counters, previous);
|
|
235
|
+
previous = cpuPercent === undefined && previous !== undefined ? undefined : counters;
|
|
236
|
+
return { cpuPercent, memoryTotalBytes: memory.totalBytes, memoryAvailableBytes: memory.availableBytes };
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async function createWindowsReader() {
|
|
240
|
+
const loaded = await import("koffi");
|
|
241
|
+
const koffi = loaded.default ?? loaded;
|
|
242
|
+
koffi.struct("FILETIME", { low: "uint32", high: "uint32" });
|
|
243
|
+
const memoryStatus = koffi.struct("MEMORYSTATUSEX", {
|
|
244
|
+
length: "uint32",
|
|
245
|
+
memoryLoad: "uint32",
|
|
246
|
+
total: "uint64",
|
|
247
|
+
available: "uint64",
|
|
248
|
+
pageFile: "uint64",
|
|
249
|
+
availablePageFile: "uint64",
|
|
250
|
+
virtual: "uint64",
|
|
251
|
+
availableVirtual: "uint64",
|
|
252
|
+
availableExtended: "uint64"
|
|
253
|
+
});
|
|
254
|
+
const kernel32 = koffi.load("kernel32.dll");
|
|
255
|
+
const getSystemTimes = kernel32.func("int __stdcall GetSystemTimes(_Out_ FILETIME *idle, _Out_ FILETIME *kernel, _Out_ FILETIME *user)");
|
|
256
|
+
const globalMemoryStatusEx = kernel32.func("int __stdcall GlobalMemoryStatusEx(_Inout_ MEMORYSTATUSEX *status)");
|
|
257
|
+
let previous;
|
|
258
|
+
return async () => {
|
|
259
|
+
const idle = {};
|
|
260
|
+
const kernel = {};
|
|
261
|
+
const user = {};
|
|
262
|
+
if (!getSystemTimes(idle, kernel, user))
|
|
263
|
+
throw new Error("GetSystemTimes failed");
|
|
264
|
+
const toUint64 = (value) => BigInt(value.low ?? 0) + (BigInt(value.high ?? 0) << 32n);
|
|
265
|
+
const counters = { total: toUint64(kernel) + toUint64(user), idle: toUint64(idle) };
|
|
266
|
+
const cpuPercent = cpuPercentFromCounters(counters, previous);
|
|
267
|
+
previous = cpuPercent === undefined && previous !== undefined ? undefined : counters;
|
|
268
|
+
const status = { length: koffi.sizeof(memoryStatus) };
|
|
269
|
+
if (!globalMemoryStatusEx(status))
|
|
270
|
+
throw new Error("GlobalMemoryStatusEx failed");
|
|
271
|
+
const memory = mapMemoryBytes(Number(status.total ?? 0n), Number(status.available ?? 0n));
|
|
272
|
+
if (memory === undefined)
|
|
273
|
+
throw new Error("Malformed Windows RAM data");
|
|
274
|
+
return { cpuPercent, ...memory };
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
async function createMacReader() {
|
|
278
|
+
const loaded = await import("koffi");
|
|
279
|
+
const koffi = loaded.default ?? loaded;
|
|
280
|
+
const vmStatistics = koffi.struct("vm_statistics64", {
|
|
281
|
+
free_count: "uint32",
|
|
282
|
+
active_count: "uint32",
|
|
283
|
+
inactive_count: "uint32",
|
|
284
|
+
wire_count: "uint32",
|
|
285
|
+
zero_fill_count: "uint32",
|
|
286
|
+
reactivations: "uint32",
|
|
287
|
+
pageins: "uint32",
|
|
288
|
+
pageouts: "uint32",
|
|
289
|
+
faults: "uint32",
|
|
290
|
+
cow_faults: "uint32",
|
|
291
|
+
lookups: "uint32",
|
|
292
|
+
hits: "uint32",
|
|
293
|
+
purgeable_count: "uint32",
|
|
294
|
+
speculative_count: "uint32",
|
|
295
|
+
decompressions: "uint32",
|
|
296
|
+
compressions: "uint32",
|
|
297
|
+
swapins: "uint32",
|
|
298
|
+
swapouts: "uint32",
|
|
299
|
+
compressor_page_count: "uint32",
|
|
300
|
+
throttled_count: "uint32",
|
|
301
|
+
external_page_count: "uint32",
|
|
302
|
+
internal_page_count: "uint32",
|
|
303
|
+
total_uncompressed_pages_in_compressor: "uint32"
|
|
304
|
+
});
|
|
305
|
+
const cpuLoad = koffi.struct("host_cpu_load_info", { user: "uint32", system: "uint32", idle: "uint32", nice: "uint32" });
|
|
306
|
+
const libc = koffi.load("libSystem.B.dylib");
|
|
307
|
+
const sysctlbyname = libc.func("int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen)");
|
|
308
|
+
const machHostSelf = libc.func("uint32 mach_host_self()");
|
|
309
|
+
const hostStatistics64 = libc.func("int host_statistics64(uint32 host, int flavor, _Out_ vm_statistics64 *stats, _Inout_ uint32_t *count)");
|
|
310
|
+
const hostCpuLoad = libc.func("int host_statistics(uint32 host, int flavor, _Out_ host_cpu_load_info *load, _Inout_ uint32_t *count)");
|
|
311
|
+
const readUint64Sysctl = (name) => {
|
|
312
|
+
const output = koffi.alloc("uint64", 1);
|
|
313
|
+
const length = [8];
|
|
314
|
+
if (sysctlbyname(name, output, length, null, 0) !== 0)
|
|
315
|
+
throw new Error(`sysctlbyname failed: ${name}`);
|
|
316
|
+
return Number(koffi.decode(output, "uint64"));
|
|
317
|
+
};
|
|
318
|
+
const totalBytes = readUint64Sysctl("hw.memsize");
|
|
319
|
+
const pageSize = readUint64Sysctl("hw.pagesize");
|
|
320
|
+
if (!Number.isSafeInteger(totalBytes) || !Number.isSafeInteger(pageSize) || pageSize <= 0)
|
|
321
|
+
throw new Error("Malformed macOS memory data");
|
|
322
|
+
let previous;
|
|
323
|
+
return async () => {
|
|
324
|
+
const load = {};
|
|
325
|
+
const loadCount = [4];
|
|
326
|
+
if (hostCpuLoad(machHostSelf(), 3, load, loadCount) !== 0)
|
|
327
|
+
throw new Error("host_statistics failed");
|
|
328
|
+
const counters = {
|
|
329
|
+
total: BigInt(load.user ?? 0) + BigInt(load.system ?? 0) + BigInt(load.idle ?? 0) + BigInt(load.nice ?? 0),
|
|
330
|
+
idle: BigInt(load.idle ?? 0)
|
|
331
|
+
};
|
|
332
|
+
const cpuPercent = cpuPercentFromCounters(counters, previous);
|
|
333
|
+
previous = cpuPercent === undefined && previous !== undefined ? undefined : counters;
|
|
334
|
+
const stats = {};
|
|
335
|
+
const count = [38];
|
|
336
|
+
if (hostStatistics64(machHostSelf(), 4, stats, count) !== 0)
|
|
337
|
+
throw new Error("host_statistics64 failed");
|
|
338
|
+
const availablePages = (stats.free_count ?? 0) + (stats.inactive_count ?? 0) + (stats.purgeable_count ?? 0) + (stats.speculative_count ?? 0);
|
|
339
|
+
const memory = mapMemoryBytes(totalBytes, availablePages * pageSize);
|
|
340
|
+
if (memory === undefined)
|
|
341
|
+
throw new Error("Malformed macOS RAM data");
|
|
342
|
+
return { cpuPercent, ...memory };
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/metrics.ts
|
|
42
347
|
var execFileAsync = promisify(execFile);
|
|
43
348
|
var DEFAULT_METRIC_TIMEOUT_MS = 1500;
|
|
44
349
|
var WINDOWS_MEMORY_TIMEOUT_MS = 5000;
|
|
@@ -139,7 +444,7 @@ function parseWindowsNetworkOutput(stdout) {
|
|
|
139
444
|
tx_bytes: finite(item.SentBytes) ?? 0
|
|
140
445
|
}));
|
|
141
446
|
}
|
|
142
|
-
async function
|
|
447
|
+
async function readWindowsNetworkFallback() {
|
|
143
448
|
const { stdout } = await execFileAsync("powershell.exe", [
|
|
144
449
|
"-NoProfile",
|
|
145
450
|
"-NonInteractive",
|
|
@@ -286,20 +591,28 @@ var cachedNetwork = {
|
|
|
286
591
|
inFlight: undefined
|
|
287
592
|
};
|
|
288
593
|
var previousWindowsNetwork;
|
|
594
|
+
var previousNativeWindowsNetwork;
|
|
289
595
|
async function readWindowsNetworkMetrics() {
|
|
290
|
-
const samples = await readWindowsNetworkSample();
|
|
291
596
|
const timestamp = Date.now();
|
|
292
|
-
const
|
|
293
|
-
|
|
597
|
+
const fallback = async () => {
|
|
598
|
+
const samples = await readWindowsNetworkFallback();
|
|
599
|
+
const rx = samples.reduce((total, item) => total + (finite(item.rx_bytes) ?? 0), 0);
|
|
600
|
+
const tx = samples.reduce((total, item) => total + (finite(item.tx_bytes) ?? 0), 0);
|
|
601
|
+
return { rx: BigInt(rx), tx: BigInt(tx) };
|
|
602
|
+
};
|
|
603
|
+
const { value: counters, usedFallback } = await readWithFallback(readWindowsNetworkCounters, fallback);
|
|
604
|
+
if (!usedFallback) {
|
|
605
|
+
const rate = networkRate(counters, previousNativeWindowsNetwork?.counters, previousNativeWindowsNetwork === undefined ? 0 : timestamp - previousNativeWindowsNetwork.timestamp);
|
|
606
|
+
previousNativeWindowsNetwork = { timestamp, counters };
|
|
607
|
+
return [{ rx_bytes: Number(counters.rx), tx_bytes: Number(counters.tx), rx_sec: rate.rx, tx_sec: rate.tx }];
|
|
608
|
+
}
|
|
294
609
|
const seconds = previousWindowsNetwork === undefined ? undefined : Math.max((timestamp - previousWindowsNetwork.timestamp) / 1000, 0.001);
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
tx_sec: seconds === undefined ? undefined : Math.max(0, tx - previousWindowsNetwork.tx) / seconds
|
|
300
|
-
}];
|
|
610
|
+
const rx = Number(counters.rx);
|
|
611
|
+
const tx = Number(counters.tx);
|
|
612
|
+
const rxSec = seconds === undefined ? undefined : Math.max(0, rx - previousWindowsNetwork.rx) / seconds;
|
|
613
|
+
const txSec = seconds === undefined ? undefined : Math.max(0, tx - previousWindowsNetwork.tx) / seconds;
|
|
301
614
|
previousWindowsNetwork = { timestamp, rx, tx };
|
|
302
|
-
return
|
|
615
|
+
return [{ rx_bytes: rx, tx_bytes: tx, rx_sec: rxSec, tx_sec: txSec }];
|
|
303
616
|
}
|
|
304
617
|
async function readWindowsCpuTemperature() {
|
|
305
618
|
try {
|
|
@@ -321,21 +634,31 @@ async function readWindowsCpuTemperature() {
|
|
|
321
634
|
return value === undefined ? undefined : { main: value };
|
|
322
635
|
}
|
|
323
636
|
async function readMetrics() {
|
|
324
|
-
const [
|
|
325
|
-
withTimeout(
|
|
326
|
-
withTimeout(si.mem(), isWindows ? WINDOWS_MEMORY_TIMEOUT_MS : DEFAULT_METRIC_TIMEOUT_MS),
|
|
637
|
+
const [native, hardwareArchitecture] = await Promise.allSettled([
|
|
638
|
+
withTimeout(readNativeMetrics(), isWindows ? WINDOWS_MEMORY_TIMEOUT_MS : DEFAULT_METRIC_TIMEOUT_MS),
|
|
327
639
|
readMacHardwareArchitecture()
|
|
328
640
|
]);
|
|
641
|
+
let cpuPercent = native.status === "fulfilled" ? finite(native.value.cpuPercent) : null;
|
|
642
|
+
let memoryTotalBytes = native.status === "fulfilled" ? finite(native.value.memoryTotalBytes) : null;
|
|
643
|
+
let memoryAvailableBytes = native.status === "fulfilled" ? finite(native.value.memoryAvailableBytes) : null;
|
|
644
|
+
let fallbackMemoryUsedBytes = null;
|
|
645
|
+
if (native.status !== "fulfilled") {
|
|
646
|
+
const [load, memory] = await Promise.allSettled([
|
|
647
|
+
withTimeout(si.currentLoad(), DEFAULT_METRIC_TIMEOUT_MS),
|
|
648
|
+
withTimeout(si.mem(), isWindows ? WINDOWS_MEMORY_TIMEOUT_MS : DEFAULT_METRIC_TIMEOUT_MS)
|
|
649
|
+
]);
|
|
650
|
+
cpuPercent = load.status === "fulfilled" ? finite(load.value.currentLoad) : null;
|
|
651
|
+
memoryTotalBytes = memory.status === "fulfilled" ? finite(memory.value.total) : null;
|
|
652
|
+
memoryAvailableBytes = memory.status === "fulfilled" ? finite(memory.value.available) : null;
|
|
653
|
+
fallbackMemoryUsedBytes = memoryTotalBytes !== null && memoryAvailableBytes !== null ? Math.max(0, memoryTotalBytes - memoryAvailableBytes) : memory.status === "fulfilled" ? finite(memory.value.used) : null;
|
|
654
|
+
}
|
|
329
655
|
const [temperature, graphics, network, mac] = await Promise.allSettled([
|
|
330
656
|
readCachedMetric(cachedCpuTemperature, isWindows ? readWindowsCpuTemperature : () => si.cpuTemperature(), TEMPERATURE_CACHE_MS, Date.now(), TEMPERATURE_CACHE_MS, 2500),
|
|
331
657
|
readCachedMetric(cachedGraphics, () => si.graphics(), SLOW_METRIC_CACHE_MS, Date.now(), SLOW_METRIC_CACHE_MS, 2500),
|
|
332
658
|
readCachedMetric(cachedNetwork, isWindows ? readWindowsNetworkMetrics : () => si.networkStats("*"), isWindows ? NETWORK_CACHE_MS : 2000, Date.now(), OPTIONAL_SOURCE_BACKOFF_MS, isWindows ? WINDOWS_NETWORK_OUTER_TIMEOUT_MS : DEFAULT_METRIC_TIMEOUT_MS),
|
|
333
659
|
isMac ? readCachedMetric(cachedMacMetrics, () => readMacMetrics(), SLOW_METRIC_CACHE_MS, Date.now(), OPTIONAL_SOURCE_BACKOFF_MS, MAC_METRIC_TIMEOUT_MS) : Promise.resolve(undefined)
|
|
334
660
|
]);
|
|
335
|
-
const
|
|
336
|
-
const memoryTotalBytes = memory.status === "fulfilled" ? finite(memory.value.total) : null;
|
|
337
|
-
const memoryAvailableBytes = memory.status === "fulfilled" ? finite(memory.value.available) : null;
|
|
338
|
-
const memoryUsedBytes = memoryTotalBytes !== null && memoryAvailableBytes !== null ? Math.max(0, memoryTotalBytes - memoryAvailableBytes) : memory.status === "fulfilled" ? finite(memory.value.used) : null;
|
|
661
|
+
const memoryUsedBytes = fallbackMemoryUsedBytes ?? (memoryTotalBytes !== null && memoryAvailableBytes !== null ? Math.max(0, memoryTotalBytes - memoryAvailableBytes) : null);
|
|
339
662
|
const memoryPercent = memoryUsedBytes !== null && memoryTotalBytes !== null && memoryTotalBytes > 0 ? memoryUsedBytes / memoryTotalBytes * 100 : null;
|
|
340
663
|
const gpu = selectGpuMetrics(graphics.status === "fulfilled" ? graphics.value : undefined);
|
|
341
664
|
const macMetrics = mac.status === "fulfilled" ? mac.value : undefined;
|
|
@@ -527,12 +850,11 @@ function MetricsPanel(props) {
|
|
|
527
850
|
};
|
|
528
851
|
});
|
|
529
852
|
return (() => {
|
|
530
|
-
var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createTextNode(` `), _el$4 = _$createElement("box"), _el$5 = _$createElement("text"), _el$6 = _$createElement("text"), _el$8 = _$createElement("text"), _el$9 = _$createTextNode(` · `), _el$0 = _$createTextNode(` `), _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$11 = _$createElement("text"), _el$13 = _$createElement("text"), _el$14 = _$createTextNode(` / `), _el$15 = _$createTextNode(` (`), _el$16 = _$createTextNode(`)`), _el$17 = _$createElement("box"), _el$18 = _$createElement("text"), _el$19 = _$createElement("text"), _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` · `), _el$23 = _$createTextNode(` `), _el$
|
|
853
|
+
var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createTextNode(` `), _el$4 = _$createElement("box"), _el$5 = _$createElement("text"), _el$6 = _$createElement("text"), _el$8 = _$createElement("text"), _el$9 = _$createTextNode(` · `), _el$0 = _$createTextNode(` `), _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$11 = _$createElement("text"), _el$13 = _$createElement("text"), _el$14 = _$createTextNode(` / `), _el$15 = _$createTextNode(` (`), _el$16 = _$createTextNode(`)`), _el$17 = _$createElement("box"), _el$18 = _$createElement("text"), _el$19 = _$createElement("text"), _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` · `), _el$23 = _$createTextNode(` `), _el$33 = _$createElement("box"), _el$34 = _$createElement("text"), _el$35 = _$createElement("text"), _el$37 = _$createElement("text"), _el$38 = _$createTextNode(`↓ `), _el$39 = _$createTextNode(` ↑ `);
|
|
531
854
|
_$insertNode(_el$, _el$2);
|
|
532
855
|
_$insertNode(_el$, _el$4);
|
|
533
856
|
_$insertNode(_el$, _el$1);
|
|
534
857
|
_$insertNode(_el$, _el$17);
|
|
535
|
-
_$insertNode(_el$, _el$24);
|
|
536
858
|
_$insertNode(_el$, _el$33);
|
|
537
859
|
_$setProp(_el$, "flexDirection", "column");
|
|
538
860
|
_$setProp(_el$, "paddingLeft", 0);
|
|
@@ -574,20 +896,40 @@ function MetricsPanel(props) {
|
|
|
574
896
|
_$insert(_el$21, () => formatPercent(metricsState.sharedMetrics().gpuPercent), _el$22);
|
|
575
897
|
_$insert(_el$21, () => icons.thermometer, _el$23);
|
|
576
898
|
_$insert(_el$21, () => formatTemperature(metricsState.sharedMetrics().gpuTemperatureCelsius), null);
|
|
577
|
-
_$
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
899
|
+
_$insert(_el$, _$createComponent(Show, {
|
|
900
|
+
get when() {
|
|
901
|
+
return metricsState.sharedMetrics().gpuMemoryIsUnified !== true;
|
|
902
|
+
},
|
|
903
|
+
get children() {
|
|
904
|
+
var _el$24 = _$createElement("box"), _el$25 = _$createElement("text"), _el$26 = _$createElement("text"), _el$27 = _$createTextNode(` `), _el$28 = _$createTextNode(` `), _el$29 = _$createElement("text"), _el$30 = _$createTextNode(` / `), _el$31 = _$createTextNode(` (`), _el$32 = _$createTextNode(`)`);
|
|
905
|
+
_$insertNode(_el$24, _el$25);
|
|
906
|
+
_$insertNode(_el$24, _el$26);
|
|
907
|
+
_$insertNode(_el$24, _el$29);
|
|
908
|
+
_$setProp(_el$24, "flexDirection", "row");
|
|
909
|
+
_$insert(_el$25, () => gpuMemory().icon);
|
|
910
|
+
_$insertNode(_el$26, _el$27);
|
|
911
|
+
_$insertNode(_el$26, _el$28);
|
|
912
|
+
_$insert(_el$26, () => gpuMemory().label, _el$28);
|
|
913
|
+
_$insertNode(_el$29, _el$30);
|
|
914
|
+
_$insertNode(_el$29, _el$31);
|
|
915
|
+
_$insertNode(_el$29, _el$32);
|
|
916
|
+
_$insert(_el$29, () => formatGiB(gpuMemory().usedBytes), _el$30);
|
|
917
|
+
_$insert(_el$29, () => formatGiB(gpuMemory().totalBytes), _el$31);
|
|
918
|
+
_$insert(_el$29, () => formatPercent(gpuMemory().percent), _el$32);
|
|
919
|
+
_$effect((_p$) => {
|
|
920
|
+
var _v$ = props.theme.success, _v$2 = props.theme.text, _v$3 = props.theme.textMuted;
|
|
921
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$25, "fg", _v$, _p$.e));
|
|
922
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$26, "fg", _v$2, _p$.t));
|
|
923
|
+
_v$3 !== _p$.a && (_p$.a = _$setProp(_el$29, "fg", _v$3, _p$.a));
|
|
924
|
+
return _p$;
|
|
925
|
+
}, {
|
|
926
|
+
e: undefined,
|
|
927
|
+
t: undefined,
|
|
928
|
+
a: undefined
|
|
929
|
+
});
|
|
930
|
+
return _el$24;
|
|
931
|
+
}
|
|
932
|
+
}), _el$33);
|
|
591
933
|
_$insertNode(_el$33, _el$34);
|
|
592
934
|
_$insertNode(_el$33, _el$35);
|
|
593
935
|
_$insertNode(_el$33, _el$37);
|
|
@@ -599,24 +941,21 @@ function MetricsPanel(props) {
|
|
|
599
941
|
_$insert(_el$37, () => formatRate(metricsState.sharedMetrics().downloadBytesPerSecond), _el$39);
|
|
600
942
|
_$insert(_el$37, () => formatRate(metricsState.sharedMetrics().uploadBytesPerSecond), null);
|
|
601
943
|
_$effect((_p$) => {
|
|
602
|
-
var _v$ = props.theme.text, _v$
|
|
603
|
-
_v$ !== _p$.e && (_p$.e = _$setProp(_el$2, "fg", _v
|
|
604
|
-
_v$
|
|
605
|
-
_v$
|
|
606
|
-
_v$
|
|
607
|
-
_v$
|
|
608
|
-
_v$
|
|
609
|
-
_v$
|
|
610
|
-
_v$
|
|
611
|
-
_v$
|
|
612
|
-
_v$
|
|
613
|
-
_v$
|
|
614
|
-
_v$
|
|
615
|
-
_v$
|
|
616
|
-
_v$
|
|
617
|
-
_v$13 !== _p$.m && (_p$.m = _$setProp(_el$34, "fg", _v$13, _p$.m));
|
|
618
|
-
_v$14 !== _p$.f && (_p$.f = _$setProp(_el$35, "fg", _v$14, _p$.f));
|
|
619
|
-
_v$15 !== _p$.y && (_p$.y = _$setProp(_el$37, "fg", _v$15, _p$.y));
|
|
944
|
+
var _v$4 = props.theme.text, _v$5 = TextAttributes.BOLD, _v$6 = props.theme.success, _v$7 = props.theme.text, _v$8 = props.theme.textMuted, _v$9 = props.theme.success, _v$0 = props.theme.text, _v$1 = props.theme.textMuted, _v$10 = props.theme.success, _v$11 = props.theme.text, _v$12 = props.theme.textMuted, _v$13 = props.theme.success, _v$14 = props.theme.text, _v$15 = props.theme.textMuted;
|
|
945
|
+
_v$4 !== _p$.e && (_p$.e = _$setProp(_el$2, "fg", _v$4, _p$.e));
|
|
946
|
+
_v$5 !== _p$.t && (_p$.t = _$setProp(_el$2, "attributes", _v$5, _p$.t));
|
|
947
|
+
_v$6 !== _p$.a && (_p$.a = _$setProp(_el$5, "fg", _v$6, _p$.a));
|
|
948
|
+
_v$7 !== _p$.o && (_p$.o = _$setProp(_el$6, "fg", _v$7, _p$.o));
|
|
949
|
+
_v$8 !== _p$.i && (_p$.i = _$setProp(_el$8, "fg", _v$8, _p$.i));
|
|
950
|
+
_v$9 !== _p$.n && (_p$.n = _$setProp(_el$10, "fg", _v$9, _p$.n));
|
|
951
|
+
_v$0 !== _p$.s && (_p$.s = _$setProp(_el$11, "fg", _v$0, _p$.s));
|
|
952
|
+
_v$1 !== _p$.h && (_p$.h = _$setProp(_el$13, "fg", _v$1, _p$.h));
|
|
953
|
+
_v$10 !== _p$.r && (_p$.r = _$setProp(_el$18, "fg", _v$10, _p$.r));
|
|
954
|
+
_v$11 !== _p$.d && (_p$.d = _$setProp(_el$19, "fg", _v$11, _p$.d));
|
|
955
|
+
_v$12 !== _p$.l && (_p$.l = _$setProp(_el$21, "fg", _v$12, _p$.l));
|
|
956
|
+
_v$13 !== _p$.u && (_p$.u = _$setProp(_el$34, "fg", _v$13, _p$.u));
|
|
957
|
+
_v$14 !== _p$.c && (_p$.c = _$setProp(_el$35, "fg", _v$14, _p$.c));
|
|
958
|
+
_v$15 !== _p$.w && (_p$.w = _$setProp(_el$37, "fg", _v$15, _p$.w));
|
|
620
959
|
return _p$;
|
|
621
960
|
}, {
|
|
622
961
|
e: undefined,
|
|
@@ -632,10 +971,7 @@ function MetricsPanel(props) {
|
|
|
632
971
|
l: undefined,
|
|
633
972
|
u: undefined,
|
|
634
973
|
c: undefined,
|
|
635
|
-
w: undefined
|
|
636
|
-
m: undefined,
|
|
637
|
-
f: undefined,
|
|
638
|
-
y: undefined
|
|
974
|
+
w: undefined
|
|
639
975
|
});
|
|
640
976
|
return _el$;
|
|
641
977
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-system-metrics-tui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "OpenCode TUI sidebar plugin for live system metrics",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@opentui/core": "^0.5.10",
|
|
35
35
|
"@opentui/solid": "^0.5.10",
|
|
36
|
-
"
|
|
36
|
+
"koffi": "^3.2.1",
|
|
37
37
|
"solid-js": "^1.9.12",
|
|
38
38
|
"systeminformation": "^5.33.8"
|
|
39
39
|
},
|
package/scripts/build.ts
CHANGED
|
@@ -6,7 +6,7 @@ const result = await Bun.build({
|
|
|
6
6
|
target: "node",
|
|
7
7
|
format: "esm",
|
|
8
8
|
plugins: [solidPlugin],
|
|
9
|
-
external: ["@opencode-ai/plugin/tui", "@opentui/core", "@opentui/solid", "solid-js", "systeminformation"],
|
|
9
|
+
external: ["@opencode-ai/plugin/tui", "@opentui/core", "@opentui/solid", "koffi", "solid-js", "systeminformation"],
|
|
10
10
|
naming: "[name].js",
|
|
11
11
|
})
|
|
12
12
|
|