@triflux/core 10.50.1 → 10.50.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/index.mjs +2 -8
- package/hub/lib/cto-env.mjs +0 -23
- package/hub/paths.mjs +0 -6
- package/hub/pipeline/index.mjs +0 -190
- package/hub/pipeline/transitions.mjs +0 -10
- package/hub/reflexion.mjs +1 -84
- package/hud/cli-policy.mjs +1 -5
- package/hud/colors.mjs +0 -13
- package/hud/constants.mjs +4 -135
- package/hud/context-monitor.mjs +3 -64
- package/hud/hud-qos-status.mjs +43 -225
- package/hud/providers/claude.mjs +4 -16
- package/hud/providers/gemini.mjs +7 -698
- package/hud/renderers.mjs +40 -294
- package/hud/terminal.mjs +1 -63
- package/hud/utils.mjs +0 -24
- package/package.json +1 -2
- package/scripts/lib/gemini-profiles.mjs +1 -1
- package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
- package/scripts/lib/psmux-info.mjs +18 -49
- package/scripts/lib/skill-template.mjs +0 -26
- package/hub/codex-compat.mjs +0 -12
- package/hub/fullcycle.mjs +0 -113
- package/hub/gemini-adapter.mjs +0 -174
- package/hub/intent.mjs +0 -321
- package/hub/lib/mcp-response-cache.mjs +0 -239
- package/hub/lib/path-utils.mjs +0 -165
- package/hub/lib/timeline-adapter.mjs +0 -190
- package/hub/pipeline/gates/confidence.mjs +0 -75
- package/hub/pipeline/gates/consensus.mjs +0 -106
- package/hub/pipeline/gates/index.mjs +0 -9
- package/hub/pipeline/gates/selfcheck.mjs +0 -120
- package/hub/quality/deslop.mjs +0 -274
- package/hub/research.mjs +0 -155
- package/hub/role-control-events.mjs +0 -113
- package/hub/routing/complexity.mjs +0 -269
- package/hub/routing/index.mjs +0 -122
- package/hub/routing/q-learning.mjs +0 -387
- package/hub/token-mode.mjs +0 -228
- package/hud/mission-board.mjs +0 -56
- package/hud/providers/cto.mjs +0 -76
- package/scripts/lib/agent-json.mjs +0 -27
- package/scripts/lib/async.mjs +0 -174
- package/scripts/lib/cli-agy.mjs +0 -85
- package/scripts/lib/cli-codex.mjs +0 -62
- package/scripts/lib/cli-gemini.mjs +0 -37
- package/scripts/lib/env.mjs +0 -14
- package/scripts/lib/hub.mjs +0 -48
- package/scripts/lib/pid.mjs +0 -63
- package/scripts/lib/quota.mjs +0 -47
- package/scripts/lib/skill-state.mjs +0 -220
- package/scripts/lib/team.mjs +0 -75
- package/scripts/lib/timeout.mjs +0 -66
- package/scripts/lib/tmp.mjs +0 -26
- package/scripts/lib/toml.mjs +0 -132
package/hud/context-monitor.mjs
CHANGED
|
@@ -4,60 +4,15 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
CONTEXT_MONITOR_CACHE_PATH,
|
|
7
|
-
CONTEXT_MONITOR_LEGACY_PATH,
|
|
8
7
|
CONTEXT_MONITOR_LOG_DIR,
|
|
9
8
|
} from "./constants.mjs";
|
|
10
|
-
import { clampPercent, formatTokenCount
|
|
9
|
+
import { clampPercent, formatTokenCount } from "./utils.mjs";
|
|
11
10
|
|
|
12
11
|
const DEFAULT_CONTEXT_LIMIT = 200_000;
|
|
13
12
|
const MILLION_CONTEXT_LIMIT = 1_000_000;
|
|
14
13
|
const MAX_CAPTURE_BYTES = 256 * 1024;
|
|
15
14
|
const MAX_TOP_KEYS = 20;
|
|
16
15
|
|
|
17
|
-
// stdin 이 context_window_size 를 제공하지 않을 때 모델 ID 로 한도를 추정한다.
|
|
18
|
-
// Anthropic 공식 문서(2026-09-30 기준): Opus 4.6 이상, Sonnet 4.6/5/5.5,
|
|
19
|
-
// Fable 5/5.1 = 1M. Sonnet 4.5 / Haiku 4.5 = 200K이며 [1m]도 유지한다.
|
|
20
|
-
const MODEL_HINT_1M_PREFIXES = [
|
|
21
|
-
"claude-sonnet-4-6",
|
|
22
|
-
"claude-sonnet-5",
|
|
23
|
-
"claude-fable-5",
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
function normalizeModelId(modelId) {
|
|
27
|
-
if (!modelId) return "";
|
|
28
|
-
return String(modelId).toLowerCase();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isMillionContextOpusModel(modelId) {
|
|
32
|
-
const match = /^claude-opus-(\d+)(?:-(\d+))?(?:$|[-[\s])/u.exec(modelId);
|
|
33
|
-
if (!match) return false;
|
|
34
|
-
|
|
35
|
-
const major = Number(match[1]);
|
|
36
|
-
const minor = Number(match[2] || 0);
|
|
37
|
-
return major > 4 || (major === 4 && minor >= 6);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function isMillionContextModel(modelId) {
|
|
41
|
-
const id = normalizeModelId(modelId);
|
|
42
|
-
if (!id) return false;
|
|
43
|
-
if (id.includes("[1m]")) return true;
|
|
44
|
-
if (isMillionContextOpusModel(id)) return true;
|
|
45
|
-
return MODEL_HINT_1M_PREFIXES.some((prefix) => id.startsWith(prefix));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function resolveModelLimit(modelId) {
|
|
49
|
-
return isMillionContextModel(modelId)
|
|
50
|
-
? MILLION_CONTEXT_LIMIT
|
|
51
|
-
: DEFAULT_CONTEXT_LIMIT;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function shouldSuppressInfoOnlyContextStatus(modelId, contextLimit = 0) {
|
|
55
|
-
const explicitLimit = Number(contextLimit);
|
|
56
|
-
return explicitLimit > 0
|
|
57
|
-
? explicitLimit >= MILLION_CONTEXT_LIMIT
|
|
58
|
-
: isMillionContextModel(modelId);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
16
|
const WARNING_LEVELS = Object.freeze({
|
|
62
17
|
ok: { min: 0, message: "" },
|
|
63
18
|
info: { min: 60, message: "컨텍스트 절반 이상 사용" },
|
|
@@ -244,14 +199,6 @@ export function formatContextUsage(usedTokens, limitTokens, percent = null) {
|
|
|
244
199
|
return `${formatTokenCount(used)}/${formatTokenCount(limit)} (${pct}%)`;
|
|
245
200
|
}
|
|
246
201
|
|
|
247
|
-
export function readContextMonitorSnapshot() {
|
|
248
|
-
return readJsonMigrate(
|
|
249
|
-
CONTEXT_MONITOR_CACHE_PATH,
|
|
250
|
-
CONTEXT_MONITOR_LEGACY_PATH,
|
|
251
|
-
null,
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
202
|
function getStdinContextUsage(stdin) {
|
|
256
203
|
const limitTokens = Number(stdin?.context_window?.context_window_size || 0);
|
|
257
204
|
const nativePercent = Number(stdin?.context_window?.used_percentage);
|
|
@@ -284,14 +231,7 @@ function getStdinContextUsage(stdin) {
|
|
|
284
231
|
return null;
|
|
285
232
|
}
|
|
286
233
|
|
|
287
|
-
export function
|
|
288
|
-
const explicit = Number(stdin?.context_window?.context_window_size || 0);
|
|
289
|
-
const modelHint = resolveModelLimit(stdin?.model?.id ?? stdin?.model);
|
|
290
|
-
return Math.max(explicit, modelHint);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export function buildContextUsageView(stdin, snapshot = null) {
|
|
294
|
-
const modelId = stdin?.model?.id ?? stdin?.model;
|
|
234
|
+
export function buildContextUsageView(stdin) {
|
|
295
235
|
const stdinUsage = getStdinContextUsage(stdin);
|
|
296
236
|
if (!stdinUsage) {
|
|
297
237
|
return {
|
|
@@ -312,8 +252,7 @@ export function buildContextUsageView(stdin, snapshot = null) {
|
|
|
312
252
|
|
|
313
253
|
const warning = classifyContextThreshold(percent);
|
|
314
254
|
const showInfoOnlyStatus = !(
|
|
315
|
-
warning.level === "info" &&
|
|
316
|
-
shouldSuppressInfoOnlyContextStatus(modelId, stdinUsage.limitTokens)
|
|
255
|
+
warning.level === "info" && stdinUsage.limitTokens >= MILLION_CONTEXT_LIMIT
|
|
317
256
|
);
|
|
318
257
|
return {
|
|
319
258
|
usedTokens,
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// 각 모듈에서 색상, 터미널, 프로바이더, 렌더러를 가져와 조합한다.
|
|
6
|
-
// ============================================================================
|
|
7
|
-
|
|
8
|
-
import { statSync } from "node:fs";
|
|
3
|
+
import { existsSync, statSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
9
5
|
import { join } from "node:path";
|
|
10
6
|
import {
|
|
11
7
|
resolveHudCliVisibility,
|
|
@@ -18,7 +14,6 @@ import {
|
|
|
18
14
|
DIM,
|
|
19
15
|
dim,
|
|
20
16
|
geminiBlue,
|
|
21
|
-
green,
|
|
22
17
|
RESET,
|
|
23
18
|
} from "./colors.mjs";
|
|
24
19
|
import {
|
|
@@ -28,107 +23,54 @@ import {
|
|
|
28
23
|
CLAUDE_BAND_MARKER_TTL_MS,
|
|
29
24
|
CLAUDE_REFRESH_FLAG,
|
|
30
25
|
CODEX_REFRESH_FLAG,
|
|
31
|
-
GEMINI_FLASH_POOL,
|
|
32
|
-
GEMINI_PRO_POOL,
|
|
33
|
-
GEMINI_REFRESH_FLAG,
|
|
34
|
-
GEMINI_SESSION_REFRESH_FLAG,
|
|
35
|
-
QOS_PATH,
|
|
36
26
|
TFX_PREFLIGHT_CACHE_PATH,
|
|
37
27
|
TFX_PREFLIGHT_CACHE_STALE_MS,
|
|
38
28
|
} from "./constants.mjs";
|
|
39
29
|
import { buildContextUsageView } from "./context-monitor.mjs";
|
|
40
|
-
import { getMissionBoardState } from "./mission-board.mjs";
|
|
41
|
-
// Claude provider
|
|
42
30
|
import {
|
|
43
31
|
fetchClaudeUsage,
|
|
44
|
-
readClaudeContextSnapshot,
|
|
45
32
|
readClaudeUsageSnapshot,
|
|
46
33
|
scheduleClaudeUsageRefresh,
|
|
47
34
|
} from "./providers/claude.mjs";
|
|
48
|
-
// Codex provider
|
|
49
35
|
import {
|
|
50
36
|
getCodexEmail,
|
|
37
|
+
hasBrokerCodexAccounts,
|
|
51
38
|
readCodexRateLimitSnapshot,
|
|
52
39
|
refreshCodexRateLimitsCache,
|
|
53
40
|
scheduleCodexRateLimitRefresh,
|
|
54
41
|
} from "./providers/codex.mjs";
|
|
55
|
-
import { readCtoStatus } from "./providers/cto.mjs";
|
|
56
|
-
// Gemini provider
|
|
57
42
|
import {
|
|
58
|
-
buildGeminiAuthContext,
|
|
59
|
-
deriveGeminiFamilyBucket,
|
|
60
|
-
fetchGeminiQuota,
|
|
61
43
|
getAntigravityAccountLabel,
|
|
62
44
|
getAntigravityCurrentModel,
|
|
63
45
|
getAntigravityModelAbbrev,
|
|
64
|
-
getAntigravityModelFamily,
|
|
65
46
|
getGeminiEmail,
|
|
66
|
-
readGeminiQuotaSnapshot,
|
|
67
|
-
readGeminiSessionSnapshot,
|
|
68
|
-
refreshGeminiSessionCache,
|
|
69
|
-
scheduleGeminiQuotaRefresh,
|
|
70
|
-
scheduleGeminiSessionRefresh,
|
|
71
47
|
} from "./providers/gemini.mjs";
|
|
72
|
-
// Renderers
|
|
73
48
|
import {
|
|
74
|
-
formatTokenSummary,
|
|
75
49
|
getClaudeRows,
|
|
76
50
|
getMicroLine,
|
|
77
51
|
getProviderRow,
|
|
78
52
|
getTeamRow,
|
|
79
|
-
readLatestBenchmarkDiff,
|
|
80
53
|
renderAlignedRows,
|
|
81
|
-
renderMissionBoard,
|
|
82
54
|
} from "./renderers.mjs";
|
|
83
55
|
import { selectTier } from "./terminal.mjs";
|
|
84
|
-
import {
|
|
85
|
-
getCliArgValue,
|
|
86
|
-
getProviderAccountId,
|
|
87
|
-
readJson,
|
|
88
|
-
readStdinJson,
|
|
89
|
-
stripAnsi,
|
|
90
|
-
} from "./utils.mjs";
|
|
56
|
+
import { readJson, readStdinJson } from "./utils.mjs";
|
|
91
57
|
|
|
92
|
-
// ============================================================================
|
|
93
|
-
// 메인
|
|
94
|
-
// ============================================================================
|
|
95
58
|
async function main() {
|
|
96
|
-
// 백그라운드 Claude 사용량 리프레시
|
|
97
59
|
if (process.argv.includes(CLAUDE_REFRESH_FLAG)) {
|
|
98
60
|
await fetchClaudeUsage(true);
|
|
99
61
|
return;
|
|
100
62
|
}
|
|
101
|
-
|
|
102
63
|
if (process.argv.includes(CODEX_REFRESH_FLAG)) {
|
|
103
64
|
await refreshCodexRateLimitsCache();
|
|
104
65
|
return;
|
|
105
66
|
}
|
|
106
67
|
|
|
107
|
-
if (process.argv.includes(GEMINI_SESSION_REFRESH_FLAG)) {
|
|
108
|
-
refreshGeminiSessionCache();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 백그라운드 Gemini 쿼터 리프레시 전용 실행 모드
|
|
113
|
-
if (process.argv.includes(GEMINI_REFRESH_FLAG)) {
|
|
114
|
-
const accountId = getCliArgValue("--account") || "gemini-main";
|
|
115
|
-
const authContext = buildGeminiAuthContext(accountId);
|
|
116
|
-
await fetchGeminiQuota(accountId, { authContext, forceRefresh: true });
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 메인 HUD 경로: 즉시 렌더 우선
|
|
121
68
|
const stdinPromise = readStdinJson();
|
|
122
|
-
|
|
123
|
-
const qosProfile = readJson(QOS_PATH, { providers: {} });
|
|
124
69
|
const preflightCache = readJson(TFX_PREFLIGHT_CACHE_PATH, null);
|
|
125
70
|
const preflightTimestamp = Number(preflightCache?.timestamp);
|
|
126
71
|
const preflightFresh =
|
|
127
72
|
Number.isFinite(preflightTimestamp) &&
|
|
128
73
|
Date.now() - preflightTimestamp <= TFX_PREFLIGHT_CACHE_STALE_MS;
|
|
129
|
-
// machine profile 의 TFX_DISABLE_* 정책. 차단된 CLI 는 행 자체를 그리지 않는다.
|
|
130
|
-
// antigravity 가 꺼지면 antigravityReady 를 강제로 내려서 기존 슬롯 로직이 그대로
|
|
131
|
-
// gemini 폴백 경로를 타게 한다.
|
|
132
74
|
const { showCodex, antigravityAllowed } = resolveHudCliVisibility();
|
|
133
75
|
const antigravityReady =
|
|
134
76
|
antigravityAllowed &&
|
|
@@ -137,236 +79,112 @@ async function main() {
|
|
|
137
79
|
const accountsConfig = readJson(ACCOUNTS_CONFIG_PATH, { providers: {} });
|
|
138
80
|
const accountsState = readJson(ACCOUNTS_STATE_PATH, { providers: {} });
|
|
139
81
|
const claudeUsageSnapshot = readClaudeUsageSnapshot();
|
|
140
|
-
const
|
|
141
|
-
|
|
82
|
+
const codexSnapshot = readCodexRateLimitSnapshot();
|
|
83
|
+
// 설정이 없는 홈에서는 갱신 프로세스를 시작하지 않는다.
|
|
84
|
+
if (
|
|
85
|
+
claudeUsageSnapshot.shouldRefresh &&
|
|
86
|
+
existsSync(join(homedir(), ".claude"))
|
|
87
|
+
) {
|
|
142
88
|
scheduleClaudeUsageRefresh();
|
|
143
89
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
const codexSnapshot = readCodexRateLimitSnapshot();
|
|
150
|
-
const geminiSessionSnapshot = readGeminiSessionSnapshot();
|
|
151
|
-
const geminiAuthContext = buildGeminiAuthContext(geminiAccountId);
|
|
152
|
-
const geminiQuotaSnapshot = readGeminiQuotaSnapshot(
|
|
153
|
-
geminiAccountId,
|
|
154
|
-
geminiAuthContext,
|
|
155
|
-
);
|
|
156
|
-
if (codexSnapshot.shouldRefresh) {
|
|
90
|
+
if (
|
|
91
|
+
showCodex &&
|
|
92
|
+
codexSnapshot.shouldRefresh &&
|
|
93
|
+
(existsSync(join(homedir(), ".codex")) || hasBrokerCodexAccounts())
|
|
94
|
+
) {
|
|
157
95
|
scheduleCodexRateLimitRefresh();
|
|
158
96
|
}
|
|
159
|
-
if (geminiSessionSnapshot.shouldRefresh) {
|
|
160
|
-
scheduleGeminiSessionRefresh();
|
|
161
|
-
}
|
|
162
|
-
if (geminiQuotaSnapshot.shouldRefresh) {
|
|
163
|
-
scheduleGeminiQuotaRefresh(geminiAccountId);
|
|
164
|
-
}
|
|
165
97
|
|
|
166
|
-
// 실측 데이터 추출
|
|
167
98
|
const stdin = await stdinPromise;
|
|
168
|
-
const contextView = buildContextUsageView(stdin
|
|
99
|
+
const contextView = buildContextUsageView(stdin);
|
|
169
100
|
const claudeUsage = claudeUsageSnapshot.data
|
|
170
101
|
? { ...claudeUsageSnapshot.data, stale: claudeUsageSnapshot.isStale }
|
|
171
|
-
:
|
|
172
|
-
const codexEmail = getCodexEmail();
|
|
173
|
-
const geminiEmail = getGeminiEmail();
|
|
174
|
-
const antigravityAccountLabel = getAntigravityAccountLabel();
|
|
102
|
+
: null;
|
|
175
103
|
const codexBuckets = codexSnapshot.buckets;
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const missionBoardState = await getMissionBoardState();
|
|
179
|
-
|
|
180
|
-
// Gemini: 3풀 버킷 추출 (Pro/Flash/Lite — 각 풀 내 모델들은 쿼터 공유)
|
|
181
|
-
const geminiModel = geminiSession?.model || "gemini-3-flash-preview";
|
|
182
|
-
const geminiBuckets = geminiQuota?.buckets || [];
|
|
183
|
-
const geminiBucket =
|
|
184
|
-
geminiBuckets.find((b) => b.modelId === geminiModel) ||
|
|
185
|
-
geminiBuckets.find((b) => b.modelId === "gemini-3-flash-preview") ||
|
|
186
|
-
null;
|
|
187
|
-
const geminiProBucket =
|
|
188
|
-
geminiBuckets.find((b) => GEMINI_PRO_POOL.has(b.modelId)) || null;
|
|
189
|
-
const geminiFlashBucket =
|
|
190
|
-
geminiBuckets.find((b) => GEMINI_FLASH_POOL.has(b.modelId)) || null;
|
|
191
|
-
const geminiLiteBucket =
|
|
192
|
-
geminiBuckets.find((b) => b.modelId?.includes("flash-lite")) || null;
|
|
193
|
-
|
|
194
|
-
// Antigravity 현재 장착 모델 + family 통합 bucket (slot1 = 장착 모델 약어, slot2 = Gn family)
|
|
195
|
-
// slot1 quota: 현재 모델이 Gemini family면 family bucket, 아니면(Claude/GPT-OSS) placeholder.
|
|
196
|
-
const antigravityModel = getAntigravityCurrentModel();
|
|
197
|
-
const antigravityAbbrev = getAntigravityModelAbbrev(antigravityModel);
|
|
198
|
-
const antigravityModelFamily = getAntigravityModelFamily(antigravityModel);
|
|
199
|
-
const antigravityFamilyBucket = deriveGeminiFamilyBucket(geminiBuckets);
|
|
200
|
-
const antigravitySlot1Bucket =
|
|
201
|
-
antigravityModelFamily === "gemini" ? antigravityFamilyBucket : null;
|
|
202
|
-
|
|
203
|
-
// 인디케이터 인식 tier 선택 (stdin + Claude 사용량 기반)
|
|
204
|
-
const CURRENT_TIER = selectTier(stdin, claudeUsageSnapshot.data);
|
|
205
|
-
|
|
206
|
-
// antigravity/gemini 슬롯 표시 판정. nano 와 나머지 tier 가 같은 판정을 쓴다.
|
|
104
|
+
const currentTier = selectTier();
|
|
105
|
+
const geminiEmail = getGeminiEmail();
|
|
207
106
|
const showGeminiRow = shouldRenderGeminiFallbackRow({
|
|
208
107
|
antigravityAllowed,
|
|
209
108
|
antigravityReady,
|
|
210
109
|
geminiEmail,
|
|
211
|
-
geminiBucket,
|
|
212
|
-
geminiSession,
|
|
213
110
|
});
|
|
214
111
|
|
|
215
|
-
|
|
216
|
-
if (CURRENT_TIER === "nano") {
|
|
112
|
+
if (currentTier === "nano") {
|
|
217
113
|
const microLine = getMicroLine(
|
|
218
114
|
contextView,
|
|
219
115
|
claudeUsage,
|
|
220
116
|
codexBuckets,
|
|
221
|
-
antigravityReady
|
|
222
|
-
antigravityReady ? null : geminiBucket,
|
|
223
|
-
antigravityReady ? "a" : "g",
|
|
117
|
+
antigravityReady,
|
|
224
118
|
{ showCodex, showGemini: showGeminiRow },
|
|
225
119
|
);
|
|
226
120
|
process.stdout.write(`\x1b[0m${microLine}\n`);
|
|
227
121
|
return;
|
|
228
122
|
}
|
|
229
123
|
|
|
230
|
-
const
|
|
231
|
-
?
|
|
232
|
-
:
|
|
233
|
-
const geminiQuotaData = {
|
|
234
|
-
type: "gemini",
|
|
235
|
-
quotaBucket: geminiBucket,
|
|
236
|
-
pools: {
|
|
237
|
-
pro: geminiProBucket,
|
|
238
|
-
flash: geminiFlashBucket,
|
|
239
|
-
lite: geminiLiteBucket,
|
|
240
|
-
},
|
|
241
|
-
session: geminiSession,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const rows = [
|
|
245
|
-
...(isClaudeBandActive(stdin?.session_id)
|
|
246
|
-
? []
|
|
247
|
-
: getClaudeRows(CURRENT_TIER, contextView, claudeUsage)),
|
|
248
|
-
];
|
|
249
|
-
|
|
250
|
-
// 정책으로 생략된 행이 있으면 뒤따르는 행의 인덱스가 밀린다. dim 래핑이
|
|
251
|
-
// 하드코딩 인덱스를 쓰지 않도록 실제 위치를 기록해 둔다.
|
|
124
|
+
const rows = isClaudeBandActive(stdin?.session_id)
|
|
125
|
+
? []
|
|
126
|
+
: getClaudeRows(currentTier, contextView, claudeUsage);
|
|
252
127
|
let codexRowIndex = -1;
|
|
253
128
|
if (showCodex) {
|
|
254
129
|
codexRowIndex = rows.length;
|
|
255
130
|
rows.push(
|
|
256
131
|
getProviderRow(
|
|
257
|
-
|
|
132
|
+
currentTier,
|
|
258
133
|
"codex",
|
|
259
134
|
"x",
|
|
260
135
|
codexWhite,
|
|
261
|
-
qosProfile,
|
|
262
136
|
accountsConfig,
|
|
263
137
|
accountsState,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
null,
|
|
138
|
+
codexBuckets ? { type: "codex", buckets: codexBuckets } : null,
|
|
139
|
+
getCodexEmail(),
|
|
267
140
|
),
|
|
268
141
|
);
|
|
269
142
|
}
|
|
270
|
-
|
|
271
143
|
let geminiRowIndex = -1;
|
|
272
144
|
if (showGeminiRow) {
|
|
273
145
|
geminiRowIndex = rows.length;
|
|
274
146
|
rows.push(
|
|
275
147
|
getProviderRow(
|
|
276
|
-
|
|
148
|
+
currentTier,
|
|
277
149
|
antigravityReady ? "antigravity" : "gemini",
|
|
278
150
|
antigravityReady ? "a" : "g",
|
|
279
151
|
geminiBlue,
|
|
280
|
-
qosProfile,
|
|
281
152
|
accountsConfig,
|
|
282
153
|
accountsState,
|
|
283
154
|
antigravityReady
|
|
284
155
|
? {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
gemini_family: antigravityFamilyBucket,
|
|
289
|
-
},
|
|
290
|
-
currentAbbrev: antigravityAbbrev,
|
|
291
|
-
currentModel: antigravityModel,
|
|
292
|
-
currentFamily: antigravityModelFamily,
|
|
156
|
+
currentAbbrev: getAntigravityModelAbbrev(
|
|
157
|
+
getAntigravityCurrentModel(),
|
|
158
|
+
),
|
|
293
159
|
}
|
|
294
|
-
:
|
|
295
|
-
antigravityReady ?
|
|
296
|
-
null,
|
|
160
|
+
: null,
|
|
161
|
+
antigravityReady ? getAntigravityAccountLabel() : geminiEmail,
|
|
297
162
|
),
|
|
298
163
|
);
|
|
299
164
|
}
|
|
300
|
-
|
|
301
|
-
const ctoStatus = readCtoStatus();
|
|
302
|
-
if (ctoStatus) {
|
|
303
|
-
const rightTag =
|
|
304
|
-
CURRENT_TIER === "nano" || CURRENT_TIER === "micro"
|
|
305
|
-
? ""
|
|
306
|
-
: stripAnsi(ctoStatus.rightTag || "").trim();
|
|
307
|
-
rows.push({
|
|
308
|
-
prefix: `${bold(claudeOrange("^"))}:`,
|
|
309
|
-
left: `${dim("cto:")}${stripAnsi(ctoStatus.line)}`,
|
|
310
|
-
right: rightTag ? dim(rightTag) : "",
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// tfx-multi 활성 시 팀 상태 행 추가 (v2.2)
|
|
315
|
-
const teamRow = getTeamRow(CURRENT_TIER);
|
|
165
|
+
const teamRow = getTeamRow(currentTier);
|
|
316
166
|
if (teamRow) rows.push(teamRow);
|
|
317
167
|
|
|
318
|
-
const missionBoard = renderMissionBoard(missionBoardState);
|
|
319
|
-
if (missionBoard) {
|
|
320
|
-
rows.push({
|
|
321
|
-
prefix: bold(claudeOrange("\u25B2")),
|
|
322
|
-
left: missionBoard,
|
|
323
|
-
right: "",
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// 최근 벤치마크 diff → 토큰 요약 행 추가
|
|
328
|
-
const latestDiff = readLatestBenchmarkDiff();
|
|
329
|
-
if (latestDiff) {
|
|
330
|
-
const summary = formatTokenSummary(latestDiff);
|
|
331
|
-
if (summary) {
|
|
332
|
-
rows.push({ prefix: `${dim("$")}:`, left: summary, right: "" });
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// 비활성 프로바이더 dim 처리: 데이터 없으면 전체 줄 dim
|
|
337
|
-
const codexActive = codexBuckets != null;
|
|
338
|
-
const geminiActive =
|
|
339
|
-
(geminiSession?.total || 0) > 0 ||
|
|
340
|
-
geminiBucket != null ||
|
|
341
|
-
geminiProBucket != null ||
|
|
342
|
-
geminiFlashBucket != null;
|
|
343
|
-
|
|
344
168
|
const outputLines = renderAlignedRows(rows);
|
|
345
|
-
|
|
346
|
-
// 비활성 줄 dim 래핑. 정책으로 생략된 행은 인덱스가 -1 이라 건너뛴다.
|
|
347
|
-
if (!codexActive && outputLines[codexRowIndex] != null) {
|
|
169
|
+
if (!codexBuckets && outputLines[codexRowIndex] != null) {
|
|
348
170
|
outputLines[codexRowIndex] = `${DIM}${outputLines[codexRowIndex]}${RESET}`;
|
|
349
171
|
}
|
|
350
|
-
if (!
|
|
351
|
-
outputLines[geminiRowIndex] =
|
|
352
|
-
`${DIM}${outputLines[geminiRowIndex]}${RESET}`;
|
|
172
|
+
if (!antigravityReady && outputLines[geminiRowIndex] != null) {
|
|
173
|
+
outputLines[geminiRowIndex] = dim(outputLines[geminiRowIndex]);
|
|
353
174
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
// 줄별 RESET: Claude Code TUI 스타일 간섭 방지 (색상 밝기 버그 수정)
|
|
359
|
-
const resetedLines = outputLines.map((line) => `\x1b[0m${line}`);
|
|
360
|
-
process.stdout.write(`${leadingBreaks}${resetedLines.join("\n")}\n`);
|
|
175
|
+
// 알림 배너와 TUI 스타일이 HUD 내용에 겹치지 않도록 한다.
|
|
176
|
+
const leadingBreaks = contextView.percent >= 85 ? "\n\n" : "\n";
|
|
177
|
+
const resetLines = outputLines.map((line) => `\x1b[0m${line}`);
|
|
178
|
+
process.stdout.write(`${leadingBreaks}${resetLines.join("\n")}\n`);
|
|
361
179
|
}
|
|
362
180
|
|
|
363
181
|
main().catch(() => {
|
|
364
182
|
process.stdout.write(
|
|
365
|
-
`\x1b[0m${bold(claudeOrange("c"))}: ${dim("5h
|
|
183
|
+
`\x1b[0m${bold(claudeOrange("c"))}: ${dim("5h:--% (n/a) 1w:--% (n/a) | ctx:--%")}\n`,
|
|
366
184
|
);
|
|
367
185
|
});
|
|
368
186
|
|
|
369
|
-
// 프롬프트 위 band
|
|
187
|
+
// 프롬프트 위 band가 같은 정보를 그리는 세션에서는 Claude 행을 생략한다.
|
|
370
188
|
function isClaudeBandActive(sessionId) {
|
|
371
189
|
if (!sessionId) return false;
|
|
372
190
|
try {
|
package/hud/providers/claude.mjs
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
SEVEN_DAY_MS,
|
|
26
26
|
SPAWN_LOCK_TTL_MS,
|
|
27
27
|
} from "../constants.mjs";
|
|
28
|
-
import { readContextMonitorSnapshot } from "../context-monitor.mjs";
|
|
29
28
|
import {
|
|
30
29
|
advanceToNextCycle,
|
|
31
30
|
clampPercent,
|
|
@@ -116,17 +115,14 @@ function normalizeClaudeCredentials(data, source, supportsUsageApi = true) {
|
|
|
116
115
|
};
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
//
|
|
120
|
-
// - CLAUDE_SECURESTORAGE_CONFIG_DIR 가 정의돼 있으면 그 값이 스코프다. 빈 문자열은 기본 디렉터리를
|
|
121
|
-
// 뜻해 접미사를 붙이지 않고, 값이 있으면 NFC 로 정규화한 뒤 해시한다.
|
|
122
|
-
// - 아니면 CLAUDE_CONFIG_DIR 원문(경로 확장·정규화 없이)을 sha256 해 앞 8자리를 붙인다.
|
|
123
|
-
// OMC 격리 세션의 `Claude Code-credentials-<hash>` 항목이 이 규칙으로 만들어진다.
|
|
118
|
+
// 격리된 설정 디렉터리는 기본 Keychain 항목과 다른 서비스 이름을 사용한다.
|
|
124
119
|
export function getKeychainServiceName(env = process.env) {
|
|
125
120
|
const secureDir = env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
|
|
126
121
|
let scope = "";
|
|
127
122
|
if (secureDir !== undefined) {
|
|
128
123
|
scope = secureDir ? secureDir.normalize("NFC") : "";
|
|
129
124
|
} else {
|
|
125
|
+
// CLAUDE_CONFIG_DIR은 정규화하지 않고 빈 문자열은 기본값으로 본다.
|
|
130
126
|
scope = env.CLAUDE_CONFIG_DIR || "";
|
|
131
127
|
}
|
|
132
128
|
if (!scope) return CLAUDE_KEYCHAIN_SERVICE;
|
|
@@ -168,9 +164,7 @@ function readClaudeKeychainRaw(
|
|
|
168
164
|
}
|
|
169
165
|
}
|
|
170
166
|
|
|
171
|
-
//
|
|
172
|
-
// 같은 키만 본다. `-a` 없는 조회는 "계정 없는 항목"이 아니라 같은 service 의 임의 계정 항목을 돌려주고,
|
|
173
|
-
// `add-generic-password` 는 `-a` 가 필수라 그런 항목에는 되쓸 수도 없다(2026-09-21 실측, exit 2).
|
|
167
|
+
// service와 account를 함께 지정해야 다른 계정의 항목을 읽지 않는다.
|
|
174
168
|
export function readClaudeKeychainEntry({
|
|
175
169
|
execFileSyncFn = execFileSync,
|
|
176
170
|
env = process.env,
|
|
@@ -372,9 +366,7 @@ function writeClaudeFileCredentials(
|
|
|
372
366
|
writeCredentialFile(mergeClaudeCredentials(creds, data), filePath);
|
|
373
367
|
}
|
|
374
368
|
|
|
375
|
-
//
|
|
376
|
-
// 계정이 다른 두 저장소가 섞이고(2026-09 실측: 파일=max 계정 만료분, Keychain=pro 계정 현재 로그인),
|
|
377
|
-
// Claude Code 가 실제로 쓰는 Keychain 항목을 옛 계정 토큰으로 덮어쓴다.
|
|
369
|
+
// 자격증명을 읽어 온 저장소에만 써서 다른 계정의 토큰을 덮어쓰지 않는다.
|
|
378
370
|
export function writeBackClaudeCredentials(
|
|
379
371
|
creds,
|
|
380
372
|
{
|
|
@@ -733,7 +725,3 @@ export function scheduleClaudeUsageRefresh() {
|
|
|
733
725
|
});
|
|
734
726
|
}
|
|
735
727
|
}
|
|
736
|
-
|
|
737
|
-
export function readClaudeContextSnapshot() {
|
|
738
|
-
return readContextMonitorSnapshot();
|
|
739
|
-
}
|