@axium/sysadmin 0.1.0 → 0.1.2

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.
@@ -1,3 +1,4 @@
1
+ import { execFileSync } from 'node:child_process';
1
2
  import * as fs from 'node:fs';
2
3
  import * as os from 'node:os';
3
4
  /** Read a sysfs/procfs file, returning the trimmed contents or undefined if unreadable. */
@@ -136,19 +137,30 @@ function memory() {
136
137
  function networkInterfaces() {
137
138
  const result = [];
138
139
  for (const name of list('/sys/class/net')) {
139
- if (name === 'lo')
140
+ if (name === 'lo' || name.startsWith('veth') || name.startsWith('docker') || name.startsWith('br-'))
140
141
  continue;
141
142
  const operstate = read(`/sys/class/net/${name}/operstate`);
142
143
  const connected = operstate === 'up' || read(`/sys/class/net/${name}/carrier`) === '1';
143
- // `speed` is in Mbit/s; only valid for connected links and may be -1/unreadable.
144
- const raw = read(`/sys/class/net/${name}/speed`);
145
- const speed = connected && raw && Number(raw) > 0 ? BigInt(raw) : undefined;
146
- // Wi-Fi devices expose a `wireless`/`phy80211` subdirectory.
144
+ const raw = Number(read(`/sys/class/net/${name}/speed`));
145
+ let speed = connected && Number.isSafeInteger(raw) && raw > 0 ? raw : undefined;
147
146
  const wireless = fs.existsSync(`/sys/class/net/${name}/wireless`) || fs.existsSync(`/sys/class/net/${name}/phy80211`);
147
+ // Wi-Fi doesn't expose sysfs `speed`; the SSID and link bitrate come from `iw` instead.
148
+ let connection;
149
+ if (wireless && connected)
150
+ try {
151
+ const link = execFileSync('iw', ['dev', name, 'link'], { encoding: 'utf8' });
152
+ connection = /^\s*SSID:\s*(.+)$/m.exec(link)?.[1].trim() || undefined;
153
+ const bitrate = /^\s*tx bitrate:\s*([\d.]+)\s*MBit\/s/m.exec(link)?.[1];
154
+ if (bitrate)
155
+ speed = Math.round(Number(bitrate));
156
+ }
157
+ catch {
158
+ // that's okay
159
+ }
148
160
  const uevent = read(`/sys/class/net/${name}/device/uevent`);
149
161
  const id = uevent && /^PCI_ID=(.+)$/m.exec(uevent)?.[1];
150
162
  const model = id ? pciName(id) : name;
151
- result.push({ name, model, connected, wireless, speed });
163
+ result.push({ name, model, connected, wireless, connection, speed });
152
164
  }
153
165
  return result;
154
166
  }
package/dist/common.d.ts CHANGED
@@ -297,7 +297,7 @@ declare const SysadminClientToServer: {
297
297
  connected: z.ZodBoolean;
298
298
  wireless: z.ZodBoolean;
299
299
  connection: z.ZodOptional<z.ZodString>;
300
- speed: z.ZodOptional<z.ZodCoercedBigInt<unknown>>;
300
+ speed: z.ZodOptional<z.ZodNumber>;
301
301
  }, z.core.$strip>>;
302
302
  arch: z.ZodString;
303
303
  machine: z.ZodString;
@@ -346,7 +346,7 @@ declare const SysadminServerToClient: {
346
346
  connected: z.ZodBoolean;
347
347
  wireless: z.ZodBoolean;
348
348
  connection: z.ZodOptional<z.ZodString>;
349
- speed: z.ZodOptional<z.ZodCoercedBigInt<unknown>>;
349
+ speed: z.ZodOptional<z.ZodNumber>;
350
350
  }, z.core.$strip>>;
351
351
  arch: z.ZodString;
352
352
  machine: z.ZodString;
package/dist/info.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare const NetworkInterface: z.ZodObject<{
44
44
  connected: z.ZodBoolean;
45
45
  wireless: z.ZodBoolean;
46
46
  connection: z.ZodOptional<z.ZodString>;
47
- speed: z.ZodOptional<z.ZodCoercedBigInt<unknown>>;
47
+ speed: z.ZodOptional<z.ZodNumber>;
48
48
  }, z.core.$strip>;
49
49
  export interface NetworkInterface extends z.infer<typeof NetworkInterface> {
50
50
  }
@@ -80,7 +80,7 @@ export declare const SystemInfo: z.ZodObject<{
80
80
  connected: z.ZodBoolean;
81
81
  wireless: z.ZodBoolean;
82
82
  connection: z.ZodOptional<z.ZodString>;
83
- speed: z.ZodOptional<z.ZodCoercedBigInt<unknown>>;
83
+ speed: z.ZodOptional<z.ZodNumber>;
84
84
  }, z.core.$strip>>;
85
85
  arch: z.ZodString;
86
86
  machine: z.ZodString;
package/dist/info.js CHANGED
@@ -30,7 +30,7 @@ export const NetworkInterface = z.object({
30
30
  connected: z.boolean(),
31
31
  wireless: z.boolean(),
32
32
  connection: z.string().optional(),
33
- speed: z.coerce.bigint().optional(),
33
+ speed: z.number().nonnegative().optional(),
34
34
  });
35
35
  export const SystemInfo = z.object({
36
36
  cpus: CPU.array(),
package/locales/en.json CHANGED
@@ -67,6 +67,7 @@
67
67
  "wired": "Wired",
68
68
  "connected": "Connected",
69
69
  "disconnected": "Disconnected",
70
+ "connected_to": "to {connection}",
70
71
  "os": "Operating System",
71
72
  "platform": "Platform",
72
73
  "release": "Release",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/sysadmin",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "description": "System administration for Axium",
6
6
  "funding": {
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@axium/client": ">=0.27.0",
40
- "@axium/core": ">=0.23.0",
40
+ "@axium/core": ">=0.33.0",
41
41
  "@axium/server": ">=0.47.0",
42
42
  "@sveltejs/kit": "^2.27.3",
43
43
  "kysely": "^0.29.0",
@@ -23,6 +23,12 @@
23
23
  return text('sysadmin.system.usage', { used: formatBytes(used), total: formatBytes(total) });
24
24
  }
25
25
 
26
+ function speedText(speed: number): string {
27
+ if (speed < 1000) return `${speed} MBit/s`;
28
+ const gb = speed / 1000;
29
+ return `${gb % 1 === 0 ? gb : gb.toFixed(1)} GBit/s`;
30
+ }
31
+
26
32
  const socket = await connect();
27
33
 
28
34
  async function loadInfo() {
@@ -147,9 +153,12 @@
147
153
  <span class="subtle">{iface.model}</span>
148
154
  <span class={['net-status', iface.connected ? 'online' : 'offline']}>
149
155
  {iface.connected ? text('sysadmin.system.connected') : text('sysadmin.system.disconnected')}
156
+ {#if iface.connection}
157
+ <span class="subtle">{text('sysadmin.system.connected_to', { connection: iface.connection })}</span>
158
+ {/if}
150
159
  </span>
151
160
  {#if iface.speed}
152
- <span>{iface.speed} Mbit/s</span>
161
+ <span class="subtle">{speedText(iface.speed)}</span>
153
162
  {/if}
154
163
  </div>
155
164
  {/each}