triflux 2.1.0 → 2.2.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/bin/tfx-doctor.mjs +1 -1
- package/bin/tfx-setup.mjs +1 -1
- package/bin/triflux.mjs +1 -1
- package/hud/hud-qos-status.mjs +43 -15
- package/package.json +1 -1
package/bin/tfx-doctor.mjs
CHANGED
package/bin/tfx-setup.mjs
CHANGED
package/bin/triflux.mjs
CHANGED
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import https from "node:https";
|
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { spawn, execSync } from "node:child_process";
|
|
9
9
|
|
|
10
|
-
const VERSION = "1.
|
|
10
|
+
const VERSION = "1.9";
|
|
11
11
|
|
|
12
12
|
// ============================================================================
|
|
13
13
|
// ANSI 색상 (OMC colors.js 스키마 일치)
|
|
@@ -19,7 +19,7 @@ const RED = "\x1b[31m";
|
|
|
19
19
|
const GREEN = "\x1b[32m";
|
|
20
20
|
const YELLOW = "\x1b[33m";
|
|
21
21
|
const CYAN = "\x1b[36m";
|
|
22
|
-
const CLAUDE_ORANGE = "\x1b[38;5;
|
|
22
|
+
const CLAUDE_ORANGE = "\x1b[38;5;173m"; // #D87656 (Claude 테라코타)
|
|
23
23
|
const CODEX_WHITE = "\x1b[97m"; // bright white (SGR 37은 Windows Terminal에서 연회색 매핑)
|
|
24
24
|
const GEMINI_BLUE = "\x1b[38;5;39m";
|
|
25
25
|
|
|
@@ -52,15 +52,43 @@ function colorParallel(current, cap) {
|
|
|
52
52
|
return red(`${current}/${cap}`);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const GAUGE_WIDTH = 5;
|
|
56
|
+
const GAUGE_BLOCKS = ["░", "▒", "▓", "█"]; // 밝기 0~3
|
|
57
|
+
|
|
58
|
+
function coloredBar(percent, width = GAUGE_WIDTH, baseColor = null) {
|
|
56
59
|
const safePercent = Math.min(100, Math.max(0, percent));
|
|
57
|
-
const
|
|
58
|
-
|
|
60
|
+
const perBlock = 100 / width;
|
|
61
|
+
|
|
62
|
+
// 상태별 색상
|
|
59
63
|
let barColor;
|
|
60
64
|
if (safePercent >= 85) barColor = RED;
|
|
61
65
|
else if (safePercent >= 70) barColor = YELLOW;
|
|
62
66
|
else barColor = baseColor || GREEN;
|
|
63
|
-
|
|
67
|
+
|
|
68
|
+
let bar = "";
|
|
69
|
+
for (let i = 0; i < width; i++) {
|
|
70
|
+
const blockStart = i * perBlock;
|
|
71
|
+
const blockEnd = (i + 1) * perBlock;
|
|
72
|
+
|
|
73
|
+
if (safePercent >= blockEnd) {
|
|
74
|
+
bar += "█"; // 완전 채움
|
|
75
|
+
} else if (safePercent > blockStart) {
|
|
76
|
+
// 프론티어: 구간 내 진행률
|
|
77
|
+
const progress = (safePercent - blockStart) / perBlock;
|
|
78
|
+
if (progress >= 0.75) bar += "▓";
|
|
79
|
+
else if (progress >= 0.33) bar += "▒";
|
|
80
|
+
else bar += "░";
|
|
81
|
+
} else {
|
|
82
|
+
bar += "░"; // 미도달
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 채워진 부분 = barColor, 빈 부분 = DIM
|
|
87
|
+
const filledEnd = Math.ceil(safePercent / perBlock);
|
|
88
|
+
const coloredPart = barColor + bar.slice(0, filledEnd) + RESET;
|
|
89
|
+
const dimPart = filledEnd < width ? DIM + bar.slice(filledEnd) + RESET : "";
|
|
90
|
+
|
|
91
|
+
return coloredPart + dimPart;
|
|
64
92
|
}
|
|
65
93
|
|
|
66
94
|
// 프로바이더별 색상 % (< 70%: 프로바이더 색, ≥ 70%: 경고색)
|
|
@@ -122,7 +150,7 @@ const GEMINI_SESSION_STALE_MS = 15 * 1000; // 15초
|
|
|
122
150
|
const GEMINI_API_TIMEOUT_MS = 3000; // 3초
|
|
123
151
|
const ACCOUNT_LABEL_WIDTH = 10;
|
|
124
152
|
const PROVIDER_PREFIX_WIDTH = 2;
|
|
125
|
-
const PERCENT_CELL_WIDTH =
|
|
153
|
+
const PERCENT_CELL_WIDTH = 3;
|
|
126
154
|
const TIME_CELL_INNER_WIDTH = 6;
|
|
127
155
|
const CLAUDE_REFRESH_FLAG = "--refresh-claude-usage";
|
|
128
156
|
const CODEX_REFRESH_FLAG = "--refresh-codex-rate-limits";
|
|
@@ -266,7 +294,7 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
266
294
|
if (fiveHourPct >= 80) indicatorRows += 1;
|
|
267
295
|
|
|
268
296
|
// 6) 각 tier에서 줄바꿈 없이 3줄 가용한지 확인
|
|
269
|
-
const tierWidths = { full:
|
|
297
|
+
const tierWidths = { full: 70, normal: 60, compact: 40, nano: 34 };
|
|
270
298
|
for (const tier of ["full", "normal", "compact", "nano"]) {
|
|
271
299
|
const lineWidth = tierWidths[tier];
|
|
272
300
|
const visualRowsPerLine = Math.ceil(lineWidth / Math.max(cols, 1));
|
|
@@ -278,14 +306,14 @@ function selectTier(stdin, claudeUsage = null) {
|
|
|
278
306
|
|
|
279
307
|
// full tier 전용: 게이지 바 접두사 (normal 이하 tier에서는 빈 문자열)
|
|
280
308
|
function tierBar(percent, baseColor = null) {
|
|
281
|
-
return CURRENT_TIER === "full" ?
|
|
309
|
+
return CURRENT_TIER === "full" ? coloredBar(percent, GAUGE_WIDTH, baseColor) + " " : "";
|
|
282
310
|
}
|
|
283
311
|
function tierDimBar() {
|
|
284
|
-
return CURRENT_TIER === "full" ?
|
|
312
|
+
return CURRENT_TIER === "full" ? DIM + "░".repeat(GAUGE_WIDTH) + RESET + " " : "";
|
|
285
313
|
}
|
|
286
314
|
// Gemini ∞% 전용: 무한 쿼터이므로 dim 회색 바
|
|
287
315
|
function tierInfBar() {
|
|
288
|
-
return CURRENT_TIER === "full" ?
|
|
316
|
+
return CURRENT_TIER === "full" ? DIM + "█".repeat(GAUGE_WIDTH) + RESET + " " : "";
|
|
289
317
|
}
|
|
290
318
|
|
|
291
319
|
// ============================================================================
|
|
@@ -405,7 +433,7 @@ function normalizeTimeToken(value) {
|
|
|
405
433
|
const text = String(value || "n/a");
|
|
406
434
|
const hourMinute = text.match(/^(\d+)h(\d+)m$/);
|
|
407
435
|
if (hourMinute) {
|
|
408
|
-
return `${Number(hourMinute[1])}h${Number(hourMinute[2])}m`;
|
|
436
|
+
return `${Number(hourMinute[1])}h${String(Number(hourMinute[2])).padStart(2, "0")}m`;
|
|
409
437
|
}
|
|
410
438
|
const dayHour = text.match(/^(\d+)d(\d+)h$/);
|
|
411
439
|
if (dayHour) {
|
|
@@ -416,7 +444,7 @@ function normalizeTimeToken(value) {
|
|
|
416
444
|
|
|
417
445
|
function formatTimeCell(value) {
|
|
418
446
|
const text = normalizeTimeToken(value);
|
|
419
|
-
return `(${text.padStart(TIME_CELL_INNER_WIDTH, "
|
|
447
|
+
return `(${text.padStart(TIME_CELL_INNER_WIDTH, "0")})`;
|
|
420
448
|
}
|
|
421
449
|
|
|
422
450
|
// 주간(d/h) 전용 — 최대 7d00h(5자)이므로 공백 불필요
|
|
@@ -748,11 +776,11 @@ function formatResetRemaining(isoOrUnix) {
|
|
|
748
776
|
const d = typeof isoOrUnix === "string" ? new Date(isoOrUnix) : new Date(isoOrUnix * 1000);
|
|
749
777
|
if (isNaN(d.getTime())) return "";
|
|
750
778
|
const diffMs = d.getTime() - Date.now();
|
|
751
|
-
if (diffMs <= 0) return "
|
|
779
|
+
if (diffMs <= 0) return "0h00m";
|
|
752
780
|
const totalMinutes = Math.floor(diffMs / 60000);
|
|
753
781
|
const totalHours = Math.floor(totalMinutes / 60);
|
|
754
782
|
const minutes = totalMinutes % 60;
|
|
755
|
-
return `${totalHours}h${minutes}m`;
|
|
783
|
+
return `${totalHours}h${String(minutes).padStart(2, "0")}m`;
|
|
756
784
|
}
|
|
757
785
|
|
|
758
786
|
function isResetPast(isoOrUnix) {
|