about-system 0.9.31 → 0.10.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.
- package/README.md +16 -0
- package/dist/about-system-cli.js +221 -127
- package/dist/about-system-cli.js.map +1 -1
- package/dist/web/assets/index--HOsj3jw.js +14 -0
- package/dist/web/assets/index-BU_blZyu.css +1 -0
- package/dist/web/index.html +13 -0
- package/package.json +20 -2
- package/src/about-system-cli.ts +36 -0
- package/src/web-server.test.ts +69 -0
- package/src/web-server.ts +120 -0
package/README.md
CHANGED
|
@@ -121,6 +121,22 @@ about-system --json
|
|
|
121
121
|
about-system --help
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Web Dashboard
|
|
125
|
+
|
|
126
|
+
`about-system web` starts a small HTTP server with a React + [shadcn/ui](https://ui.shadcn.com) dashboard: usage bars for memory and disk, every info block grouped into cards, a filter box, light/dark mode, and auto-refresh every 10 seconds.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
about-system web # http://127.0.0.1:3777
|
|
130
|
+
about-system web --port 8080 # or PORT=8080 about-system web
|
|
131
|
+
about-system web --host 0.0.0.0 --port 8080 # reachable from other machines
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
It listens on localhost by default. Pass `--host 0.0.0.0` to reach it on a remote server, but note that anyone who can reach the port sees the public IP, open ports, and running processes — prefer an SSH tunnel (`ssh -L 3777:localhost:3777 server`) or put it behind auth.
|
|
135
|
+
|
|
136
|
+
The page reads `GET /api/info`, which returns the same object as `about-system --json`.
|
|
137
|
+
|
|
138
|
+
To work on the dashboard itself (`web/`), run `about-system web` in one terminal and `bun run dev:web` in another — the Vite dev server proxies `/api` to port 3777.
|
|
139
|
+
|
|
124
140
|
### Installation as Shell Greeting
|
|
125
141
|
|
|
126
142
|
```bash
|
package/dist/about-system-cli.js
CHANGED
|
@@ -1,10 +1,84 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import { g as
|
|
7
|
-
|
|
2
|
+
import S from "os";
|
|
3
|
+
import c from "fs";
|
|
4
|
+
import u from "path";
|
|
5
|
+
import { fileURLToPath as O } from "url";
|
|
6
|
+
import { g as $, a as T, b, C as j, S as k, c as g, d as B, D as I } from "./system-info-api-C_y-O4VW.js";
|
|
7
|
+
import A from "http";
|
|
8
|
+
const C = 3777, _ = "127.0.0.1", w = {
|
|
9
|
+
".html": "text/html; charset=utf-8",
|
|
10
|
+
".js": "text/javascript; charset=utf-8",
|
|
11
|
+
".css": "text/css; charset=utf-8",
|
|
12
|
+
".json": "application/json; charset=utf-8",
|
|
13
|
+
".svg": "image/svg+xml",
|
|
14
|
+
".png": "image/png",
|
|
15
|
+
".ico": "image/x-icon",
|
|
16
|
+
".woff2": "font/woff2"
|
|
17
|
+
};
|
|
18
|
+
function D() {
|
|
19
|
+
return u.join(u.dirname(O(import.meta.url)), "web");
|
|
20
|
+
}
|
|
21
|
+
function U(e, o) {
|
|
22
|
+
let t;
|
|
23
|
+
try {
|
|
24
|
+
t = decodeURIComponent(o.split("?")[0]);
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const s = u.resolve(e), n = u.resolve(s, "." + u.posix.normalize("/" + t));
|
|
29
|
+
if (n !== s && !n.startsWith(s + u.sep)) return null;
|
|
30
|
+
if (c.existsSync(n) && c.statSync(n).isFile()) return n;
|
|
31
|
+
const l = u.join(s, "index.html");
|
|
32
|
+
return c.existsSync(l) ? l : null;
|
|
33
|
+
}
|
|
34
|
+
function p(e, o, t, s) {
|
|
35
|
+
e.writeHead(o, {
|
|
36
|
+
"Content-Type": t,
|
|
37
|
+
"Cache-Control": "no-store",
|
|
38
|
+
"X-Content-Type-Options": "nosniff"
|
|
39
|
+
}), e.end(s);
|
|
40
|
+
}
|
|
41
|
+
function R(e = {}) {
|
|
42
|
+
const o = e.webRoot ?? D();
|
|
43
|
+
return A.createServer(async (t, s) => {
|
|
44
|
+
const n = t.url ?? "/";
|
|
45
|
+
if (t.method !== "GET" && t.method !== "HEAD") {
|
|
46
|
+
p(s, 405, "text/plain; charset=utf-8", "Method Not Allowed");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (n === "/api/info" || n.startsWith("/api/info?")) {
|
|
50
|
+
try {
|
|
51
|
+
const r = await $();
|
|
52
|
+
p(s, 200, w[".json"], JSON.stringify(r));
|
|
53
|
+
} catch (r) {
|
|
54
|
+
p(s, 500, w[".json"], JSON.stringify({ error: r.message }));
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const l = U(o, n);
|
|
59
|
+
if (!l) {
|
|
60
|
+
p(
|
|
61
|
+
s,
|
|
62
|
+
404,
|
|
63
|
+
"text/plain; charset=utf-8",
|
|
64
|
+
"Dashboard not found. Build it with: npm run build:web"
|
|
65
|
+
);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const i = w[u.extname(l)] ?? "application/octet-stream";
|
|
69
|
+
p(s, 200, i, c.readFileSync(l));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function H(e = {}) {
|
|
73
|
+
const o = e.port ?? C, t = e.host ?? _, s = R(e);
|
|
74
|
+
return new Promise((n, l) => {
|
|
75
|
+
s.once("error", l), s.listen(o, t, () => {
|
|
76
|
+
const i = s.address(), r = typeof i == "object" && i ? i.port : o;
|
|
77
|
+
n({ server: s, url: `http://${t === "0.0.0.0" || t === "::" ? "localhost" : t}:${r}` });
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const z = O(import.meta.url), y = S.platform() === "win32", J = {
|
|
8
82
|
31: [41, 97],
|
|
9
83
|
// red
|
|
10
84
|
32: [42, 30],
|
|
@@ -18,158 +92,158 @@ const x = P(import.meta.url), g = y.platform() === "win32", T = {
|
|
|
18
92
|
36: [46, 30]
|
|
19
93
|
// cyan
|
|
20
94
|
};
|
|
21
|
-
function
|
|
22
|
-
if (!
|
|
23
|
-
const
|
|
24
|
-
if (e === "battery" &&
|
|
25
|
-
const
|
|
26
|
-
return `${
|
|
95
|
+
function E(e, o, t) {
|
|
96
|
+
if (!o || o.trim() === "") return "";
|
|
97
|
+
const s = t.display.show_backgrounds !== !1, n = t.colors[e], l = B[n], i = s && l || g[n] || g.reset, r = s ? g.reset : "", a = t.display.show_emojis && t.emojis[e] || "";
|
|
98
|
+
if (e === "battery" && t.display.show_emojis) {
|
|
99
|
+
const f = o.includes("+") ? t.emojis.battery_charging : t.emojis.battery;
|
|
100
|
+
return `${i}${f}${o}${r}`;
|
|
27
101
|
}
|
|
28
|
-
if (e === "ports" &&
|
|
29
|
-
let
|
|
30
|
-
const
|
|
31
|
-
return
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
const [
|
|
35
|
-
|
|
102
|
+
if (e === "ports" && t.colors[e] === "multicolor" && o) {
|
|
103
|
+
let h = ` ${t.display.show_emojis ? t.emojis.ports : ""}`;
|
|
104
|
+
const d = o.split(" "), m = [31, 32, 33, 34, 35, 36];
|
|
105
|
+
return d.forEach((v, L) => {
|
|
106
|
+
const x = m[L % m.length];
|
|
107
|
+
if (s) {
|
|
108
|
+
const [W, N] = J[x];
|
|
109
|
+
h += `\x1B[${W}m\x1B[${N}m${v}\x1B[0m `;
|
|
36
110
|
} else
|
|
37
|
-
|
|
38
|
-
}),
|
|
111
|
+
h += `\x1B[${x}m${v}\x1B[0m `;
|
|
112
|
+
}), h.trim();
|
|
39
113
|
}
|
|
40
|
-
if (e === "pacman" &&
|
|
41
|
-
const
|
|
42
|
-
return `${
|
|
114
|
+
if (e === "pacman" && t.colors[e] === "multicolor" && o) {
|
|
115
|
+
const f = t.display.show_emojis ? t.emojis.pacman : "";
|
|
116
|
+
return `${i}${f}${o}${r}`;
|
|
43
117
|
}
|
|
44
|
-
return `${
|
|
118
|
+
return `${i}${a}${o}${r}`;
|
|
45
119
|
}
|
|
46
|
-
function
|
|
47
|
-
if (
|
|
48
|
-
const
|
|
49
|
-
let
|
|
50
|
-
for (let
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
let
|
|
54
|
-
for (;
|
|
55
|
-
|
|
56
|
-
|
|
120
|
+
function G(e, o) {
|
|
121
|
+
if (o <= 0) return e ? [e] : [];
|
|
122
|
+
const t = [];
|
|
123
|
+
let s = "", n = 0, l = "";
|
|
124
|
+
for (let i = 0; i < e.length; i++) {
|
|
125
|
+
const r = e[i];
|
|
126
|
+
if (r === "\x1B" && e[i + 1] === "[") {
|
|
127
|
+
let d = r, m = i + 1;
|
|
128
|
+
for (; m < e.length && e[m] !== "m"; )
|
|
129
|
+
d += e[m], m++;
|
|
130
|
+
m < e.length && (d += e[m]), s += d, l = /\x1b\[0m/.test(d) ? "" : d, i = m;
|
|
57
131
|
continue;
|
|
58
132
|
}
|
|
59
|
-
let
|
|
60
|
-
const
|
|
61
|
-
|
|
133
|
+
let a = r, f = 1;
|
|
134
|
+
const h = e.charCodeAt(i);
|
|
135
|
+
h >= 55296 && h <= 56319 && i + 1 < e.length && (a = r + e[i + 1], f = 2, i++), n >= o && (t.push(s), s = l, n = 0), s += a, n += f;
|
|
62
136
|
}
|
|
63
|
-
return
|
|
137
|
+
return s && t.push(s), t;
|
|
64
138
|
}
|
|
65
|
-
async function
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
const
|
|
69
|
-
for (const
|
|
70
|
-
for (const
|
|
71
|
-
const
|
|
72
|
-
|
|
139
|
+
async function F(e = null) {
|
|
140
|
+
const o = T(), t = e || o.display_order, s = await $();
|
|
141
|
+
if (o.display.single_line) {
|
|
142
|
+
const r = [];
|
|
143
|
+
for (const a of t)
|
|
144
|
+
for (const f of a) {
|
|
145
|
+
const h = s[f], d = E(f, h, o);
|
|
146
|
+
d && d.trim() && r.push(d);
|
|
73
147
|
}
|
|
74
|
-
|
|
148
|
+
r.length > 0 && console.log(r.join(" ") + g.reset);
|
|
75
149
|
return;
|
|
76
150
|
}
|
|
77
|
-
const n =
|
|
78
|
-
for (const
|
|
79
|
-
for (const
|
|
80
|
-
const
|
|
81
|
-
|
|
151
|
+
const n = o.display.line_wrap_length, l = [];
|
|
152
|
+
for (const r of t)
|
|
153
|
+
for (const a of r) {
|
|
154
|
+
const f = s[a], h = E(a, f, o);
|
|
155
|
+
h && h.trim() && l.push(h);
|
|
82
156
|
}
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
console.log(
|
|
86
|
-
}) :
|
|
157
|
+
const i = G(l.join(" "), n);
|
|
158
|
+
i.length > 0 ? i.forEach((r) => {
|
|
159
|
+
console.log(r + g.reset);
|
|
160
|
+
}) : o.advanced.debug && console.log("No system information could be displayed");
|
|
87
161
|
}
|
|
88
|
-
function
|
|
89
|
-
const
|
|
162
|
+
function M(e) {
|
|
163
|
+
const o = T();
|
|
90
164
|
if (e.includes("--settings-init"))
|
|
91
|
-
return
|
|
165
|
+
return b(I) ? console.log("Settings initialized with defaults") : console.log("Failed to initialize settings"), !0;
|
|
92
166
|
if (e.includes("--settings-show"))
|
|
93
|
-
return console.log("Current settings:"), console.log(JSON.stringify(
|
|
167
|
+
return console.log("Current settings:"), console.log(JSON.stringify(o, null, 2)), !0;
|
|
94
168
|
if (e.includes("--settings-reset"))
|
|
95
|
-
return
|
|
169
|
+
return b(I) ? console.log("Settings reset to defaults") : console.log("Failed to reset settings"), !0;
|
|
96
170
|
if (e.includes("--refresh")) {
|
|
97
171
|
try {
|
|
98
|
-
|
|
99
|
-
} catch (
|
|
100
|
-
console.error("Error clearing cache:",
|
|
172
|
+
c.existsSync(j) && (c.unlinkSync(j), console.log("Cache cleared"));
|
|
173
|
+
} catch (s) {
|
|
174
|
+
console.error("Error clearing cache:", s.message);
|
|
101
175
|
}
|
|
102
176
|
return !0;
|
|
103
177
|
}
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
const
|
|
178
|
+
const t = e.indexOf("--set");
|
|
179
|
+
if (t !== -1 && e[t + 1] && e[t + 2]) {
|
|
180
|
+
const s = e[t + 1], n = e[t + 2];
|
|
107
181
|
try {
|
|
108
|
-
const
|
|
109
|
-
let
|
|
110
|
-
for (let
|
|
111
|
-
i[
|
|
112
|
-
i[
|
|
113
|
-
} catch (
|
|
114
|
-
console.error("Error setting value:",
|
|
182
|
+
const l = n.startsWith("{") || n.startsWith("[") ? JSON.parse(n) : n === "true" ? !0 : n === "false" ? !1 : n, i = s.split(".");
|
|
183
|
+
let r = o;
|
|
184
|
+
for (let a = 0; a < i.length - 1; a++)
|
|
185
|
+
r[i[a]] || (r[i[a]] = {}), r = r[i[a]];
|
|
186
|
+
r[i[i.length - 1]] = l, b(o) ? console.log(`Setting ${s} = ${n}`) : console.log("Failed to save settings");
|
|
187
|
+
} catch (l) {
|
|
188
|
+
console.error("Error setting value:", l.message);
|
|
115
189
|
}
|
|
116
190
|
return !0;
|
|
117
191
|
}
|
|
118
192
|
return !1;
|
|
119
193
|
}
|
|
120
|
-
function
|
|
121
|
-
const e =
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
const
|
|
194
|
+
function V() {
|
|
195
|
+
const e = S.homedir();
|
|
196
|
+
let o, t;
|
|
197
|
+
y ? (o = u.join(e, "AppData", "Local"), t = u.join(o, "systeminfo")) : (o = u.join(e, ".config"), t = u.join(o, "systeminfo"));
|
|
198
|
+
const s = u.resolve(z);
|
|
125
199
|
try {
|
|
126
|
-
if (
|
|
127
|
-
console.log("Windows installation:"), console.log("1. Script copied to:",
|
|
200
|
+
if (c.existsSync(o) || c.mkdirSync(o, { recursive: !0 }), c.copyFileSync(s, t), y || c.chmodSync(t, "755"), y) {
|
|
201
|
+
console.log("Windows installation:"), console.log("1. Script copied to:", t), console.log("2. To add to PowerShell profile, run:"), console.log(` Add-Content $PROFILE "node '${t}'"`), console.log(
|
|
128
202
|
"3. To add to Command Prompt, create a batch file in your startup folder"
|
|
129
203
|
);
|
|
130
|
-
const n =
|
|
131
|
-
|
|
132
|
-
node "${
|
|
204
|
+
const n = u.join(o, "systeminfo-startup.bat");
|
|
205
|
+
c.writeFileSync(n, `@echo off
|
|
206
|
+
node "${t}"
|
|
133
207
|
`), console.log("4. Batch file created:", n);
|
|
134
208
|
} else {
|
|
135
209
|
try {
|
|
136
|
-
const
|
|
137
|
-
|
|
210
|
+
const f = u.join(e, ".hushlogin");
|
|
211
|
+
c.writeFileSync(f, "");
|
|
138
212
|
} catch {
|
|
139
213
|
}
|
|
140
|
-
const n =
|
|
141
|
-
|
|
142
|
-
${
|
|
143
|
-
`) :
|
|
214
|
+
const n = u.join(e, ".bashrc"), l = `node ${t}`;
|
|
215
|
+
c.existsSync(n) ? c.readFileSync(n, "utf8").includes("systeminfo") || c.appendFileSync(n, `
|
|
216
|
+
${l}
|
|
217
|
+
`) : c.writeFileSync(n, `${l}
|
|
144
218
|
`);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
${
|
|
219
|
+
const i = u.join(e, ".zshrc");
|
|
220
|
+
c.existsSync(i) && (c.readFileSync(i, "utf8").includes("systeminfo") || c.appendFileSync(i, `
|
|
221
|
+
${l}
|
|
148
222
|
`));
|
|
149
|
-
const
|
|
223
|
+
const r = u.join(
|
|
150
224
|
e,
|
|
151
225
|
".config",
|
|
152
226
|
"fish",
|
|
153
227
|
"config.fish"
|
|
154
228
|
);
|
|
155
|
-
|
|
156
|
-
|
|
229
|
+
c.existsSync(r) && (c.readFileSync(r, "utf8").includes("systeminfo") || c.appendFileSync(
|
|
230
|
+
r,
|
|
157
231
|
`
|
|
158
232
|
set -U fish_greeting ""
|
|
159
|
-
${
|
|
233
|
+
${l}
|
|
160
234
|
`
|
|
161
235
|
));
|
|
162
|
-
const
|
|
236
|
+
const a = u.join(
|
|
163
237
|
e,
|
|
164
238
|
".config",
|
|
165
239
|
"nushell",
|
|
166
240
|
"config.nu"
|
|
167
241
|
);
|
|
168
|
-
|
|
169
|
-
|
|
242
|
+
c.existsSync(a) && (c.readFileSync(a, "utf8").includes("systeminfo") || c.appendFileSync(
|
|
243
|
+
a,
|
|
170
244
|
`
|
|
171
245
|
$env.config.show_banner = false
|
|
172
|
-
${
|
|
246
|
+
${l}
|
|
173
247
|
`
|
|
174
248
|
));
|
|
175
249
|
}
|
|
@@ -178,22 +252,36 @@ ${f}
|
|
|
178
252
|
console.error("Error installing shell greeting:", n.message), process.exit(1);
|
|
179
253
|
}
|
|
180
254
|
}
|
|
181
|
-
function
|
|
182
|
-
for (
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
255
|
+
function P(e, o) {
|
|
256
|
+
for (let t = 0; t < e.length; t++) {
|
|
257
|
+
if (e[t] === o) return e[t + 1];
|
|
258
|
+
if (e[t].startsWith(o + "=")) return e[t].slice(o.length + 1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function X(e) {
|
|
262
|
+
const o = P(e, "--port") ?? process.env.PORT, t = o === void 0 ? C : Number(o);
|
|
263
|
+
if (!Number.isInteger(t) || t < 0 || t > 65535)
|
|
264
|
+
throw new Error(`Invalid port: ${o}`);
|
|
265
|
+
const s = P(e, "--host") ?? _, { url: n } = await H({ port: t, host: s });
|
|
266
|
+
console.log(`About System web UI running at ${n}`), console.log(s === _ ? "Listening on localhost only. Use --host 0.0.0.0 to expose it on the network." : "Warning: system details (public IP, ports, processes) are visible to anyone who can reach this port."), console.log("Press Ctrl+C to stop.");
|
|
267
|
+
}
|
|
268
|
+
function Y(e) {
|
|
269
|
+
for (const o of e)
|
|
270
|
+
if (!o.startsWith("--") && o.includes(","))
|
|
271
|
+
return o.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
272
|
+
for (const o of e)
|
|
273
|
+
if (!o.startsWith("--") && !o.includes("=") && o.length > 0)
|
|
274
|
+
return [o.trim()];
|
|
188
275
|
return null;
|
|
189
276
|
}
|
|
190
|
-
async function
|
|
277
|
+
async function K() {
|
|
191
278
|
console.log(`
|
|
192
279
|
System Info Script - TypeScript Version
|
|
193
280
|
|
|
194
281
|
Usage:
|
|
195
282
|
about-system [options]
|
|
196
283
|
about-system <part1,part2,...> # CLI mode: show specific parts only
|
|
284
|
+
about-system web [--port N] [--host H] # Serve the web dashboard
|
|
197
285
|
|
|
198
286
|
Options:
|
|
199
287
|
--help, -h Show this help message
|
|
@@ -217,11 +305,13 @@ Examples:
|
|
|
217
305
|
about-system --set emojis.cpu "🚀 "
|
|
218
306
|
about-system --set labels.cpu "Processor"
|
|
219
307
|
about-system --json
|
|
308
|
+
about-system web # http://127.0.0.1:${C}
|
|
309
|
+
about-system web --port 8080 --host 0.0.0.0
|
|
220
310
|
|
|
221
311
|
Settings file: ${k}
|
|
222
|
-
Cache file: ${
|
|
312
|
+
Cache file: ${j}
|
|
223
313
|
|
|
224
|
-
Platform: ${
|
|
314
|
+
Platform: ${y ? "Windows" : S.platform() === "darwin" ? "macOS" : S.platform() === "linux" ? "Linux" : "Unknown"}
|
|
225
315
|
|
|
226
316
|
Available display blocks:
|
|
227
317
|
Basic: user, hostname, uptime, shell, os, kernel, device
|
|
@@ -232,35 +322,39 @@ Available display blocks:
|
|
|
232
322
|
Tools: pacman, ports, containers
|
|
233
323
|
`);
|
|
234
324
|
}
|
|
235
|
-
async function
|
|
325
|
+
async function Q() {
|
|
236
326
|
const e = process.argv.slice(2);
|
|
237
|
-
if (
|
|
327
|
+
if (M(e))
|
|
238
328
|
return;
|
|
239
329
|
if (e.includes("--help") || e.includes("-h")) {
|
|
240
|
-
await
|
|
330
|
+
await K();
|
|
241
331
|
return;
|
|
242
332
|
}
|
|
243
333
|
if (e.includes("--install")) {
|
|
244
|
-
|
|
334
|
+
V();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (e[0] === "web" || e.includes("--web")) {
|
|
338
|
+
await X(e);
|
|
245
339
|
return;
|
|
246
340
|
}
|
|
247
341
|
if (e.includes("--json")) {
|
|
248
|
-
const
|
|
249
|
-
console.log(JSON.stringify(
|
|
342
|
+
const t = await $();
|
|
343
|
+
console.log(JSON.stringify(t, null, 2));
|
|
250
344
|
return;
|
|
251
345
|
}
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
254
|
-
await
|
|
346
|
+
const o = Y(e);
|
|
347
|
+
if (o) {
|
|
348
|
+
await F([o]);
|
|
255
349
|
return;
|
|
256
350
|
}
|
|
257
|
-
await
|
|
351
|
+
await F();
|
|
258
352
|
}
|
|
259
|
-
|
|
353
|
+
Q().catch((e) => {
|
|
260
354
|
console.error("Error:", e.message), process.exit(1);
|
|
261
355
|
});
|
|
262
356
|
export {
|
|
263
|
-
|
|
264
|
-
|
|
357
|
+
F as displaySystemInfo,
|
|
358
|
+
V as installShellGreeting
|
|
265
359
|
};
|
|
266
360
|
//# sourceMappingURL=about-system-cli.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"about-system-cli.js","sources":["../src/about-system-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport os from \"os\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getSystemInfo } from \"./system-info-api\";\nimport type { SystemInfo, SystemInfoOptions } from \"./systeminfo-types\";\n\nconst __filename = fileURLToPath(import.meta.url);\n\nimport {\n Settings,\n DEFAULT_SETTINGS,\n colors,\n backgrounds,\n SETTINGS_FILE,\n CACHE_FILE,\n loadSettings,\n saveSettings,\n} from \"./info/settings\";\n\n// Platform detection\nconst IS_WINDOWS = os.platform() === \"win32\";\n\n// Foreground code -> [background code, contrasting text code] for the\n// legacy 8-color multicolor rotation (ports/pacman rainbow mode).\nconst MULTICOLOR_BG: Record<number, [number, number]> = {\n 31: [41, 97], // red\n 32: [42, 30], // green\n 33: [43, 30], // yellow\n 34: [44, 97], // blue\n 35: [45, 97], // magenta\n 36: [46, 30], // cyan\n};\n\nfunction formatValue(key: string, value: string, settings: Settings): string {\n if (!value || value.trim() === \"\") return \"\";\n\n const showBackgrounds = settings.display.show_backgrounds !== false;\n const colorName = settings.colors[key] as keyof typeof colors;\n const bg = backgrounds[colorName as keyof typeof backgrounds];\n const style = (showBackgrounds && bg) || colors[colorName] || colors.reset;\n const suffix = showBackgrounds ? colors.reset : \"\";\n const emoji = settings.display.show_emojis ? settings.emojis[key] || \"\" : \"\";\n\n // Special handling for battery emoji\n if (key === \"battery\" && settings.display.show_emojis) {\n const batteryEmoji = value.includes(\"+\")\n ? settings.emojis.battery_charging\n : settings.emojis.battery;\n return `${style}${batteryEmoji}${value}${suffix}`;\n }\n\n // Multicolor handling for ports\n if (key === \"ports\" && settings.colors[key] === \"multicolor\" && value) {\n const emoji = settings.display.show_emojis ? settings.emojis.ports : \"\";\n let output = ` ${emoji}`;\n const ports = value.split(\" \");\n const colorCodes = [31, 32, 33, 34, 35, 36];\n ports.forEach((port, index) => {\n const colorCode = colorCodes[index % colorCodes.length];\n if (showBackgrounds) {\n const [bgCode, textCode] = MULTICOLOR_BG[colorCode];\n output += `\\x1b[${bgCode}m\\x1b[${textCode}m${port}\\x1b[0m `;\n } else {\n output += `\\x1b[${colorCode}m${port}\\x1b[0m `;\n }\n });\n return output.trim();\n }\n\n // Multicolor handling for pacman\n if (key === \"pacman\" && settings.colors[key] === \"multicolor\" && value) {\n const emoji = settings.display.show_emojis ? settings.emojis.pacman : \"\";\n return `${style}${emoji}${value}${suffix}`;\n }\n\n return `${style}${emoji}${value}${suffix}`;\n}\n\n/**\n * Wraps text to a maximum visible width, filling each line completely with\n * characters before moving on. Unlike block-boundary wrapping, this breaks in\n * the middle of an info block when it doesn't fit, so no visible space is left\n * unused at the end of a line.\n *\n * ANSI color codes are copied verbatim and never counted toward the width, and\n * the active color is re-applied at the start of each wrapped line so colors\n * survive a mid-block break. Emoji surrogate pairs are kept intact.\n *\n * @param text - The text to wrap (may contain ANSI escape sequences)\n * @param maxLength - Maximum visible characters per line\n * @returns Array of wrapped lines\n */\nfunction wrapAnsiText(text: string, maxLength: number): string[] {\n if (maxLength <= 0) return text ? [text] : [];\n\n const lines: string[] = [];\n let currentLine = \"\";\n let visibleCount = 0;\n let activeAnsi = \"\";\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n\n // Copy ANSI escape sequences verbatim without counting them.\n if (char === \"\\x1b\" && text[i + 1] === \"[\") {\n let seq = char;\n let j = i + 1;\n while (j < text.length && text[j] !== \"m\") {\n seq += text[j];\n j++;\n }\n if (j < text.length) seq += text[j]; // include the trailing \"m\"\n currentLine += seq;\n // Track the active color so wrapped lines keep it; a reset clears it.\n activeAnsi = /\\x1b\\[0m/.test(seq) ? \"\" : seq;\n i = j;\n continue;\n }\n\n // Keep an emoji (surrogate pair) together and count it as width 2.\n let unit = char;\n let width = 1;\n const code = text.charCodeAt(i);\n if (code >= 0xd800 && code <= 0xdbff && i + 1 < text.length) {\n unit = char + text[i + 1];\n width = 2;\n i++;\n }\n\n // Line is full: break here, even in the middle of an info block.\n if (visibleCount >= maxLength) {\n lines.push(currentLine);\n currentLine = activeAnsi;\n visibleCount = 0;\n }\n\n currentLine += unit;\n visibleCount += width;\n }\n\n if (currentLine) lines.push(currentLine);\n\n return lines;\n}\n\nasync function displaySystemInfo(\n customDisplayOrder: string[][] | null = null\n): Promise<void> {\n const settings = loadSettings();\n const displayOrder = customDisplayOrder || settings.display_order;\n\n // Get system info\n const info = await getSystemInfo();\n\n // Single line mode\n if (settings.display.single_line) {\n const allItems: string[] = [];\n\n for (const group of displayOrder) {\n for (const key of group) {\n const value = info[key as keyof SystemInfo] as string;\n const formatted = formatValue(key, value, settings);\n if (formatted && formatted.trim()) {\n allItems.push(formatted);\n }\n }\n }\n\n if (allItems.length > 0) {\n console.log(allItems.join(\" \") + colors.reset);\n }\n return;\n }\n\n // Multi-line mode: fill each line completely with characters, wrapping in\n // the middle of an info block when it no longer fits.\n const maxLineLength = settings.display.line_wrap_length;\n const allItems: string[] = [];\n\n for (const group of displayOrder) {\n for (const key of group) {\n const value = info[key as keyof SystemInfo] as string;\n const formatted = formatValue(key, value, settings);\n\n if (formatted && formatted.trim()) {\n allItems.push(formatted);\n }\n }\n }\n\n const lines = wrapAnsiText(allItems.join(\" \"), maxLineLength);\n\n // Output\n if (lines.length > 0) {\n lines.forEach((line) => {\n console.log(line + colors.reset);\n });\n } else if (settings.advanced.debug) {\n console.log(\"No system information could be displayed\");\n }\n}\n\nfunction handleSettingsCommand(args: string[]): boolean {\n const settings = loadSettings();\n\n if (args.includes(\"--settings-init\")) {\n if (saveSettings(DEFAULT_SETTINGS)) {\n console.log(\"Settings initialized with defaults\");\n } else {\n console.log(\"Failed to initialize settings\");\n }\n return true;\n }\n\n if (args.includes(\"--settings-show\")) {\n console.log(\"Current settings:\");\n console.log(JSON.stringify(settings, null, 2));\n return true;\n }\n\n if (args.includes(\"--settings-reset\")) {\n if (saveSettings(DEFAULT_SETTINGS)) {\n console.log(\"Settings reset to defaults\");\n } else {\n console.log(\"Failed to reset settings\");\n }\n return true;\n }\n\n if (args.includes(\"--refresh\")) {\n try {\n if (fs.existsSync(CACHE_FILE)) {\n fs.unlinkSync(CACHE_FILE);\n console.log(\"Cache cleared\");\n }\n } catch (error) {\n console.error(\"Error clearing cache:\", (error as Error).message);\n }\n return true;\n }\n\n const setIndex = args.indexOf(\"--set\");\n if (setIndex !== -1 && args[setIndex + 1] && args[setIndex + 2]) {\n const key = args[setIndex + 1];\n const value = args[setIndex + 2];\n\n try {\n const parsedValue =\n value.startsWith(\"{\") || value.startsWith(\"[\")\n ? JSON.parse(value)\n : value === \"true\"\n ? true\n : value === \"false\"\n ? false\n : value;\n\n const keys = key.split(\".\");\n let current: any = settings;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]];\n }\n current[keys[keys.length - 1]] = parsedValue;\n\n if (saveSettings(settings)) {\n console.log(`Setting ${key} = ${value}`);\n } else {\n console.log(\"Failed to save settings\");\n }\n } catch (error) {\n console.error(\"Error setting value:\", (error as Error).message);\n }\n return true;\n }\n\n return false;\n}\n\nfunction installShellGreeting(): void {\n const homeDir = os.homedir();\n\n let configDir: string, scriptPath: string;\n if (IS_WINDOWS) {\n configDir = path.join(homeDir, \"AppData\", \"Local\");\n scriptPath = path.join(configDir, \"systeminfo\");\n } else {\n configDir = path.join(homeDir, \".config\");\n scriptPath = path.join(configDir, \"systeminfo\");\n }\n\n const currentScript = path.resolve(__filename);\n\n try {\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n fs.copyFileSync(currentScript, scriptPath);\n if (!IS_WINDOWS) {\n fs.chmodSync(scriptPath, \"755\");\n }\n\n if (IS_WINDOWS) {\n console.log(\"Windows installation:\");\n console.log(\"1. Script copied to:\", scriptPath);\n console.log(\"2. To add to PowerShell profile, run:\");\n console.log(` Add-Content $PROFILE \"node '${scriptPath}'\"`);\n console.log(\n \"3. To add to Command Prompt, create a batch file in your startup folder\"\n );\n\n const startupBat = path.join(configDir, \"systeminfo-startup.bat\");\n fs.writeFileSync(startupBat, `@echo off\\nnode \"${scriptPath}\"\\n`);\n console.log(\"4. Batch file created:\", startupBat);\n } else {\n try {\n const hushLoginPath = path.join(homeDir, \".hushlogin\");\n fs.writeFileSync(hushLoginPath, \"\");\n } catch {}\n\n const bashrcPath = path.join(homeDir, \".bashrc\");\n const bashLine = `node ${scriptPath}`;\n\n if (fs.existsSync(bashrcPath)) {\n const bashrc = fs.readFileSync(bashrcPath, \"utf8\");\n if (!bashrc.includes(\"systeminfo\")) {\n fs.appendFileSync(bashrcPath, `\\n${bashLine}\\n`);\n }\n } else {\n fs.writeFileSync(bashrcPath, `${bashLine}\\n`);\n }\n\n const zshrcPath = path.join(homeDir, \".zshrc\");\n if (fs.existsSync(zshrcPath)) {\n const zshrc = fs.readFileSync(zshrcPath, \"utf8\");\n if (!zshrc.includes(\"systeminfo\")) {\n fs.appendFileSync(zshrcPath, `\\n${bashLine}\\n`);\n }\n }\n\n const fishConfigPath = path.join(\n homeDir,\n \".config\",\n \"fish\",\n \"config.fish\"\n );\n if (fs.existsSync(fishConfigPath)) {\n const fishConfig = fs.readFileSync(fishConfigPath, \"utf8\");\n if (!fishConfig.includes(\"systeminfo\")) {\n fs.appendFileSync(\n fishConfigPath,\n `\\nset -U fish_greeting \"\"\\n${bashLine}\\n`\n );\n }\n }\n\n const nushellConfigPath = path.join(\n homeDir,\n \".config\",\n \"nushell\",\n \"config.nu\"\n );\n if (fs.existsSync(nushellConfigPath)) {\n const nushellConfig = fs.readFileSync(nushellConfigPath, \"utf8\");\n if (!nushellConfig.includes(\"systeminfo\")) {\n fs.appendFileSync(\n nushellConfigPath,\n `\\n$env.config.show_banner = false\\n${bashLine}\\n`\n );\n }\n }\n }\n\n console.log(\"Shell greeting installation completed!\");\n } catch (error) {\n console.error(\"Error installing shell greeting:\", (error as Error).message);\n process.exit(1);\n }\n}\n\nfunction parseCLIMode(args: string[]): string[] | null {\n for (const arg of args) {\n if (!arg.startsWith(\"--\") && arg.includes(\",\")) {\n return arg\n .split(\",\")\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n }\n }\n\n for (const arg of args) {\n if (!arg.startsWith(\"--\") && !arg.includes(\"=\") && arg.length > 0) {\n return [arg.trim()];\n }\n }\n\n return null;\n}\n\nasync function showHelp(): Promise<void> {\n console.log(`\nSystem Info Script - TypeScript Version\n\nUsage:\n about-system [options]\n about-system <part1,part2,...> # CLI mode: show specific parts only\n\nOptions:\n --help, -h Show this help message\n --install Install as shell greeting\n --settings-init Initialize settings file with defaults\n --settings-show Display current settings\n --settings-reset Reset settings to defaults\n --refresh Clear the cache file\n --set <key> <value> Set a configuration value (use dot notation)\n --json Output as JSON\n\nExamples:\n about-system # Show all info (default)\n about-system cpu,os # Show only CPU and OS info\n about-system user,hostname,ip # Show user, hostname, and IP\n about-system disk_used # Show only disk usage\n about-system --install\n about-system --set display.show_emojis false\n about-system --set display.show_backgrounds false\n about-system --set colors.user blue\n about-system --set emojis.cpu \"🚀 \"\n about-system --set labels.cpu \"Processor\"\n about-system --json\n\nSettings file: ${SETTINGS_FILE}\nCache file: ${CACHE_FILE}\n\nPlatform: ${\n IS_WINDOWS\n ? \"Windows\"\n : os.platform() === \"darwin\"\n ? \"macOS\"\n : os.platform() === \"linux\"\n ? \"Linux\"\n : \"Unknown\"\n }\n\nAvailable display blocks:\n Basic: user, hostname, uptime, shell, os, kernel, device\n Resources: disk_used, ram_used, memory_available, swap_used, top_process\n Network: ip, iplocal, city, domain, isp, network_interfaces\n Hardware: cpu, gpu, temperature, battery, screen_resolution\n System: load_average, users_logged_in, mount_points, services_running\n Tools: pacman, ports, containers\n`);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (handleSettingsCommand(args)) {\n return;\n }\n\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n await showHelp();\n return;\n }\n\n if (args.includes(\"--install\")) {\n installShellGreeting();\n return;\n }\n\n if (args.includes(\"--json\")) {\n const info = await getSystemInfo();\n console.log(JSON.stringify(info, null, 2));\n return;\n }\n\n const cliParts = parseCLIMode(args);\n if (cliParts) {\n const customDisplayOrder = [cliParts];\n await displaySystemInfo(customDisplayOrder);\n return;\n }\n\n await displaySystemInfo();\n}\n\nmain().catch((error) => {\n console.error(\"Error:\", error.message);\n process.exit(1);\n});\n\nexport { displaySystemInfo, installShellGreeting };\n"],"names":["__filename","fileURLToPath","IS_WINDOWS","os","MULTICOLOR_BG","formatValue","key","value","settings","showBackgrounds","colorName","bg","backgrounds","style","colors","suffix","emoji","batteryEmoji","output","ports","colorCodes","port","index","colorCode","bgCode","textCode","wrapAnsiText","text","maxLength","lines","currentLine","visibleCount","activeAnsi","i","char","seq","j","unit","width","code","displaySystemInfo","customDisplayOrder","loadSettings","displayOrder","info","getSystemInfo","allItems","group","formatted","maxLineLength","line","handleSettingsCommand","args","saveSettings","DEFAULT_SETTINGS","fs","CACHE_FILE","error","setIndex","parsedValue","keys","current","installShellGreeting","homeDir","configDir","scriptPath","path","currentScript","startupBat","hushLoginPath","bashrcPath","bashLine","zshrcPath","fishConfigPath","nushellConfigPath","parseCLIMode","arg","part","showHelp","SETTINGS_FILE","main","cliParts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"about-system-cli.js","sources":["../src/web-server.ts","../src/about-system-cli.ts"],"sourcesContent":["/**\n * @fileoverview `about-system web` — serves the React dashboard and a JSON API.\n *\n * The dashboard is prebuilt into dist/web by `vite build --config web/vite.config.ts`\n * and served as static files; the only dynamic route is GET /api/info, which\n * returns the same object as `about-system --json`.\n */\n\nimport http from \"http\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getSystemInfo } from \"./system-info-api\";\n\nexport interface WebServerOptions {\n port?: number;\n host?: string;\n /** Directory holding the built dashboard. Defaults to dist/web next to this file. */\n webRoot?: string;\n}\n\nexport const DEFAULT_WEB_PORT = 3777;\nexport const DEFAULT_WEB_HOST = \"127.0.0.1\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n \".woff2\": \"font/woff2\",\n};\n\nfunction defaultWebRoot(): string {\n return path.join(path.dirname(fileURLToPath(import.meta.url)), \"web\");\n}\n\n/**\n * Resolves a request path to a file inside webRoot, or null if it escapes the\n * root or does not exist. Unknown paths fall back to index.html (SPA routing).\n */\nexport function resolveStaticFile(webRoot: string, urlPath: string): string | null {\n let decoded: string;\n try {\n decoded = decodeURIComponent(urlPath.split(\"?\")[0]);\n } catch {\n return null;\n }\n const root = path.resolve(webRoot);\n const candidate = path.resolve(root, \".\" + path.posix.normalize(\"/\" + decoded));\n if (candidate !== root && !candidate.startsWith(root + path.sep)) return null;\n\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return candidate;\n const index = path.join(root, \"index.html\");\n return fs.existsSync(index) ? index : null;\n}\n\nfunction send(res: http.ServerResponse, status: number, type: string, body: string | Buffer): void {\n res.writeHead(status, {\n \"Content-Type\": type,\n \"Cache-Control\": \"no-store\",\n \"X-Content-Type-Options\": \"nosniff\",\n });\n res.end(body);\n}\n\nexport function createWebServer(options: WebServerOptions = {}): http.Server {\n const webRoot = options.webRoot ?? defaultWebRoot();\n\n return http.createServer(async (req, res) => {\n const url = req.url ?? \"/\";\n\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n send(res, 405, \"text/plain; charset=utf-8\", \"Method Not Allowed\");\n return;\n }\n\n if (url === \"/api/info\" || url.startsWith(\"/api/info?\")) {\n try {\n const info = await getSystemInfo();\n send(res, 200, MIME_TYPES[\".json\"], JSON.stringify(info));\n } catch (error) {\n send(res, 500, MIME_TYPES[\".json\"], JSON.stringify({ error: (error as Error).message }));\n }\n return;\n }\n\n const file = resolveStaticFile(webRoot, url);\n if (!file) {\n send(\n res,\n 404,\n \"text/plain; charset=utf-8\",\n \"Dashboard not found. Build it with: npm run build:web\"\n );\n return;\n }\n const type = MIME_TYPES[path.extname(file)] ?? \"application/octet-stream\";\n send(res, 200, type, fs.readFileSync(file));\n });\n}\n\n/** Starts the dashboard server and resolves with the URL it is listening on. */\nexport function startWebServer(options: WebServerOptions = {}): Promise<{ server: http.Server; url: string }> {\n const port = options.port ?? DEFAULT_WEB_PORT;\n const host = options.host ?? DEFAULT_WEB_HOST;\n const server = createWebServer(options);\n\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n const address = server.address();\n const actualPort = typeof address === \"object\" && address ? address.port : port;\n const shownHost = host === \"0.0.0.0\" || host === \"::\" ? \"localhost\" : host;\n resolve({ server, url: `http://${shownHost}:${actualPort}` });\n });\n });\n}\n","#!/usr/bin/env node\nimport os from \"os\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getSystemInfo } from \"./system-info-api\";\nimport type { SystemInfo, SystemInfoOptions } from \"./systeminfo-types\";\nimport { startWebServer, DEFAULT_WEB_PORT, DEFAULT_WEB_HOST } from \"./web-server\";\n\nconst __filename = fileURLToPath(import.meta.url);\n\nimport {\n Settings,\n DEFAULT_SETTINGS,\n colors,\n backgrounds,\n SETTINGS_FILE,\n CACHE_FILE,\n loadSettings,\n saveSettings,\n} from \"./info/settings\";\n\n// Platform detection\nconst IS_WINDOWS = os.platform() === \"win32\";\n\n// Foreground code -> [background code, contrasting text code] for the\n// legacy 8-color multicolor rotation (ports/pacman rainbow mode).\nconst MULTICOLOR_BG: Record<number, [number, number]> = {\n 31: [41, 97], // red\n 32: [42, 30], // green\n 33: [43, 30], // yellow\n 34: [44, 97], // blue\n 35: [45, 97], // magenta\n 36: [46, 30], // cyan\n};\n\nfunction formatValue(key: string, value: string, settings: Settings): string {\n if (!value || value.trim() === \"\") return \"\";\n\n const showBackgrounds = settings.display.show_backgrounds !== false;\n const colorName = settings.colors[key] as keyof typeof colors;\n const bg = backgrounds[colorName as keyof typeof backgrounds];\n const style = (showBackgrounds && bg) || colors[colorName] || colors.reset;\n const suffix = showBackgrounds ? colors.reset : \"\";\n const emoji = settings.display.show_emojis ? settings.emojis[key] || \"\" : \"\";\n\n // Special handling for battery emoji\n if (key === \"battery\" && settings.display.show_emojis) {\n const batteryEmoji = value.includes(\"+\")\n ? settings.emojis.battery_charging\n : settings.emojis.battery;\n return `${style}${batteryEmoji}${value}${suffix}`;\n }\n\n // Multicolor handling for ports\n if (key === \"ports\" && settings.colors[key] === \"multicolor\" && value) {\n const emoji = settings.display.show_emojis ? settings.emojis.ports : \"\";\n let output = ` ${emoji}`;\n const ports = value.split(\" \");\n const colorCodes = [31, 32, 33, 34, 35, 36];\n ports.forEach((port, index) => {\n const colorCode = colorCodes[index % colorCodes.length];\n if (showBackgrounds) {\n const [bgCode, textCode] = MULTICOLOR_BG[colorCode];\n output += `\\x1b[${bgCode}m\\x1b[${textCode}m${port}\\x1b[0m `;\n } else {\n output += `\\x1b[${colorCode}m${port}\\x1b[0m `;\n }\n });\n return output.trim();\n }\n\n // Multicolor handling for pacman\n if (key === \"pacman\" && settings.colors[key] === \"multicolor\" && value) {\n const emoji = settings.display.show_emojis ? settings.emojis.pacman : \"\";\n return `${style}${emoji}${value}${suffix}`;\n }\n\n return `${style}${emoji}${value}${suffix}`;\n}\n\n/**\n * Wraps text to a maximum visible width, filling each line completely with\n * characters before moving on. Unlike block-boundary wrapping, this breaks in\n * the middle of an info block when it doesn't fit, so no visible space is left\n * unused at the end of a line.\n *\n * ANSI color codes are copied verbatim and never counted toward the width, and\n * the active color is re-applied at the start of each wrapped line so colors\n * survive a mid-block break. Emoji surrogate pairs are kept intact.\n *\n * @param text - The text to wrap (may contain ANSI escape sequences)\n * @param maxLength - Maximum visible characters per line\n * @returns Array of wrapped lines\n */\nfunction wrapAnsiText(text: string, maxLength: number): string[] {\n if (maxLength <= 0) return text ? [text] : [];\n\n const lines: string[] = [];\n let currentLine = \"\";\n let visibleCount = 0;\n let activeAnsi = \"\";\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n\n // Copy ANSI escape sequences verbatim without counting them.\n if (char === \"\\x1b\" && text[i + 1] === \"[\") {\n let seq = char;\n let j = i + 1;\n while (j < text.length && text[j] !== \"m\") {\n seq += text[j];\n j++;\n }\n if (j < text.length) seq += text[j]; // include the trailing \"m\"\n currentLine += seq;\n // Track the active color so wrapped lines keep it; a reset clears it.\n activeAnsi = /\\x1b\\[0m/.test(seq) ? \"\" : seq;\n i = j;\n continue;\n }\n\n // Keep an emoji (surrogate pair) together and count it as width 2.\n let unit = char;\n let width = 1;\n const code = text.charCodeAt(i);\n if (code >= 0xd800 && code <= 0xdbff && i + 1 < text.length) {\n unit = char + text[i + 1];\n width = 2;\n i++;\n }\n\n // Line is full: break here, even in the middle of an info block.\n if (visibleCount >= maxLength) {\n lines.push(currentLine);\n currentLine = activeAnsi;\n visibleCount = 0;\n }\n\n currentLine += unit;\n visibleCount += width;\n }\n\n if (currentLine) lines.push(currentLine);\n\n return lines;\n}\n\nasync function displaySystemInfo(\n customDisplayOrder: string[][] | null = null\n): Promise<void> {\n const settings = loadSettings();\n const displayOrder = customDisplayOrder || settings.display_order;\n\n // Get system info\n const info = await getSystemInfo();\n\n // Single line mode\n if (settings.display.single_line) {\n const allItems: string[] = [];\n\n for (const group of displayOrder) {\n for (const key of group) {\n const value = info[key as keyof SystemInfo] as string;\n const formatted = formatValue(key, value, settings);\n if (formatted && formatted.trim()) {\n allItems.push(formatted);\n }\n }\n }\n\n if (allItems.length > 0) {\n console.log(allItems.join(\" \") + colors.reset);\n }\n return;\n }\n\n // Multi-line mode: fill each line completely with characters, wrapping in\n // the middle of an info block when it no longer fits.\n const maxLineLength = settings.display.line_wrap_length;\n const allItems: string[] = [];\n\n for (const group of displayOrder) {\n for (const key of group) {\n const value = info[key as keyof SystemInfo] as string;\n const formatted = formatValue(key, value, settings);\n\n if (formatted && formatted.trim()) {\n allItems.push(formatted);\n }\n }\n }\n\n const lines = wrapAnsiText(allItems.join(\" \"), maxLineLength);\n\n // Output\n if (lines.length > 0) {\n lines.forEach((line) => {\n console.log(line + colors.reset);\n });\n } else if (settings.advanced.debug) {\n console.log(\"No system information could be displayed\");\n }\n}\n\nfunction handleSettingsCommand(args: string[]): boolean {\n const settings = loadSettings();\n\n if (args.includes(\"--settings-init\")) {\n if (saveSettings(DEFAULT_SETTINGS)) {\n console.log(\"Settings initialized with defaults\");\n } else {\n console.log(\"Failed to initialize settings\");\n }\n return true;\n }\n\n if (args.includes(\"--settings-show\")) {\n console.log(\"Current settings:\");\n console.log(JSON.stringify(settings, null, 2));\n return true;\n }\n\n if (args.includes(\"--settings-reset\")) {\n if (saveSettings(DEFAULT_SETTINGS)) {\n console.log(\"Settings reset to defaults\");\n } else {\n console.log(\"Failed to reset settings\");\n }\n return true;\n }\n\n if (args.includes(\"--refresh\")) {\n try {\n if (fs.existsSync(CACHE_FILE)) {\n fs.unlinkSync(CACHE_FILE);\n console.log(\"Cache cleared\");\n }\n } catch (error) {\n console.error(\"Error clearing cache:\", (error as Error).message);\n }\n return true;\n }\n\n const setIndex = args.indexOf(\"--set\");\n if (setIndex !== -1 && args[setIndex + 1] && args[setIndex + 2]) {\n const key = args[setIndex + 1];\n const value = args[setIndex + 2];\n\n try {\n const parsedValue =\n value.startsWith(\"{\") || value.startsWith(\"[\")\n ? JSON.parse(value)\n : value === \"true\"\n ? true\n : value === \"false\"\n ? false\n : value;\n\n const keys = key.split(\".\");\n let current: any = settings;\n for (let i = 0; i < keys.length - 1; i++) {\n if (!current[keys[i]]) current[keys[i]] = {};\n current = current[keys[i]];\n }\n current[keys[keys.length - 1]] = parsedValue;\n\n if (saveSettings(settings)) {\n console.log(`Setting ${key} = ${value}`);\n } else {\n console.log(\"Failed to save settings\");\n }\n } catch (error) {\n console.error(\"Error setting value:\", (error as Error).message);\n }\n return true;\n }\n\n return false;\n}\n\nfunction installShellGreeting(): void {\n const homeDir = os.homedir();\n\n let configDir: string, scriptPath: string;\n if (IS_WINDOWS) {\n configDir = path.join(homeDir, \"AppData\", \"Local\");\n scriptPath = path.join(configDir, \"systeminfo\");\n } else {\n configDir = path.join(homeDir, \".config\");\n scriptPath = path.join(configDir, \"systeminfo\");\n }\n\n const currentScript = path.resolve(__filename);\n\n try {\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n fs.copyFileSync(currentScript, scriptPath);\n if (!IS_WINDOWS) {\n fs.chmodSync(scriptPath, \"755\");\n }\n\n if (IS_WINDOWS) {\n console.log(\"Windows installation:\");\n console.log(\"1. Script copied to:\", scriptPath);\n console.log(\"2. To add to PowerShell profile, run:\");\n console.log(` Add-Content $PROFILE \"node '${scriptPath}'\"`);\n console.log(\n \"3. To add to Command Prompt, create a batch file in your startup folder\"\n );\n\n const startupBat = path.join(configDir, \"systeminfo-startup.bat\");\n fs.writeFileSync(startupBat, `@echo off\\nnode \"${scriptPath}\"\\n`);\n console.log(\"4. Batch file created:\", startupBat);\n } else {\n try {\n const hushLoginPath = path.join(homeDir, \".hushlogin\");\n fs.writeFileSync(hushLoginPath, \"\");\n } catch {}\n\n const bashrcPath = path.join(homeDir, \".bashrc\");\n const bashLine = `node ${scriptPath}`;\n\n if (fs.existsSync(bashrcPath)) {\n const bashrc = fs.readFileSync(bashrcPath, \"utf8\");\n if (!bashrc.includes(\"systeminfo\")) {\n fs.appendFileSync(bashrcPath, `\\n${bashLine}\\n`);\n }\n } else {\n fs.writeFileSync(bashrcPath, `${bashLine}\\n`);\n }\n\n const zshrcPath = path.join(homeDir, \".zshrc\");\n if (fs.existsSync(zshrcPath)) {\n const zshrc = fs.readFileSync(zshrcPath, \"utf8\");\n if (!zshrc.includes(\"systeminfo\")) {\n fs.appendFileSync(zshrcPath, `\\n${bashLine}\\n`);\n }\n }\n\n const fishConfigPath = path.join(\n homeDir,\n \".config\",\n \"fish\",\n \"config.fish\"\n );\n if (fs.existsSync(fishConfigPath)) {\n const fishConfig = fs.readFileSync(fishConfigPath, \"utf8\");\n if (!fishConfig.includes(\"systeminfo\")) {\n fs.appendFileSync(\n fishConfigPath,\n `\\nset -U fish_greeting \"\"\\n${bashLine}\\n`\n );\n }\n }\n\n const nushellConfigPath = path.join(\n homeDir,\n \".config\",\n \"nushell\",\n \"config.nu\"\n );\n if (fs.existsSync(nushellConfigPath)) {\n const nushellConfig = fs.readFileSync(nushellConfigPath, \"utf8\");\n if (!nushellConfig.includes(\"systeminfo\")) {\n fs.appendFileSync(\n nushellConfigPath,\n `\\n$env.config.show_banner = false\\n${bashLine}\\n`\n );\n }\n }\n }\n\n console.log(\"Shell greeting installation completed!\");\n } catch (error) {\n console.error(\"Error installing shell greeting:\", (error as Error).message);\n process.exit(1);\n }\n}\n\n/** Reads `--flag value` or `--flag=value` from args. */\nfunction flagValue(args: string[], flag: string): string | undefined {\n for (let i = 0; i < args.length; i++) {\n if (args[i] === flag) return args[i + 1];\n if (args[i].startsWith(flag + \"=\")) return args[i].slice(flag.length + 1);\n }\n return undefined;\n}\n\nasync function runWebServer(args: string[]): Promise<void> {\n const rawPort = flagValue(args, \"--port\") ?? process.env.PORT;\n const port = rawPort === undefined ? DEFAULT_WEB_PORT : Number(rawPort);\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new Error(`Invalid port: ${rawPort}`);\n }\n const host = flagValue(args, \"--host\") ?? DEFAULT_WEB_HOST;\n\n const { url } = await startWebServer({ port, host });\n console.log(`About System web UI running at ${url}`);\n if (host === DEFAULT_WEB_HOST) {\n console.log(\"Listening on localhost only. Use --host 0.0.0.0 to expose it on the network.\");\n } else {\n console.log(\"Warning: system details (public IP, ports, processes) are visible to anyone who can reach this port.\");\n }\n console.log(\"Press Ctrl+C to stop.\");\n}\n\nfunction parseCLIMode(args: string[]): string[] | null {\n for (const arg of args) {\n if (!arg.startsWith(\"--\") && arg.includes(\",\")) {\n return arg\n .split(\",\")\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n }\n }\n\n for (const arg of args) {\n if (!arg.startsWith(\"--\") && !arg.includes(\"=\") && arg.length > 0) {\n return [arg.trim()];\n }\n }\n\n return null;\n}\n\nasync function showHelp(): Promise<void> {\n console.log(`\nSystem Info Script - TypeScript Version\n\nUsage:\n about-system [options]\n about-system <part1,part2,...> # CLI mode: show specific parts only\n about-system web [--port N] [--host H] # Serve the web dashboard\n\nOptions:\n --help, -h Show this help message\n --install Install as shell greeting\n --settings-init Initialize settings file with defaults\n --settings-show Display current settings\n --settings-reset Reset settings to defaults\n --refresh Clear the cache file\n --set <key> <value> Set a configuration value (use dot notation)\n --json Output as JSON\n\nExamples:\n about-system # Show all info (default)\n about-system cpu,os # Show only CPU and OS info\n about-system user,hostname,ip # Show user, hostname, and IP\n about-system disk_used # Show only disk usage\n about-system --install\n about-system --set display.show_emojis false\n about-system --set display.show_backgrounds false\n about-system --set colors.user blue\n about-system --set emojis.cpu \"🚀 \"\n about-system --set labels.cpu \"Processor\"\n about-system --json\n about-system web # http://127.0.0.1:${DEFAULT_WEB_PORT}\n about-system web --port 8080 --host 0.0.0.0\n\nSettings file: ${SETTINGS_FILE}\nCache file: ${CACHE_FILE}\n\nPlatform: ${\n IS_WINDOWS\n ? \"Windows\"\n : os.platform() === \"darwin\"\n ? \"macOS\"\n : os.platform() === \"linux\"\n ? \"Linux\"\n : \"Unknown\"\n }\n\nAvailable display blocks:\n Basic: user, hostname, uptime, shell, os, kernel, device\n Resources: disk_used, ram_used, memory_available, swap_used, top_process\n Network: ip, iplocal, city, domain, isp, network_interfaces\n Hardware: cpu, gpu, temperature, battery, screen_resolution\n System: load_average, users_logged_in, mount_points, services_running\n Tools: pacman, ports, containers\n`);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (handleSettingsCommand(args)) {\n return;\n }\n\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n await showHelp();\n return;\n }\n\n if (args.includes(\"--install\")) {\n installShellGreeting();\n return;\n }\n\n if (args[0] === \"web\" || args.includes(\"--web\")) {\n await runWebServer(args);\n return;\n }\n\n if (args.includes(\"--json\")) {\n const info = await getSystemInfo();\n console.log(JSON.stringify(info, null, 2));\n return;\n }\n\n const cliParts = parseCLIMode(args);\n if (cliParts) {\n const customDisplayOrder = [cliParts];\n await displaySystemInfo(customDisplayOrder);\n return;\n }\n\n await displaySystemInfo();\n}\n\nmain().catch((error) => {\n console.error(\"Error:\", error.message);\n process.exit(1);\n});\n\nexport { displaySystemInfo, installShellGreeting };\n"],"names":["DEFAULT_WEB_PORT","DEFAULT_WEB_HOST","MIME_TYPES","defaultWebRoot","path","fileURLToPath","resolveStaticFile","webRoot","urlPath","decoded","root","candidate","fs","index","send","res","status","type","body","createWebServer","options","http","req","url","info","getSystemInfo","error","file","startWebServer","port","host","server","resolve","reject","address","actualPort","__filename","IS_WINDOWS","os","MULTICOLOR_BG","formatValue","key","value","settings","showBackgrounds","colorName","bg","backgrounds","style","colors","suffix","emoji","batteryEmoji","output","ports","colorCodes","colorCode","bgCode","textCode","wrapAnsiText","text","maxLength","lines","currentLine","visibleCount","activeAnsi","char","seq","j","unit","width","code","displaySystemInfo","customDisplayOrder","loadSettings","displayOrder","allItems","group","formatted","maxLineLength","line","handleSettingsCommand","args","saveSettings","DEFAULT_SETTINGS","CACHE_FILE","setIndex","parsedValue","keys","current","i","installShellGreeting","homeDir","configDir","scriptPath","currentScript","startupBat","hushLoginPath","bashrcPath","bashLine","zshrcPath","fishConfigPath","nushellConfigPath","flagValue","flag","runWebServer","rawPort","parseCLIMode","arg","part","showHelp","SETTINGS_FILE","main","cliParts"],"mappings":"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"}
|