trx 1.1.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/package.json +12 -0
- package/trx.js +154 -0
package/package.json
ADDED
package/trx.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require("child_process");
|
|
4
|
+
const process = require("process");
|
|
5
|
+
|
|
6
|
+
/* ─────────────── COLORS ─────────────── */
|
|
7
|
+
|
|
8
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
9
|
+
|
|
10
|
+
const C = useColor ? {
|
|
11
|
+
reset: "\x1b[0m",
|
|
12
|
+
dim: "\x1b[2m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
|
|
15
|
+
red: "\x1b[31m",
|
|
16
|
+
brightRed: "\x1b[91m",
|
|
17
|
+
green: "\x1b[32m",
|
|
18
|
+
yellow: "\x1b[33m",
|
|
19
|
+
blue: "\x1b[34m",
|
|
20
|
+
magenta: "\x1b[35m",
|
|
21
|
+
cyan: "\x1b[36m",
|
|
22
|
+
gray: "\x1b[90m",
|
|
23
|
+
|
|
24
|
+
PROC: "\x1b[36m",
|
|
25
|
+
OUT: "\x1b[90m",
|
|
26
|
+
ERR: "\x1b[31m",
|
|
27
|
+
TIME: "\x1b[35m",
|
|
28
|
+
OK: "\x1b[32m",
|
|
29
|
+
FAIL: "\x1b[91m"
|
|
30
|
+
} : Object.fromEntries(
|
|
31
|
+
["reset","dim","bold","red","brightRed","green","yellow","blue","magenta","cyan","gray",
|
|
32
|
+
"PROC","OUT","ERR","TIME","OK","FAIL"].map(k => [k, ""])
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/* ─────────────── UTILS ─────────────── */
|
|
36
|
+
|
|
37
|
+
const now = () =>
|
|
38
|
+
new Date().toISOString().split("T")[1].replace("Z", "");
|
|
39
|
+
|
|
40
|
+
function formatElapsed(sec) {
|
|
41
|
+
if (sec < 60) return `${sec}s`;
|
|
42
|
+
const m = Math.floor(sec / 60);
|
|
43
|
+
const s = sec % 60;
|
|
44
|
+
return `${m}m ${s}s`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function banner(cmd) {
|
|
48
|
+
console.log(`
|
|
49
|
+
${C.cyan}${C.bold}┌─[ TERMTTRACE ]────────────────────────────────────────┐${C.reset}
|
|
50
|
+
${C.cyan}│${C.reset} ${C.gray}Command:${C.reset} ${C.bold}${cmd}${C.reset}
|
|
51
|
+
${C.cyan}└────────────────────────────────────────────────────────┘${C.reset}
|
|
52
|
+
`.trim());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function log(type, icon, message) {
|
|
56
|
+
clearSpinner();
|
|
57
|
+
console.log(
|
|
58
|
+
`${C.dim}[${now()}]${C.reset} ${C[type]}${icon} ${message}${C.reset}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function die(msg) {
|
|
63
|
+
console.error(`${C.FAIL}${msg}${C.reset}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ─────────────── SPINNER ─────────────── */
|
|
68
|
+
|
|
69
|
+
const frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
|
|
70
|
+
let spinnerIndex = 0;
|
|
71
|
+
let spinnerTimer = null;
|
|
72
|
+
let startTime = null;
|
|
73
|
+
|
|
74
|
+
function startSpinner() {
|
|
75
|
+
startTime = Date.now();
|
|
76
|
+
spinnerTimer = setInterval(() => {
|
|
77
|
+
const elapsedSec = Math.floor((Date.now() - startTime) / 1000);
|
|
78
|
+
const frame = frames[spinnerIndex++ % frames.length];
|
|
79
|
+
const time = formatElapsed(elapsedSec);
|
|
80
|
+
|
|
81
|
+
process.stdout.write(
|
|
82
|
+
`\r${C.cyan}${frame}${C.reset} ${C.gray}running…${C.reset} ${C.magenta}${time}${C.reset} `
|
|
83
|
+
);
|
|
84
|
+
}, 120);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function clearSpinner() {
|
|
88
|
+
if (spinnerTimer) {
|
|
89
|
+
clearInterval(spinnerTimer);
|
|
90
|
+
spinnerTimer = null;
|
|
91
|
+
process.stdout.write("\r\x1b[2K");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ─────────────── ARGUMENTS ─────────────── */
|
|
96
|
+
|
|
97
|
+
const args = process.argv.slice(2);
|
|
98
|
+
if (!args.length) {
|
|
99
|
+
die("Usage: termtrace <command> [args...]");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const command = args.join(" ");
|
|
103
|
+
|
|
104
|
+
/* ─────────────── EXECUTION ─────────────── */
|
|
105
|
+
|
|
106
|
+
banner(command);
|
|
107
|
+
log("PROC", "⚡", "process spawned");
|
|
108
|
+
|
|
109
|
+
startSpinner();
|
|
110
|
+
|
|
111
|
+
const child = spawn(command, {
|
|
112
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
113
|
+
shell: true
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
child.stdout.on("data", (data) => {
|
|
117
|
+
data
|
|
118
|
+
.toString()
|
|
119
|
+
.split("\n")
|
|
120
|
+
.filter(Boolean)
|
|
121
|
+
.forEach(line => log("OUT", "›", line));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
child.stderr.on("data", (data) => {
|
|
125
|
+
data
|
|
126
|
+
.toString()
|
|
127
|
+
.split("\n")
|
|
128
|
+
.filter(Boolean)
|
|
129
|
+
.forEach(line => log("ERR", "✖", line));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
child.on("exit", (code, signal) => {
|
|
133
|
+
clearSpinner();
|
|
134
|
+
|
|
135
|
+
const elapsedSec = Math.floor((Date.now() - startTime) / 1000);
|
|
136
|
+
const elapsed = formatElapsed(elapsedSec);
|
|
137
|
+
|
|
138
|
+
if (signal) {
|
|
139
|
+
log("FAIL", "☠", `killed by signal ${signal}`);
|
|
140
|
+
} else if (code === 0) {
|
|
141
|
+
log("OK", "✔", "process completed successfully");
|
|
142
|
+
} else {
|
|
143
|
+
log("FAIL", "✖", `exit code ${code}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
log("TIME", "⏱", `total time ${elapsed}`);
|
|
147
|
+
process.exit(code ?? 1);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
child.on("error", (err) => {
|
|
151
|
+
clearSpinner();
|
|
152
|
+
log("FAIL", "☠", err.message);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
});
|