@triflux/core 10.1.0 → 10.25.1
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/hooks/agent-route-guard.mjs +14 -10
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +20 -8
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +267 -35
- package/hooks/hook-registry.json +343 -256
- package/hooks/hooks.json +49 -1
- package/hooks/keyword-rules.json +183 -23
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/permission-safe-allow.mjs +233 -0
- package/hooks/pipeline-stop.mjs +369 -22
- package/hooks/post-tool-tips.mjs +306 -0
- package/hooks/pre-compact-snapshot.mjs +181 -0
- package/hooks/safety-guard.mjs +254 -33
- package/hooks/session-end-cleanup.mjs +345 -0
- package/hooks/session-start-fast.mjs +214 -0
- package/hooks/subagent-tracker.mjs +267 -0
- package/hooks/subagent-verifier.mjs +9 -4
- package/hub/account-broker.mjs +1036 -66
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +96 -58
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +163 -126
- package/hub/bridge.mjs +831 -290
- package/hub/cli-adapter-base.mjs +321 -97
- package/hub/codex-adapter.mjs +179 -93
- package/hub/codex-compat.mjs +4 -4
- package/hub/codex-preflight.mjs +113 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/dynamic-routing-engine.mjs +511 -0
- package/hub/fullcycle.mjs +34 -18
- package/hub/gemini-adapter.mjs +77 -95
- package/hub/hitl.mjs +202 -143
- package/hub/intent.mjs +167 -38
- package/hub/lib/bash-path.mjs +73 -0
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/env-detect.mjs +145 -0
- package/hub/lib/hosts-compat.mjs +319 -0
- package/hub/lib/mcp-response-cache.mjs +239 -0
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +165 -0
- package/hub/lib/phase-manager.mjs +493 -0
- package/hub/lib/process-utils.mjs +797 -104
- package/hub/lib/prompt-tmp.mjs +33 -0
- package/hub/lib/risk-tier.mjs +53 -0
- package/hub/lib/spawn-trace.mjs +561 -0
- package/hub/lib/ssh-command.mjs +44 -24
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/staged-file-detect.mjs +43 -0
- package/hub/lib/state-snapshot.mjs +293 -0
- package/hub/lib/tfx-route-args.mjs +309 -0
- package/hub/lib/timeline-adapter.mjs +190 -0
- package/hub/lib/trace-recorder.mjs +153 -0
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/quota-middleware.mjs +70 -0
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +65 -48
- package/hub/quality/deslop.mjs +70 -49
- package/hub/reflexion.mjs +87 -13
- package/hub/research.mjs +33 -25
- package/hub/router.mjs +941 -791
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/routing-snapshot.mjs +261 -0
- package/hub/session-fingerprint.mjs +127 -61
- package/hub/state.mjs +86 -44
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/token-mode.mjs +72 -71
- package/hub/workers/worker-utils.mjs +22 -16
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +184 -27
- package/hud/context-monitor.mjs +133 -44
- package/hud/hud-qos-status.mjs +153 -50
- package/hud/mission-board.mjs +56 -0
- package/hud/providers/claude.mjs +398 -114
- package/hud/providers/codex.mjs +96 -21
- package/hud/providers/gemini.mjs +184 -51
- package/hud/renderers.mjs +413 -123
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +81 -39
- package/mesh/index.mjs +69 -0
- package/mesh/mesh-budget.mjs +137 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +163 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +77 -0
- package/package.json +5 -1
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +13 -5
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +58 -30
- package/scripts/lib/env.mjs +14 -0
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +48 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +391 -247
- package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
- package/scripts/lib/mcp-guard-engine.mjs +1281 -115
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +24 -14
- package/scripts/lib/mcp-server-catalog.mjs +355 -118
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +70 -10
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +32 -7
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
package/hud/renderers.mjs
CHANGED
|
@@ -5,26 +5,51 @@ import { existsSync, readdirSync } from "node:fs";
|
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
bold,
|
|
9
|
+
CLAUDE_ORANGE,
|
|
10
|
+
CODEX_WHITE,
|
|
11
|
+
claudeOrange,
|
|
12
|
+
codexWhite,
|
|
13
|
+
colorByPercent,
|
|
14
|
+
colorByProvider,
|
|
15
|
+
dim,
|
|
16
|
+
GEMINI_BLUE,
|
|
17
|
+
geminiBlue,
|
|
18
|
+
green,
|
|
19
|
+
red,
|
|
20
|
+
yellow,
|
|
12
21
|
} from "./colors.mjs";
|
|
13
22
|
import {
|
|
14
|
-
|
|
15
|
-
FIVE_HOUR_MS,
|
|
16
|
-
|
|
23
|
+
ACCOUNT_LABEL_WIDTH,
|
|
24
|
+
FIVE_HOUR_MS,
|
|
25
|
+
LEGACY_SV_ACCUMULATOR,
|
|
26
|
+
ONE_DAY_MS,
|
|
27
|
+
PROVIDER_PREFIX_WIDTH,
|
|
28
|
+
SEVEN_DAY_MS,
|
|
29
|
+
SV_ACCUMULATOR_PATH,
|
|
30
|
+
TEAM_STATE_PATH,
|
|
17
31
|
} from "./constants.mjs";
|
|
32
|
+
import { buildContextUsageView } from "./context-monitor.mjs";
|
|
33
|
+
import { deriveGeminiLimits } from "./providers/gemini.mjs";
|
|
34
|
+
import { getTerminalColumns, tierBar, tierDimBar } from "./terminal.mjs";
|
|
18
35
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
clampPercent,
|
|
37
|
+
fitText,
|
|
38
|
+
formatPercentCell,
|
|
39
|
+
formatPlaceholderPercentCell,
|
|
40
|
+
formatResetRemaining,
|
|
41
|
+
formatResetRemainingDayHour,
|
|
42
|
+
formatSavings,
|
|
43
|
+
formatSvPct,
|
|
44
|
+
formatTimeCell,
|
|
45
|
+
formatTimeCellDH,
|
|
46
|
+
formatTokenCount,
|
|
47
|
+
padAnsiRight,
|
|
48
|
+
readJson,
|
|
49
|
+
readJsonMigrate,
|
|
50
|
+
stripAnsi,
|
|
51
|
+
truncateAnsi,
|
|
24
52
|
} from "./utils.mjs";
|
|
25
|
-
import { tierBar, tierDimBar, getTerminalColumns } from "./terminal.mjs";
|
|
26
|
-
import { deriveGeminiLimits } from "./providers/gemini.mjs";
|
|
27
|
-
import { buildContextUsageView } from "./context-monitor.mjs";
|
|
28
53
|
|
|
29
54
|
// ============================================================================
|
|
30
55
|
// 최근 벤치마크 diff 파일 읽기
|
|
@@ -33,15 +58,26 @@ export function readLatestBenchmarkDiff() {
|
|
|
33
58
|
const diffsDir = join(homedir(), ".omc", "state", "cx-auto-tokens", "diffs");
|
|
34
59
|
if (!existsSync(diffsDir)) return null;
|
|
35
60
|
try {
|
|
36
|
-
const files = readdirSync(diffsDir)
|
|
61
|
+
const files = readdirSync(diffsDir)
|
|
62
|
+
.filter((f) => f.endsWith(".json"))
|
|
63
|
+
.sort()
|
|
64
|
+
.reverse();
|
|
37
65
|
if (files.length === 0) return null;
|
|
38
66
|
return readJson(join(diffsDir, files[0]), null);
|
|
39
|
-
} catch {
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
40
70
|
}
|
|
41
71
|
|
|
42
72
|
// 토큰 절약액 누적치 읽기 (tfx-auto token tracker)
|
|
43
73
|
export function readTokenSavings() {
|
|
44
|
-
const savingsPath = join(
|
|
74
|
+
const savingsPath = join(
|
|
75
|
+
homedir(),
|
|
76
|
+
".omc",
|
|
77
|
+
"state",
|
|
78
|
+
"tfx-auto-tokens",
|
|
79
|
+
"savings-total.json",
|
|
80
|
+
);
|
|
45
81
|
const data = readJson(savingsPath, null);
|
|
46
82
|
if (!data || data.totalSaved === 0) return null;
|
|
47
83
|
return data;
|
|
@@ -63,14 +99,15 @@ export function formatTokenSummary(diff) {
|
|
|
63
99
|
const inputStr = formatTokenCount(t.input);
|
|
64
100
|
const outputStr = formatTokenCount(t.output);
|
|
65
101
|
const actualStr = formatSavings(s.actualCost);
|
|
66
|
-
const
|
|
67
|
-
const savedPct =
|
|
68
|
-
? Math.round((s.saved / s.claudeCost) * 100)
|
|
69
|
-
: 0;
|
|
102
|
+
const _claudeStr = formatSavings(s.claudeCost);
|
|
103
|
+
const savedPct =
|
|
104
|
+
s.claudeCost > 0 ? Math.round((s.saved / s.claudeCost) * 100) : 0;
|
|
70
105
|
|
|
71
|
-
return
|
|
106
|
+
return (
|
|
107
|
+
`${dim("tok:")}${inputStr}${dim("in")} ${outputStr}${dim("out")} ` +
|
|
72
108
|
`${dim("cost:")}${actualStr} ` +
|
|
73
|
-
`${dim("sv:")}${green(formatSavings(s.saved))}${dim("(")}${savedPct}%${dim(")")}
|
|
109
|
+
`${dim("sv:")}${green(formatSavings(s.saved))}${dim("(")}${savedPct}%${dim(")")}`
|
|
110
|
+
);
|
|
74
111
|
}
|
|
75
112
|
|
|
76
113
|
// ============================================================================
|
|
@@ -78,10 +115,14 @@ export function formatTokenSummary(diff) {
|
|
|
78
115
|
// ============================================================================
|
|
79
116
|
export function getTeamRow(currentTier) {
|
|
80
117
|
const teamState = readJson(TEAM_STATE_PATH, null);
|
|
81
|
-
if (!teamState
|
|
118
|
+
if (!teamState?.sessionName) return null;
|
|
82
119
|
|
|
83
120
|
// 팀 생존 확인: startedAt 기준 24시간 초과면 stale로 간주
|
|
84
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
teamState.startedAt &&
|
|
123
|
+
Date.now() - teamState.startedAt > 24 * 60 * 60 * 1000
|
|
124
|
+
)
|
|
125
|
+
return null;
|
|
85
126
|
|
|
86
127
|
const workers = (teamState.members || []).filter((m) => m.role === "worker");
|
|
87
128
|
if (!workers.length) return null;
|
|
@@ -92,26 +133,44 @@ export function getTeamRow(currentTier) {
|
|
|
92
133
|
const total = tasks.length || workers.length;
|
|
93
134
|
|
|
94
135
|
// 경과 시간 (80col 이상에서만 표시)
|
|
95
|
-
const elapsed =
|
|
96
|
-
|
|
97
|
-
|
|
136
|
+
const elapsed =
|
|
137
|
+
teamState.startedAt && (currentTier === "full" || currentTier === "compact")
|
|
138
|
+
? `${Math.round((Date.now() - teamState.startedAt) / 60000)}m`
|
|
139
|
+
: "";
|
|
98
140
|
|
|
99
141
|
// CLI 브랜드: 단일문자 + ANSI 색상 (x=codex, g=gemini, c=claude)
|
|
100
|
-
const cliTag = (cli) =>
|
|
142
|
+
const cliTag = (cli) =>
|
|
143
|
+
cli === "codex"
|
|
144
|
+
? bold(codexWhite("x"))
|
|
145
|
+
: cli === "gemini"
|
|
146
|
+
? bold(geminiBlue("g"))
|
|
147
|
+
: bold(claudeOrange("c"));
|
|
101
148
|
// 멤버 상태: 태그 + 상태기호 (60col 이상)
|
|
102
|
-
const memberIcons =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
149
|
+
const memberIcons =
|
|
150
|
+
currentTier === "full" ||
|
|
151
|
+
currentTier === "compact" ||
|
|
152
|
+
currentTier === "minimal"
|
|
153
|
+
? workers
|
|
154
|
+
.map((m) => {
|
|
155
|
+
const task = tasks.find((t) => t.owner === m.name);
|
|
156
|
+
const status =
|
|
157
|
+
task?.status === "completed"
|
|
158
|
+
? green("\u2713")
|
|
159
|
+
: task?.status === "in_progress"
|
|
160
|
+
? yellow("\u22EF")
|
|
161
|
+
: task?.status === "failed"
|
|
162
|
+
? red("\u2717")
|
|
163
|
+
: dim("\u25CC");
|
|
164
|
+
return `${cliTag(m.cli)}${status}`;
|
|
165
|
+
})
|
|
166
|
+
.join(" ")
|
|
167
|
+
: "";
|
|
110
168
|
|
|
111
169
|
// 진행 텍스트
|
|
112
|
-
const doneText =
|
|
113
|
-
|
|
114
|
-
|
|
170
|
+
const doneText =
|
|
171
|
+
failed > 0
|
|
172
|
+
? `${completed}/${total} ${red(`${failed}\u2717`)}`
|
|
173
|
+
: `${completed}/${total}`;
|
|
115
174
|
|
|
116
175
|
const leftText = elapsed ? `${doneText} ${dim(elapsed)}` : doneText;
|
|
117
176
|
|
|
@@ -122,13 +181,50 @@ export function getTeamRow(currentTier) {
|
|
|
122
181
|
};
|
|
123
182
|
}
|
|
124
183
|
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Mission Board 렌더러
|
|
186
|
+
// ============================================================================
|
|
187
|
+
|
|
188
|
+
const STATUS_ICON = {
|
|
189
|
+
active: "*",
|
|
190
|
+
idle: ".",
|
|
191
|
+
done: "+",
|
|
192
|
+
failed: "!",
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Mission Board 상태를 1줄 compact 포맷으로 렌더링한다.
|
|
197
|
+
* 예: "MB: exec:+ ui:* perf:. [2/3 67%]"
|
|
198
|
+
* @param {{ agents: Array<{name: string, status: string, progress: number}>, dagLevel: number, totalProgress: number } | null} state
|
|
199
|
+
* @returns {string}
|
|
200
|
+
*/
|
|
201
|
+
export function renderMissionBoard(state) {
|
|
202
|
+
if (!state) return "";
|
|
203
|
+
|
|
204
|
+
const { agents, totalProgress } = state;
|
|
205
|
+
const done = agents.filter((a) => a.status === "done").length;
|
|
206
|
+
const total = agents.length;
|
|
207
|
+
|
|
208
|
+
const parts = agents.map((a) => {
|
|
209
|
+
const icon = STATUS_ICON[a.status] ?? "?";
|
|
210
|
+
return `${a.name}:${icon}`;
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return `MB: ${parts.join(" ")} [${done}/${total} ${totalProgress}%]`;
|
|
214
|
+
}
|
|
215
|
+
|
|
125
216
|
// ============================================================================
|
|
126
217
|
// 행 정렬 렌더링
|
|
127
218
|
// ============================================================================
|
|
128
219
|
export function renderAlignedRows(rows) {
|
|
129
220
|
const cols = getTerminalColumns() || 120;
|
|
130
|
-
const rightRows = rows.filter(
|
|
131
|
-
|
|
221
|
+
const rightRows = rows.filter(
|
|
222
|
+
(row) => stripAnsi(String(row.right || "")).trim().length > 0,
|
|
223
|
+
);
|
|
224
|
+
const rawLeftWidth = rightRows.reduce(
|
|
225
|
+
(max, row) => Math.max(max, stripAnsi(row.left).length),
|
|
226
|
+
0,
|
|
227
|
+
);
|
|
132
228
|
return rows.map((row) => {
|
|
133
229
|
const prefix = padAnsiRight(row.prefix, PROVIDER_PREFIX_WIDTH);
|
|
134
230
|
const hasRight = stripAnsi(String(row.right || "")).trim().length > 0;
|
|
@@ -147,9 +243,10 @@ export function renderAlignedRows(rows) {
|
|
|
147
243
|
return truncateAnsi(`${prefix} ${left}`, cols);
|
|
148
244
|
}
|
|
149
245
|
const rightVisible = stripAnsi(row.right).length;
|
|
150
|
-
const right =
|
|
151
|
-
|
|
152
|
-
|
|
246
|
+
const right =
|
|
247
|
+
rightVisible <= availableForRight
|
|
248
|
+
? row.right
|
|
249
|
+
: truncateAnsi(row.right, availableForRight);
|
|
153
250
|
return `${prefix} ${left} ${dim("|")} ${right}`;
|
|
154
251
|
});
|
|
155
252
|
}
|
|
@@ -157,23 +254,43 @@ export function renderAlignedRows(rows) {
|
|
|
157
254
|
// ============================================================================
|
|
158
255
|
// micro tier: 모든 프로바이더를 1줄로 압축
|
|
159
256
|
// ============================================================================
|
|
160
|
-
export function getMicroLine(
|
|
257
|
+
export function getMicroLine(
|
|
258
|
+
contextView,
|
|
259
|
+
claudeUsage,
|
|
260
|
+
codexBuckets,
|
|
261
|
+
geminiSession,
|
|
262
|
+
geminiBucket,
|
|
263
|
+
combinedSvPct,
|
|
264
|
+
geminiMarker = "g",
|
|
265
|
+
) {
|
|
161
266
|
const ctxView = contextView || buildContextUsageView({}, null);
|
|
162
|
-
|
|
163
267
|
// Claude 5h/1w
|
|
164
|
-
const cF =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
268
|
+
const cF =
|
|
269
|
+
claudeUsage?.fiveHourPercent != null
|
|
270
|
+
? clampPercent(claudeUsage.fiveHourPercent)
|
|
271
|
+
: null;
|
|
272
|
+
const cW =
|
|
273
|
+
claudeUsage?.weeklyPercent != null
|
|
274
|
+
? clampPercent(claudeUsage.weeklyPercent)
|
|
275
|
+
: null;
|
|
276
|
+
const cVal =
|
|
277
|
+
claudeUsage != null
|
|
278
|
+
? `${cF != null ? colorByProvider(cF, `${cF}`, claudeOrange) : dim("--")}${dim("/")}${cW != null ? colorByProvider(cW, `${cW}`, claudeOrange) : dim("--")}`
|
|
279
|
+
: dim("--/--");
|
|
169
280
|
|
|
170
281
|
// Codex 5h/1w
|
|
171
282
|
let xVal = dim("--/--");
|
|
172
283
|
if (codexBuckets) {
|
|
173
284
|
const mb = codexBuckets.codex || codexBuckets[Object.keys(codexBuckets)[0]];
|
|
174
285
|
if (mb) {
|
|
175
|
-
const xF =
|
|
176
|
-
|
|
286
|
+
const xF =
|
|
287
|
+
mb.primary?.used_percent != null
|
|
288
|
+
? clampPercent(mb.primary.used_percent)
|
|
289
|
+
: null;
|
|
290
|
+
const xW =
|
|
291
|
+
mb.secondary?.used_percent != null
|
|
292
|
+
? clampPercent(mb.secondary.used_percent)
|
|
293
|
+
: null;
|
|
177
294
|
xVal = `${xF != null ? colorByProvider(xF, `${xF}`, codexWhite) : dim("--")}${dim("/")}${xW != null ? colorByProvider(xW, `${xW}`, codexWhite) : dim("--")}`;
|
|
178
295
|
}
|
|
179
296
|
}
|
|
@@ -182,7 +299,9 @@ export function getMicroLine(contextView, claudeUsage, codexBuckets, geminiSessi
|
|
|
182
299
|
let gVal;
|
|
183
300
|
if (geminiBucket) {
|
|
184
301
|
const gl = deriveGeminiLimits(geminiBucket);
|
|
185
|
-
const gU = gl
|
|
302
|
+
const gU = gl
|
|
303
|
+
? gl.usedPct
|
|
304
|
+
: clampPercent((1 - (geminiBucket.remainingFraction ?? 1)) * 100);
|
|
186
305
|
gVal = colorByProvider(gU, `${gU}`, geminiBlue);
|
|
187
306
|
} else if ((geminiSession?.total || 0) > 0) {
|
|
188
307
|
gVal = geminiBlue("\u221E");
|
|
@@ -194,9 +313,10 @@ export function getMicroLine(contextView, claudeUsage, codexBuckets, geminiSessi
|
|
|
194
313
|
const sv = formatSvPct(combinedSvPct || 0).trim();
|
|
195
314
|
|
|
196
315
|
const cols = getTerminalColumns() || 120;
|
|
197
|
-
const line =
|
|
316
|
+
const line =
|
|
317
|
+
`${bold(claudeOrange("c"))}${dim(":")}${cVal} ` +
|
|
198
318
|
`${bold(codexWhite("x"))}${dim(":")}${xVal} ` +
|
|
199
|
-
`${bold(geminiBlue(
|
|
319
|
+
`${bold(geminiBlue(geminiMarker))}${dim(":")}${gVal} ` +
|
|
200
320
|
`${dim("sv:")}${sv} ` +
|
|
201
321
|
`${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
|
|
202
322
|
return truncateAnsi(line, cols);
|
|
@@ -205,10 +325,14 @@ export function getMicroLine(contextView, claudeUsage, codexBuckets, geminiSessi
|
|
|
205
325
|
// ============================================================================
|
|
206
326
|
// Claude 행 렌더러
|
|
207
327
|
// ============================================================================
|
|
208
|
-
export function getClaudeRows(
|
|
328
|
+
export function getClaudeRows(
|
|
329
|
+
currentTier,
|
|
330
|
+
contextView,
|
|
331
|
+
claudeUsage,
|
|
332
|
+
combinedSvPct,
|
|
333
|
+
) {
|
|
209
334
|
const ctxView = contextView || buildContextUsageView({}, null);
|
|
210
335
|
const prefix = `${bold(claudeOrange("c"))}:`;
|
|
211
|
-
|
|
212
336
|
// 절약 퍼센트
|
|
213
337
|
const svStr = formatSvPct(combinedSvPct || 0);
|
|
214
338
|
const svSuffix = `${dim("sv:")}${svStr}`;
|
|
@@ -225,36 +349,67 @@ export function getClaudeRows(currentTier, contextView, claudeUsage, combinedSvP
|
|
|
225
349
|
|
|
226
350
|
const hasData = claudeUsage != null;
|
|
227
351
|
|
|
228
|
-
const fStr =
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
352
|
+
const fStr =
|
|
353
|
+
hasData && fiveHourPercent != null
|
|
354
|
+
? colorByProvider(
|
|
355
|
+
fiveHourPercent,
|
|
356
|
+
formatPercentCell(fiveHourPercent),
|
|
357
|
+
claudeOrange,
|
|
358
|
+
)
|
|
359
|
+
: dim(formatPlaceholderPercentCell());
|
|
360
|
+
const wStr =
|
|
361
|
+
hasData && weeklyPercent != null
|
|
362
|
+
? colorByProvider(
|
|
363
|
+
weeklyPercent,
|
|
364
|
+
formatPercentCell(weeklyPercent),
|
|
365
|
+
claudeOrange,
|
|
366
|
+
)
|
|
367
|
+
: dim(formatPlaceholderPercentCell());
|
|
368
|
+
const fBar =
|
|
369
|
+
hasData && fiveHourPercent != null
|
|
370
|
+
? tierBar(currentTier, fiveHourPercent, CLAUDE_ORANGE)
|
|
371
|
+
: tierDimBar(currentTier);
|
|
372
|
+
const wBar =
|
|
373
|
+
hasData && weeklyPercent != null
|
|
374
|
+
? tierBar(currentTier, weeklyPercent, CLAUDE_ORANGE)
|
|
375
|
+
: tierDimBar(currentTier);
|
|
232
376
|
const fTime = formatTimeCell(fiveHourReset);
|
|
233
377
|
const wTime = formatTimeCellDH(weeklyReset);
|
|
234
378
|
|
|
235
379
|
if (currentTier === "nano" || currentTier === "micro") {
|
|
236
|
-
const fShort =
|
|
237
|
-
|
|
380
|
+
const fShort =
|
|
381
|
+
hasData && fiveHourPercent != null
|
|
382
|
+
? colorByProvider(fiveHourPercent, `${fiveHourPercent}%`, claudeOrange)
|
|
383
|
+
: dim("--");
|
|
384
|
+
const wShort =
|
|
385
|
+
hasData && weeklyPercent != null
|
|
386
|
+
? colorByProvider(weeklyPercent, `${weeklyPercent}%`, claudeOrange)
|
|
387
|
+
: dim("--");
|
|
238
388
|
const quotaSection = `${fShort}${dim("/")}${wShort}`;
|
|
239
389
|
return [{ prefix, left: quotaSection, right: "" }];
|
|
240
390
|
}
|
|
241
391
|
|
|
242
392
|
if (currentTier === "minimal") {
|
|
243
|
-
const
|
|
393
|
+
const staleTag = claudeUsage?.stale ? ` ${dim("[stale]")}` : "";
|
|
394
|
+
const quotaSection = `${dim("5h:")}${fStr} ${dim("1w:")}${wStr}${staleTag}`;
|
|
244
395
|
const right = `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
|
|
245
396
|
return [{ prefix, left: quotaSection, right }];
|
|
246
397
|
}
|
|
247
398
|
|
|
248
399
|
if (currentTier === "compact") {
|
|
249
400
|
const quotaSection = `${dim("5h:")}${fStr} ${dim(fTime)} ${dim("1w:")}${wStr} ${dim(wTime)}`;
|
|
250
|
-
const warning = ctxView.warningTag
|
|
401
|
+
const warning = ctxView.warningTag
|
|
402
|
+
? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
|
|
403
|
+
: "";
|
|
251
404
|
const contextSection = `${svSuffix} ${dim("|")} ${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
|
|
252
405
|
return [{ prefix, left: quotaSection, right: contextSection }];
|
|
253
406
|
}
|
|
254
407
|
|
|
255
408
|
// full tier (>= 120 cols)
|
|
256
409
|
const quotaSection = `${dim("5h:")}${fBar}${fStr} ${dim(fTime)} ${dim("1w:")}${wBar}${wStr} ${dim(wTime)}`;
|
|
257
|
-
const warning = ctxView.warningTag
|
|
410
|
+
const warning = ctxView.warningTag
|
|
411
|
+
? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
|
|
412
|
+
: "";
|
|
258
413
|
const contextSection = `${svSuffix} ${dim("|")} ${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
|
|
259
414
|
return [{ prefix, left: quotaSection, right: contextSection }];
|
|
260
415
|
}
|
|
@@ -262,53 +417,109 @@ export function getClaudeRows(currentTier, contextView, claudeUsage, combinedSvP
|
|
|
262
417
|
// ============================================================================
|
|
263
418
|
// 계정 라벨 + 범용 프로바이더 행 렌더러
|
|
264
419
|
// ============================================================================
|
|
265
|
-
export function getAccountLabel(
|
|
420
|
+
export function getAccountLabel(
|
|
421
|
+
provider,
|
|
422
|
+
accountsConfig,
|
|
423
|
+
accountsState,
|
|
424
|
+
codexEmail,
|
|
425
|
+
) {
|
|
266
426
|
const providerConfig = accountsConfig?.providers?.[provider] || [];
|
|
267
427
|
const providerState = accountsState?.providers?.[provider] || {};
|
|
268
428
|
const lastId = providerState.last_selected_id;
|
|
269
|
-
const picked = providerConfig.find((a) => a.id === lastId) ||
|
|
270
|
-
|| { id: `${provider}-main`, label: provider };
|
|
429
|
+
const picked = providerConfig.find((a) => a.id === lastId) ||
|
|
430
|
+
providerConfig[0] || { id: `${provider}-main`, label: provider };
|
|
271
431
|
let label = picked.label || picked.id;
|
|
272
432
|
if (codexEmail) label = codexEmail;
|
|
273
433
|
if (label.includes("@")) label = label.split("@")[0];
|
|
274
434
|
return label;
|
|
275
435
|
}
|
|
276
436
|
|
|
277
|
-
export function getProviderRow(
|
|
278
|
-
|
|
437
|
+
export function getProviderRow(
|
|
438
|
+
currentTier,
|
|
439
|
+
provider,
|
|
440
|
+
marker,
|
|
441
|
+
markerColor,
|
|
442
|
+
qosProfile,
|
|
443
|
+
accountsConfig,
|
|
444
|
+
accountsState,
|
|
445
|
+
realQuota,
|
|
446
|
+
codexEmail,
|
|
447
|
+
savingsMultiplier,
|
|
448
|
+
modelLabel,
|
|
449
|
+
) {
|
|
450
|
+
const accountLabel = fitText(
|
|
451
|
+
getAccountLabel(provider, accountsConfig, accountsState, codexEmail),
|
|
452
|
+
ACCOUNT_LABEL_WIDTH,
|
|
453
|
+
);
|
|
279
454
|
|
|
280
455
|
// 절약 퍼센트 섹션
|
|
281
|
-
const svPct =
|
|
456
|
+
const svPct =
|
|
457
|
+
savingsMultiplier != null ? Math.round(savingsMultiplier * 100) : null;
|
|
282
458
|
const svStr = formatSvPct(svPct);
|
|
283
|
-
const
|
|
459
|
+
const _modelLabelStr = modelLabel ? ` ${markerColor(modelLabel)}` : "";
|
|
284
460
|
|
|
285
461
|
// 프로바이더별 색상 프로필
|
|
286
|
-
const
|
|
287
|
-
const
|
|
462
|
+
const isGeminiFamily = provider === "gemini" || provider === "antigravity";
|
|
463
|
+
const provAnsi =
|
|
464
|
+
provider === "codex" ? CODEX_WHITE : isGeminiFamily ? GEMINI_BLUE : GREEN;
|
|
465
|
+
const provFn =
|
|
466
|
+
provider === "codex" ? codexWhite : isGeminiFamily ? geminiBlue : green;
|
|
288
467
|
|
|
289
468
|
let quotaSection;
|
|
290
|
-
|
|
469
|
+
const _extraRightSection = "";
|
|
291
470
|
|
|
292
471
|
if (currentTier === "nano" || currentTier === "micro") {
|
|
293
472
|
const minPrefix = `${bold(markerColor(`${marker}`))}:`;
|
|
294
473
|
if (realQuota?.type === "codex") {
|
|
295
|
-
const main =
|
|
474
|
+
const main =
|
|
475
|
+
realQuota.buckets.codex ||
|
|
476
|
+
realQuota.buckets[Object.keys(realQuota.buckets)[0]];
|
|
296
477
|
if (main) {
|
|
297
|
-
const fiveP =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
478
|
+
const fiveP =
|
|
479
|
+
main.primary?.used_percent != null
|
|
480
|
+
? clampPercent(main.primary.used_percent)
|
|
481
|
+
: null;
|
|
482
|
+
const weekP =
|
|
483
|
+
main.secondary?.used_percent != null
|
|
484
|
+
? clampPercent(main.secondary.used_percent)
|
|
485
|
+
: null;
|
|
486
|
+
const fCellN =
|
|
487
|
+
fiveP != null
|
|
488
|
+
? colorByProvider(fiveP, `${fiveP}%`, provFn)
|
|
489
|
+
: dim("--%");
|
|
490
|
+
const wCellN =
|
|
491
|
+
weekP != null
|
|
492
|
+
? colorByProvider(weekP, `${weekP}%`, provFn)
|
|
493
|
+
: dim("--%");
|
|
494
|
+
return {
|
|
495
|
+
prefix: minPrefix,
|
|
496
|
+
left: `${fCellN}${dim("/")}${wCellN}`,
|
|
497
|
+
right: "",
|
|
498
|
+
};
|
|
302
499
|
}
|
|
303
500
|
}
|
|
304
501
|
if (realQuota?.type === "gemini") {
|
|
305
502
|
const pools = realQuota.pools || {};
|
|
306
503
|
if (pools.pro || pools.flash) {
|
|
307
|
-
const pP = pools.pro
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
504
|
+
const pP = pools.pro
|
|
505
|
+
? clampPercent(
|
|
506
|
+
Math.round((1 - (pools.pro.remainingFraction ?? 1)) * 100),
|
|
507
|
+
)
|
|
508
|
+
: null;
|
|
509
|
+
const pF = pools.flash
|
|
510
|
+
? clampPercent(
|
|
511
|
+
Math.round((1 - (pools.flash.remainingFraction ?? 1)) * 100),
|
|
512
|
+
)
|
|
513
|
+
: null;
|
|
514
|
+
const pStr =
|
|
515
|
+
pP != null ? colorByProvider(pP, `${pP}`, provFn) : dim("--");
|
|
516
|
+
const fStr =
|
|
517
|
+
pF != null ? colorByProvider(pF, `${pF}`, provFn) : dim("--");
|
|
518
|
+
return {
|
|
519
|
+
prefix: minPrefix,
|
|
520
|
+
left: `${pStr}${dim("/")}${fStr}`,
|
|
521
|
+
right: "",
|
|
522
|
+
};
|
|
312
523
|
}
|
|
313
524
|
}
|
|
314
525
|
return { prefix: minPrefix, left: dim("--/--"), right: "" };
|
|
@@ -316,12 +527,26 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
316
527
|
|
|
317
528
|
if (currentTier === "minimal") {
|
|
318
529
|
if (realQuota?.type === "codex") {
|
|
319
|
-
const main =
|
|
530
|
+
const main =
|
|
531
|
+
realQuota.buckets.codex ||
|
|
532
|
+
realQuota.buckets[Object.keys(realQuota.buckets)[0]];
|
|
320
533
|
if (main) {
|
|
321
|
-
const fiveP =
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
534
|
+
const fiveP =
|
|
535
|
+
main.primary?.used_percent != null
|
|
536
|
+
? clampPercent(main.primary.used_percent)
|
|
537
|
+
: null;
|
|
538
|
+
const weekP =
|
|
539
|
+
main.secondary?.used_percent != null
|
|
540
|
+
? clampPercent(main.secondary.used_percent)
|
|
541
|
+
: null;
|
|
542
|
+
const fCell =
|
|
543
|
+
fiveP != null
|
|
544
|
+
? colorByProvider(fiveP, formatPercentCell(fiveP), provFn)
|
|
545
|
+
: dim(formatPlaceholderPercentCell());
|
|
546
|
+
const wCell =
|
|
547
|
+
weekP != null
|
|
548
|
+
? colorByProvider(weekP, formatPercentCell(weekP), provFn)
|
|
549
|
+
: dim(formatPlaceholderPercentCell());
|
|
325
550
|
quotaSection = `${dim("5h:")}${fCell} ${dim("1w:")}${wCell}`;
|
|
326
551
|
}
|
|
327
552
|
}
|
|
@@ -329,9 +554,12 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
329
554
|
const pools = realQuota.pools || {};
|
|
330
555
|
if (pools.pro || pools.flash) {
|
|
331
556
|
const slot = (bucket, label) => {
|
|
332
|
-
if (!bucket)
|
|
557
|
+
if (!bucket)
|
|
558
|
+
return `${dim(label + ":")}${dim(formatPlaceholderPercentCell())}`;
|
|
333
559
|
const gl = deriveGeminiLimits(bucket);
|
|
334
|
-
const usedP = gl
|
|
560
|
+
const usedP = gl
|
|
561
|
+
? gl.usedPct
|
|
562
|
+
: clampPercent((1 - (bucket.remainingFraction ?? 1)) * 100);
|
|
335
563
|
return `${dim(label + ":")}${colorByProvider(usedP, formatPercentCell(usedP), provFn)}`;
|
|
336
564
|
};
|
|
337
565
|
quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
|
|
@@ -343,19 +571,42 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
343
571
|
quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim("1w:")}${dim(formatPlaceholderPercentCell())}`;
|
|
344
572
|
}
|
|
345
573
|
const prefix = `${bold(markerColor(`${marker}`))}:`;
|
|
346
|
-
return {
|
|
574
|
+
return {
|
|
575
|
+
prefix,
|
|
576
|
+
left: quotaSection,
|
|
577
|
+
right: accountLabel ? markerColor(accountLabel) : "",
|
|
578
|
+
};
|
|
347
579
|
}
|
|
348
580
|
|
|
349
581
|
if (currentTier === "compact") {
|
|
350
582
|
if (realQuota?.type === "codex") {
|
|
351
|
-
const main =
|
|
583
|
+
const main =
|
|
584
|
+
realQuota.buckets.codex ||
|
|
585
|
+
realQuota.buckets[Object.keys(realQuota.buckets)[0]];
|
|
352
586
|
if (main) {
|
|
353
|
-
const fiveP =
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
587
|
+
const fiveP =
|
|
588
|
+
main.primary?.used_percent != null
|
|
589
|
+
? clampPercent(main.primary.used_percent)
|
|
590
|
+
: null;
|
|
591
|
+
const weekP =
|
|
592
|
+
main.secondary?.used_percent != null
|
|
593
|
+
? clampPercent(main.secondary.used_percent)
|
|
594
|
+
: null;
|
|
595
|
+
const fCell =
|
|
596
|
+
fiveP != null
|
|
597
|
+
? colorByProvider(fiveP, formatPercentCell(fiveP), provFn)
|
|
598
|
+
: dim(formatPlaceholderPercentCell());
|
|
599
|
+
const wCell =
|
|
600
|
+
weekP != null
|
|
601
|
+
? colorByProvider(weekP, formatPercentCell(weekP), provFn)
|
|
602
|
+
: dim(formatPlaceholderPercentCell());
|
|
603
|
+
const fiveReset =
|
|
604
|
+
formatResetRemaining(main.primary?.resets_at, FIVE_HOUR_MS) || "n/a";
|
|
605
|
+
const weekReset =
|
|
606
|
+
formatResetRemainingDayHour(
|
|
607
|
+
main.secondary?.resets_at,
|
|
608
|
+
SEVEN_DAY_MS,
|
|
609
|
+
) || "n/a";
|
|
359
610
|
quotaSection = `${dim("5h:")}${fCell} ${dim(formatTimeCell(fiveReset))} ${dim("1w:")}${wCell} ${dim(formatTimeCellDH(weekReset))}`;
|
|
360
611
|
}
|
|
361
612
|
}
|
|
@@ -364,10 +615,14 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
364
615
|
const hasAnyPool = pools.pro || pools.flash;
|
|
365
616
|
if (hasAnyPool) {
|
|
366
617
|
const slot = (bucket, label) => {
|
|
367
|
-
if (!bucket)
|
|
618
|
+
if (!bucket)
|
|
619
|
+
return `${dim(label + ":")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
|
|
368
620
|
const gl = deriveGeminiLimits(bucket);
|
|
369
|
-
const usedP = gl
|
|
370
|
-
|
|
621
|
+
const usedP = gl
|
|
622
|
+
? gl.usedPct
|
|
623
|
+
: clampPercent((1 - (bucket.remainingFraction ?? 1)) * 100);
|
|
624
|
+
const rstRemaining =
|
|
625
|
+
formatResetRemaining(bucket.resetTime, ONE_DAY_MS) || "n/a";
|
|
371
626
|
return `${dim(label + ":")}${colorByProvider(usedP, formatPercentCell(usedP), provFn)} ${dim(formatTimeCell(rstRemaining))}`;
|
|
372
627
|
};
|
|
373
628
|
quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
|
|
@@ -379,23 +634,52 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
379
634
|
quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
|
|
380
635
|
}
|
|
381
636
|
const prefix = `${bold(markerColor(`${marker}`))}:`;
|
|
382
|
-
const compactRight = [
|
|
637
|
+
const compactRight = [
|
|
638
|
+
svStr ? `${dim("sv:")}${svStr}` : "",
|
|
639
|
+
accountLabel ? markerColor(accountLabel) : "",
|
|
640
|
+
]
|
|
641
|
+
.filter(Boolean)
|
|
642
|
+
.join(" ");
|
|
383
643
|
return { prefix, left: quotaSection, right: compactRight };
|
|
384
644
|
}
|
|
385
645
|
|
|
386
646
|
// full tier
|
|
387
647
|
if (realQuota?.type === "codex") {
|
|
388
|
-
const main =
|
|
648
|
+
const main =
|
|
649
|
+
realQuota.buckets.codex ||
|
|
650
|
+
realQuota.buckets[Object.keys(realQuota.buckets)[0]];
|
|
389
651
|
if (main) {
|
|
390
|
-
const fiveP =
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
652
|
+
const fiveP =
|
|
653
|
+
main.primary?.used_percent != null
|
|
654
|
+
? clampPercent(main.primary.used_percent)
|
|
655
|
+
: null;
|
|
656
|
+
const weekP =
|
|
657
|
+
main.secondary?.used_percent != null
|
|
658
|
+
? clampPercent(main.secondary.used_percent)
|
|
659
|
+
: null;
|
|
660
|
+
const fiveReset =
|
|
661
|
+
formatResetRemaining(main.primary?.resets_at, FIVE_HOUR_MS) || "n/a";
|
|
662
|
+
const weekReset =
|
|
663
|
+
formatResetRemainingDayHour(main.secondary?.resets_at, SEVEN_DAY_MS) ||
|
|
664
|
+
"n/a";
|
|
665
|
+
const fCell =
|
|
666
|
+
fiveP != null
|
|
667
|
+
? colorByProvider(fiveP, formatPercentCell(fiveP), provFn)
|
|
668
|
+
: dim(formatPlaceholderPercentCell());
|
|
669
|
+
const wCell =
|
|
670
|
+
weekP != null
|
|
671
|
+
? colorByProvider(weekP, formatPercentCell(weekP), provFn)
|
|
672
|
+
: dim(formatPlaceholderPercentCell());
|
|
673
|
+
const fBar =
|
|
674
|
+
fiveP != null
|
|
675
|
+
? tierBar(currentTier, fiveP, provAnsi)
|
|
676
|
+
: tierDimBar(currentTier);
|
|
677
|
+
const wBar =
|
|
678
|
+
weekP != null
|
|
679
|
+
? tierBar(currentTier, weekP, provAnsi)
|
|
680
|
+
: tierDimBar(currentTier);
|
|
681
|
+
quotaSection =
|
|
682
|
+
`${dim("5h:")}${fBar}${fCell} ` +
|
|
399
683
|
`${dim(formatTimeCell(fiveReset))} ` +
|
|
400
684
|
`${dim("1w:")}${wBar}${wCell} ` +
|
|
401
685
|
`${dim(formatTimeCellDH(weekReset))}`;
|
|
@@ -412,14 +696,18 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
412
696
|
return `${dim(label + ":")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
|
|
413
697
|
}
|
|
414
698
|
const gl = deriveGeminiLimits(bucket);
|
|
415
|
-
const usedP = gl
|
|
416
|
-
|
|
699
|
+
const usedP = gl
|
|
700
|
+
? gl.usedPct
|
|
701
|
+
: clampPercent((1 - (bucket.remainingFraction ?? 1)) * 100);
|
|
702
|
+
const rstRemaining =
|
|
703
|
+
formatResetRemaining(bucket.resetTime, ONE_DAY_MS) || "n/a";
|
|
417
704
|
return `${dim(label + ":")}${tierBar(currentTier, usedP, provAnsi)}${colorByProvider(usedP, formatPercentCell(usedP), provFn)} ${dim(formatTimeCell(rstRemaining))}`;
|
|
418
705
|
};
|
|
419
706
|
|
|
420
707
|
quotaSection = `${slot(pools.pro, "Pr")} ${slot(pools.flash, "Fl")}`;
|
|
421
708
|
} else {
|
|
422
|
-
quotaSection =
|
|
709
|
+
quotaSection =
|
|
710
|
+
`${dim("Pr:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ` +
|
|
423
711
|
`${dim("Fl:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
|
|
424
712
|
}
|
|
425
713
|
}
|
|
@@ -433,7 +721,9 @@ export function getProviderRow(currentTier, provider, marker, markerColor, qosPr
|
|
|
433
721
|
const accountSection = `${markerColor(accountLabel)}`;
|
|
434
722
|
const svSection = svStr ? `${dim("sv:")}${svStr}` : "";
|
|
435
723
|
const modelLabelSection = modelLabel ? markerColor(modelLabel) : "";
|
|
436
|
-
const rightParts = [svSection, accountSection, modelLabelSection].filter(
|
|
724
|
+
const rightParts = [svSection, accountSection, modelLabelSection].filter(
|
|
725
|
+
Boolean,
|
|
726
|
+
);
|
|
437
727
|
return {
|
|
438
728
|
prefix,
|
|
439
729
|
left: quotaSection,
|