@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/hub/token-mode.mjs
CHANGED
|
@@ -2,83 +2,84 @@
|
|
|
2
2
|
// GAP 분석 P2 #7: 심볼 통신 + 약어로 30-50% 토큰 절감
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
+
* @experimental 런타임 미연결 — 향후 통합 예정
|
|
5
6
|
* @experimental
|
|
6
7
|
* @type {Array<{ from: string[], to: string, type: 'symbol'|'abbrev' }>}
|
|
7
8
|
*/
|
|
8
9
|
export const COMPACT_RULES = [
|
|
9
10
|
// ── 심볼 치환 (긴 매칭 우선 정렬) ──
|
|
10
|
-
{ from: [
|
|
11
|
-
{ from: [
|
|
12
|
-
{ from: [
|
|
13
|
-
{ from: [
|
|
14
|
-
{ from: [
|
|
15
|
-
{ from: [
|
|
16
|
-
{ from: [
|
|
17
|
-
{ from: [
|
|
18
|
-
{ from: [
|
|
19
|
-
{ from: [
|
|
20
|
-
{ from: [
|
|
21
|
-
{ from: [
|
|
22
|
-
{ from: [
|
|
23
|
-
{ from: [
|
|
11
|
+
{ from: ["greater than or equal"], to: "≥", type: "symbol" },
|
|
12
|
+
{ from: ["less than or equal"], to: "≤", type: "symbol" },
|
|
13
|
+
{ from: ["results in", "결과적으로"], to: "→", type: "symbol" },
|
|
14
|
+
{ from: ["therefore", "따라서"], to: "∴", type: "symbol" },
|
|
15
|
+
{ from: ["because", "왜냐하면"], to: "∵", type: "symbol" },
|
|
16
|
+
{ from: ["approximately", "대략"], to: "≈", type: "symbol" },
|
|
17
|
+
{ from: ["not equal", "같지 않"], to: "≠", type: "symbol" },
|
|
18
|
+
{ from: ["in progress", "진행 중"], to: "⏳", type: "symbol" },
|
|
19
|
+
{ from: ["completed", "완료"], to: "✓", type: "symbol" },
|
|
20
|
+
{ from: ["success", "성공"], to: "✓", type: "symbol" },
|
|
21
|
+
{ from: ["failure", "실패"], to: "✗", type: "symbol" },
|
|
22
|
+
{ from: ["warning", "경고"], to: "⚠", type: "symbol" },
|
|
23
|
+
{ from: ["error", "에러"], to: "✗", type: "symbol" },
|
|
24
|
+
{ from: ["pending", "대기"], to: "⏸", type: "symbol" },
|
|
24
25
|
|
|
25
26
|
// ── 약어 (긴 매칭 우선 정렬) ──
|
|
26
|
-
{ from: [
|
|
27
|
-
{ from: [
|
|
28
|
-
{ from: [
|
|
29
|
-
{ from: [
|
|
30
|
-
{ from: [
|
|
31
|
-
{ from: [
|
|
32
|
-
{ from: [
|
|
33
|
-
{ from: [
|
|
34
|
-
{ from: [
|
|
35
|
-
{ from: [
|
|
27
|
+
{ from: ["configuration", "설정"], to: "cfg", type: "abbrev" },
|
|
28
|
+
{ from: ["implementation", "구현"], to: "impl", type: "abbrev" },
|
|
29
|
+
{ from: ["architecture", "아키텍처"], to: "arch", type: "abbrev" },
|
|
30
|
+
{ from: ["dependency", "의존성"], to: "dep", type: "abbrev" },
|
|
31
|
+
{ from: ["function", "함수"], to: "fn", type: "abbrev" },
|
|
32
|
+
{ from: ["parameter", "파라미터"], to: "param", type: "abbrev" },
|
|
33
|
+
{ from: ["repository", "저장소"], to: "repo", type: "abbrev" },
|
|
34
|
+
{ from: ["environment", "환경"], to: "env", type: "abbrev" },
|
|
35
|
+
{ from: ["variable", "변수"], to: "var", type: "abbrev" },
|
|
36
|
+
{ from: ["directory", "디렉토리"], to: "dir", type: "abbrev" },
|
|
36
37
|
|
|
37
38
|
// ── 한국어 동사/명령형 약어 ──
|
|
38
|
-
{ from: [
|
|
39
|
-
{ from: [
|
|
40
|
-
{ from: [
|
|
41
|
-
{ from: [
|
|
42
|
-
{ from: [
|
|
43
|
-
{ from: [
|
|
44
|
-
{ from: [
|
|
45
|
-
{ from: [
|
|
39
|
+
{ from: ["구현해"], to: "impl", type: "abbrev" },
|
|
40
|
+
{ from: ["확인해"], to: "check", type: "abbrev" },
|
|
41
|
+
{ from: ["수정해"], to: "fix", type: "abbrev" },
|
|
42
|
+
{ from: ["테스트"], to: "test", type: "abbrev" },
|
|
43
|
+
{ from: ["리뷰"], to: "review", type: "abbrev" },
|
|
44
|
+
{ from: ["분석"], to: "analyze", type: "abbrev" },
|
|
45
|
+
{ from: ["설계"], to: "design", type: "abbrev" },
|
|
46
|
+
{ from: ["문서화"], to: "docs", type: "abbrev" },
|
|
46
47
|
];
|
|
47
48
|
|
|
48
49
|
/** @type {Array<{ from: string[], to: string, type: 'symbol'|'abbrev' }>} */
|
|
49
50
|
export const REVIEW_RULES = [
|
|
50
|
-
{ from: [
|
|
51
|
-
{ from: [
|
|
52
|
-
{ from: [
|
|
53
|
-
{ from: [
|
|
54
|
-
{ from: [
|
|
55
|
-
{ from: [
|
|
56
|
-
{ from: [
|
|
57
|
-
{ from: [
|
|
51
|
+
{ from: ["looks good to me", "lgtm"], to: "✓lgtm", type: "abbrev" },
|
|
52
|
+
{ from: ["needs changes", "수정 필요"], to: "✗chg", type: "abbrev" },
|
|
53
|
+
{ from: ["nitpick", "사소한"], to: "nit", type: "abbrev" },
|
|
54
|
+
{ from: ["blocking", "블로킹"], to: "blk", type: "abbrev" },
|
|
55
|
+
{ from: ["suggestion", "제안"], to: "sug", type: "abbrev" },
|
|
56
|
+
{ from: ["question", "질문"], to: "q", type: "abbrev" },
|
|
57
|
+
{ from: ["approved", "승인"], to: "✓apv", type: "abbrev" },
|
|
58
|
+
{ from: ["request changes", "변경 요청"], to: "✗req", type: "abbrev" },
|
|
58
59
|
];
|
|
59
60
|
|
|
60
61
|
/** @type {Array<{ from: string[], to: string, type: 'symbol'|'abbrev' }>} */
|
|
61
62
|
export const DESIGN_RULES = [
|
|
62
|
-
{ from: [
|
|
63
|
-
{ from: [
|
|
64
|
-
{ from: [
|
|
65
|
-
{ from: [
|
|
66
|
-
{ from: [
|
|
67
|
-
{ from: [
|
|
68
|
-
{ from: [
|
|
69
|
-
{ from: [
|
|
63
|
+
{ from: ["component", "컴포넌트"], to: "cmp", type: "abbrev" },
|
|
64
|
+
{ from: ["interface", "인터페이스"], to: "iface", type: "abbrev" },
|
|
65
|
+
{ from: ["abstraction", "추상화"], to: "abs", type: "abbrev" },
|
|
66
|
+
{ from: ["pattern", "패턴"], to: "ptn", type: "abbrev" },
|
|
67
|
+
{ from: ["dependency injection", "의존성 주입"], to: "di", type: "abbrev" },
|
|
68
|
+
{ from: ["single responsibility", "단일 책임"], to: "srp", type: "abbrev" },
|
|
69
|
+
{ from: ["open closed", "개방 폐쇄"], to: "ocp", type: "abbrev" },
|
|
70
|
+
{ from: ["inheritance", "상속"], to: "inh", type: "abbrev" },
|
|
70
71
|
];
|
|
71
72
|
|
|
72
73
|
/** @type {Array<{ from: string[], to: string, type: 'symbol'|'abbrev' }>} */
|
|
73
74
|
export const DOCS_RULES = [
|
|
74
|
-
{ from: [
|
|
75
|
-
{ from: [
|
|
76
|
-
{ from: [
|
|
77
|
-
{ from: [
|
|
78
|
-
{ from: [
|
|
79
|
-
{ from: [
|
|
80
|
-
{ from: [
|
|
81
|
-
{ from: [
|
|
75
|
+
{ from: ["description", "설명"], to: "desc", type: "abbrev" },
|
|
76
|
+
{ from: ["example", "예시"], to: "ex", type: "abbrev" },
|
|
77
|
+
{ from: ["reference", "참조"], to: "ref", type: "abbrev" },
|
|
78
|
+
{ from: ["introduction", "소개"], to: "intro", type: "abbrev" },
|
|
79
|
+
{ from: ["deprecated", "사용 중단"], to: "dep", type: "abbrev" },
|
|
80
|
+
{ from: ["optional", "선택적"], to: "opt", type: "abbrev" },
|
|
81
|
+
{ from: ["required", "필수"], to: "req", type: "abbrev" },
|
|
82
|
+
{ from: ["returns", "반환"], to: "ret", type: "abbrev" },
|
|
82
83
|
];
|
|
83
84
|
|
|
84
85
|
// ── 프로필 맵 ──
|
|
@@ -86,9 +87,9 @@ export const DOCS_RULES = [
|
|
|
86
87
|
/** @type {Record<string, Array<{ from: string[], to: string, type: 'symbol'|'abbrev' }>>} */
|
|
87
88
|
const PROFILE_MAP = {
|
|
88
89
|
default: COMPACT_RULES,
|
|
89
|
-
review:
|
|
90
|
-
design:
|
|
91
|
-
docs:
|
|
90
|
+
review: [...COMPACT_RULES, ...REVIEW_RULES],
|
|
91
|
+
design: [...COMPACT_RULES, ...DESIGN_RULES],
|
|
92
|
+
docs: [...COMPACT_RULES, ...DOCS_RULES],
|
|
92
93
|
};
|
|
93
94
|
|
|
94
95
|
// ── 내부: 정렬된 치환 쌍 빌드 헬퍼 ──
|
|
@@ -134,7 +135,7 @@ const CODE_BLOCK_RE = /```[\s\S]*?```/g;
|
|
|
134
135
|
*/
|
|
135
136
|
function withCodeProtection(text, transform) {
|
|
136
137
|
const blocks = [];
|
|
137
|
-
const placeholder =
|
|
138
|
+
const placeholder = "\x00CB";
|
|
138
139
|
let idx = 0;
|
|
139
140
|
const masked = text.replace(CODE_BLOCK_RE, (match) => {
|
|
140
141
|
blocks.push(match);
|
|
@@ -145,7 +146,7 @@ function withCodeProtection(text, transform) {
|
|
|
145
146
|
|
|
146
147
|
// 플레이스홀더 복원
|
|
147
148
|
return transformed.replace(
|
|
148
|
-
new RegExp(`${placeholder}(\\d+)${placeholder}`,
|
|
149
|
+
new RegExp(`${placeholder}(\\d+)${placeholder}`, "g"),
|
|
149
150
|
(_, i) => blocks[Number(i)],
|
|
150
151
|
);
|
|
151
152
|
}
|
|
@@ -169,14 +170,14 @@ export function isCompactMode() {
|
|
|
169
170
|
* @returns {string}
|
|
170
171
|
*/
|
|
171
172
|
export function compactify(text) {
|
|
172
|
-
if (!text || typeof text !==
|
|
173
|
+
if (!text || typeof text !== "string") return text ?? "";
|
|
173
174
|
_compactMode = true;
|
|
174
175
|
|
|
175
176
|
return withCodeProtection(text, (segment) => {
|
|
176
177
|
let result = segment;
|
|
177
178
|
for (const { pattern, to } of _compactPairs) {
|
|
178
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g,
|
|
179
|
-
const re = new RegExp(escaped,
|
|
179
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
180
|
+
const re = new RegExp(escaped, "gi");
|
|
180
181
|
result = result.replace(re, to);
|
|
181
182
|
}
|
|
182
183
|
return result;
|
|
@@ -189,8 +190,8 @@ export function compactify(text) {
|
|
|
189
190
|
* @param {'default'|'review'|'design'|'docs'} [profile='default'] — 도메인 프로필
|
|
190
191
|
* @returns {string}
|
|
191
192
|
*/
|
|
192
|
-
export function applyCompactRules(text, profile =
|
|
193
|
-
if (!text || typeof text !==
|
|
193
|
+
export function applyCompactRules(text, profile = "default") {
|
|
194
|
+
if (!text || typeof text !== "string") return text ?? "";
|
|
194
195
|
|
|
195
196
|
const rules = PROFILE_MAP[profile] ?? COMPACT_RULES;
|
|
196
197
|
const pairs = buildCompactPairs(rules);
|
|
@@ -198,8 +199,8 @@ export function applyCompactRules(text, profile = 'default') {
|
|
|
198
199
|
return withCodeProtection(text, (segment) => {
|
|
199
200
|
let result = segment;
|
|
200
201
|
for (const { pattern, to } of pairs) {
|
|
201
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g,
|
|
202
|
-
const re = new RegExp(escaped,
|
|
202
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
203
|
+
const re = new RegExp(escaped, "gi");
|
|
203
204
|
result = result.replace(re, to);
|
|
204
205
|
}
|
|
205
206
|
return result;
|
|
@@ -212,14 +213,14 @@ export function applyCompactRules(text, profile = 'default') {
|
|
|
212
213
|
* @returns {string}
|
|
213
214
|
*/
|
|
214
215
|
export function expand(text) {
|
|
215
|
-
if (!text || typeof text !==
|
|
216
|
+
if (!text || typeof text !== "string") return text ?? "";
|
|
216
217
|
_compactMode = false;
|
|
217
218
|
|
|
218
219
|
return withCodeProtection(text, (segment) => {
|
|
219
220
|
let result = segment;
|
|
220
221
|
for (const { symbol, restore } of _expandPairs) {
|
|
221
|
-
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g,
|
|
222
|
-
const re = new RegExp(escaped,
|
|
222
|
+
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
223
|
+
const re = new RegExp(escaped, "g");
|
|
223
224
|
result = result.replace(re, restore);
|
|
224
225
|
}
|
|
225
226
|
return result;
|
|
@@ -6,9 +6,7 @@ export const DEFAULT_KILL_GRACE_MS = 1000;
|
|
|
6
6
|
|
|
7
7
|
export function toStringList(value) {
|
|
8
8
|
if (!Array.isArray(value)) return [];
|
|
9
|
-
return value
|
|
10
|
-
.map((item) => String(item ?? '').trim())
|
|
11
|
-
.filter(Boolean);
|
|
9
|
+
return value.map((item) => String(item ?? "").trim()).filter(Boolean);
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
export function safeJsonParse(line) {
|
|
@@ -27,8 +25,7 @@ export function createWorkerError(message, details = {}) {
|
|
|
27
25
|
|
|
28
26
|
function sleep(delayMs) {
|
|
29
27
|
return new Promise((resolve) => {
|
|
30
|
-
|
|
31
|
-
timer.unref?.();
|
|
28
|
+
setTimeout(resolve, Math.max(0, delayMs));
|
|
32
29
|
});
|
|
33
30
|
}
|
|
34
31
|
|
|
@@ -51,8 +48,9 @@ export async function withRetry(fn, opts = {}) {
|
|
|
51
48
|
throw error;
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
const delay =
|
|
55
|
-
* (
|
|
51
|
+
const delay =
|
|
52
|
+
Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs) *
|
|
53
|
+
(0.5 + Math.random() * 0.5);
|
|
56
54
|
await sleep(delay);
|
|
57
55
|
}
|
|
58
56
|
}
|
|
@@ -63,7 +61,7 @@ export async function withRetry(fn, opts = {}) {
|
|
|
63
61
|
export function appendTextFragments(value, parts) {
|
|
64
62
|
if (value == null) return;
|
|
65
63
|
|
|
66
|
-
if (typeof value ===
|
|
64
|
+
if (typeof value === "string") {
|
|
67
65
|
const trimmed = value.trim();
|
|
68
66
|
if (trimmed) parts.push(trimmed);
|
|
69
67
|
return;
|
|
@@ -74,11 +72,13 @@ export function appendTextFragments(value, parts) {
|
|
|
74
72
|
return;
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
if (typeof value !==
|
|
75
|
+
if (typeof value !== "object") return;
|
|
78
76
|
|
|
79
|
-
if (typeof value.text ===
|
|
80
|
-
if (typeof value.response ===
|
|
81
|
-
|
|
77
|
+
if (typeof value.text === "string") appendTextFragments(value.text, parts);
|
|
78
|
+
if (typeof value.response === "string")
|
|
79
|
+
appendTextFragments(value.response, parts);
|
|
80
|
+
if (typeof value.result === "string")
|
|
81
|
+
appendTextFragments(value.result, parts);
|
|
82
82
|
if (value.content != null) appendTextFragments(value.content, parts);
|
|
83
83
|
if (value.message != null) appendTextFragments(value.message, parts);
|
|
84
84
|
}
|
|
@@ -86,18 +86,24 @@ export function appendTextFragments(value, parts) {
|
|
|
86
86
|
export function extractText(value) {
|
|
87
87
|
const parts = [];
|
|
88
88
|
appendTextFragments(value, parts);
|
|
89
|
-
return parts.join(
|
|
89
|
+
return parts.join("\n").trim();
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export function terminateChild(child, killGraceMs) {
|
|
93
93
|
if (!child || child.exitCode !== null || child.killed) return;
|
|
94
94
|
|
|
95
|
-
try {
|
|
96
|
-
|
|
95
|
+
try {
|
|
96
|
+
child.stdin.end();
|
|
97
|
+
} catch {}
|
|
98
|
+
try {
|
|
99
|
+
child.kill();
|
|
100
|
+
} catch {}
|
|
97
101
|
|
|
98
102
|
const timer = setTimeout(() => {
|
|
99
103
|
if (child.exitCode === null) {
|
|
100
|
-
try {
|
|
104
|
+
try {
|
|
105
|
+
child.kill("SIGKILL");
|
|
106
|
+
} catch {}
|
|
101
107
|
}
|
|
102
108
|
}, killGraceMs);
|
|
103
109
|
timer.unref?.();
|
package/hud/colors.mjs
CHANGED
|
@@ -12,15 +12,33 @@ export const CLAUDE_ORANGE = "\x1b[38;2;232;112;64m"; // #E87040 (Claude 공식
|
|
|
12
12
|
export const CODEX_WHITE = "\x1b[97m"; // bright white (SGR 37은 Windows Terminal에서 연회색 매핑)
|
|
13
13
|
export const GEMINI_BLUE = "\x1b[38;5;39m";
|
|
14
14
|
|
|
15
|
-
export function green(t) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
export function green(t) {
|
|
16
|
+
return `${GREEN}${t}${RESET}`;
|
|
17
|
+
}
|
|
18
|
+
export function yellow(t) {
|
|
19
|
+
return `${YELLOW}${t}${RESET}`;
|
|
20
|
+
}
|
|
21
|
+
export function red(t) {
|
|
22
|
+
return `${RED}${t}${RESET}`;
|
|
23
|
+
}
|
|
24
|
+
export function cyan(t) {
|
|
25
|
+
return `${CYAN}${t}${RESET}`;
|
|
26
|
+
}
|
|
27
|
+
export function dim(t) {
|
|
28
|
+
return `${DIM}${t}${RESET}`;
|
|
29
|
+
}
|
|
30
|
+
export function bold(t) {
|
|
31
|
+
return `${BOLD}${t}${RESET}`;
|
|
32
|
+
}
|
|
33
|
+
export function claudeOrange(t) {
|
|
34
|
+
return `${CLAUDE_ORANGE}${t}${RESET}`;
|
|
35
|
+
}
|
|
36
|
+
export function codexWhite(t) {
|
|
37
|
+
return `${CODEX_WHITE}${t}${RESET}`;
|
|
38
|
+
}
|
|
39
|
+
export function geminiBlue(t) {
|
|
40
|
+
return `${GEMINI_BLUE}${t}${RESET}`;
|
|
41
|
+
}
|
|
24
42
|
|
|
25
43
|
export function colorByPercent(value, text) {
|
|
26
44
|
if (value >= 85) return red(text);
|
package/hud/constants.mjs
CHANGED
|
@@ -6,26 +6,88 @@ import { join } from "node:path";
|
|
|
6
6
|
|
|
7
7
|
export const VERSION = "2.0";
|
|
8
8
|
|
|
9
|
-
export const QOS_PATH = join(
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export const QOS_PATH = join(
|
|
10
|
+
homedir(),
|
|
11
|
+
".claude",
|
|
12
|
+
"cache",
|
|
13
|
+
"tfx-hub",
|
|
14
|
+
"cli_qos_profile.json",
|
|
15
|
+
);
|
|
16
|
+
export const ACCOUNTS_CONFIG_PATH = join(
|
|
17
|
+
homedir(),
|
|
18
|
+
".claude",
|
|
19
|
+
"cache",
|
|
20
|
+
"tfx-hub",
|
|
21
|
+
"accounts.json",
|
|
22
|
+
);
|
|
23
|
+
export const ACCOUNTS_STATE_PATH = join(
|
|
24
|
+
homedir(),
|
|
25
|
+
".claude",
|
|
26
|
+
"cache",
|
|
27
|
+
"tfx-hub",
|
|
28
|
+
"cli_accounts_state.json",
|
|
29
|
+
);
|
|
12
30
|
|
|
13
31
|
// tfx-multi 상태 (v2.2 HUD 통합)
|
|
14
|
-
export const TEAM_STATE_PATH = join(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
export const TEAM_STATE_PATH = join(
|
|
33
|
+
homedir(),
|
|
34
|
+
".claude",
|
|
35
|
+
"cache",
|
|
36
|
+
"tfx-hub",
|
|
37
|
+
"team-state.json",
|
|
38
|
+
);
|
|
39
|
+
export const CONTEXT_MONITOR_CACHE_PATH = join(
|
|
40
|
+
homedir(),
|
|
41
|
+
".claude",
|
|
42
|
+
"cache",
|
|
43
|
+
"tfx-hub",
|
|
44
|
+
"context-monitor.json",
|
|
45
|
+
);
|
|
46
|
+
export const CONTEXT_MONITOR_LEGACY_PATH = join(
|
|
47
|
+
homedir(),
|
|
48
|
+
".omc",
|
|
49
|
+
"state",
|
|
50
|
+
"context-monitor.json",
|
|
51
|
+
);
|
|
52
|
+
export const CONTEXT_MONITOR_LOG_DIR = join(
|
|
53
|
+
homedir(),
|
|
54
|
+
".claude",
|
|
55
|
+
"cache",
|
|
56
|
+
"tfx-hub",
|
|
57
|
+
"logs",
|
|
58
|
+
);
|
|
18
59
|
|
|
19
60
|
// 원격 프로브 캐시 (tfx-remote-spawn)
|
|
20
|
-
export const REMOTE_ENV_CACHE_DIR = join(
|
|
61
|
+
export const REMOTE_ENV_CACHE_DIR = join(
|
|
62
|
+
homedir(),
|
|
63
|
+
".claude",
|
|
64
|
+
"cache",
|
|
65
|
+
"tfx-hub",
|
|
66
|
+
"remote-env",
|
|
67
|
+
);
|
|
21
68
|
export const REMOTE_ENV_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24시간
|
|
22
69
|
|
|
23
70
|
// Claude OAuth Usage API (api.anthropic.com/api/oauth/usage)
|
|
24
|
-
export const CLAUDE_CREDENTIALS_PATH = join(
|
|
25
|
-
|
|
26
|
-
|
|
71
|
+
export const CLAUDE_CREDENTIALS_PATH = join(
|
|
72
|
+
homedir(),
|
|
73
|
+
".claude",
|
|
74
|
+
".credentials.json",
|
|
75
|
+
);
|
|
76
|
+
export const CLAUDE_USAGE_CACHE_PATH = join(
|
|
77
|
+
homedir(),
|
|
78
|
+
".claude",
|
|
79
|
+
"cache",
|
|
80
|
+
"claude-usage-cache.json",
|
|
81
|
+
);
|
|
82
|
+
export const OMC_PLUGIN_USAGE_CACHE_PATH = join(
|
|
83
|
+
homedir(),
|
|
84
|
+
".claude",
|
|
85
|
+
"cache",
|
|
86
|
+
"tfx-hub",
|
|
87
|
+
"plugin-usage-cache.json",
|
|
88
|
+
);
|
|
27
89
|
export const CLAUDE_USAGE_STALE_MS_SOLO = 5 * 60 * 1000; // OMC 없을 때: 5분 캐시
|
|
28
|
-
export const CLAUDE_USAGE_STALE_MS_WITH_OMC = 15 * 60 * 1000; // OMC 있을 때: 15분
|
|
90
|
+
export const CLAUDE_USAGE_STALE_MS_WITH_OMC = 15 * 60 * 1000; // OMC 있을 때: 15분
|
|
29
91
|
export const CLAUDE_USAGE_429_BACKOFF_MS = 10 * 60 * 1000; // 429 에러 시 10분 backoff
|
|
30
92
|
export const GEMINI_429_BASE_DELAY_MS = 2000;
|
|
31
93
|
export const GEMINI_429_MAX_RETRIES = 3;
|
|
@@ -38,27 +100,115 @@ export const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
|
38
100
|
export const DEFAULT_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
39
101
|
|
|
40
102
|
export const CODEX_AUTH_PATH = join(homedir(), ".codex", "auth.json");
|
|
41
|
-
export const CODEX_QUOTA_CACHE_PATH = join(
|
|
42
|
-
|
|
103
|
+
export const CODEX_QUOTA_CACHE_PATH = join(
|
|
104
|
+
homedir(),
|
|
105
|
+
".claude",
|
|
106
|
+
"cache",
|
|
107
|
+
"codex-rate-limits-cache.json",
|
|
108
|
+
);
|
|
109
|
+
export const CODEX_QUOTA_STALE_MS = 30 * 1000; // 30초
|
|
43
110
|
export const CODEX_MIN_BUCKETS = 2;
|
|
44
111
|
|
|
112
|
+
// Spawn lock (중복 refresh 방지)
|
|
113
|
+
export const CLAUDE_REFRESH_LOCK_PATH = join(
|
|
114
|
+
homedir(),
|
|
115
|
+
".claude",
|
|
116
|
+
"cache",
|
|
117
|
+
".claude-refresh-lock",
|
|
118
|
+
);
|
|
119
|
+
export const CODEX_REFRESH_LOCK_PATH = join(
|
|
120
|
+
homedir(),
|
|
121
|
+
".claude",
|
|
122
|
+
"cache",
|
|
123
|
+
".codex-refresh-lock",
|
|
124
|
+
);
|
|
125
|
+
export const GEMINI_QUOTA_REFRESH_LOCK_PATH = join(
|
|
126
|
+
homedir(),
|
|
127
|
+
".claude",
|
|
128
|
+
"cache",
|
|
129
|
+
".gemini-quota-refresh-lock",
|
|
130
|
+
);
|
|
131
|
+
export const GEMINI_SESSION_REFRESH_LOCK_PATH = join(
|
|
132
|
+
homedir(),
|
|
133
|
+
".claude",
|
|
134
|
+
"cache",
|
|
135
|
+
".gemini-session-refresh-lock",
|
|
136
|
+
);
|
|
137
|
+
export const SPAWN_LOCK_TTL_MS = 30 * 1000; // 30초 spawn dedup
|
|
138
|
+
|
|
45
139
|
// Gemini 쿼터 API 관련
|
|
46
140
|
export const GEMINI_OAUTH_PATH = join(homedir(), ".gemini", "oauth_creds.json");
|
|
47
|
-
export const GEMINI_QUOTA_CACHE_PATH = join(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
141
|
+
export const GEMINI_QUOTA_CACHE_PATH = join(
|
|
142
|
+
homedir(),
|
|
143
|
+
".claude",
|
|
144
|
+
"cache",
|
|
145
|
+
"gemini-quota-cache.json",
|
|
146
|
+
);
|
|
147
|
+
export const GEMINI_PROJECT_CACHE_PATH = join(
|
|
148
|
+
homedir(),
|
|
149
|
+
".claude",
|
|
150
|
+
"cache",
|
|
151
|
+
"gemini-project-id.json",
|
|
152
|
+
);
|
|
153
|
+
export const GEMINI_SESSION_CACHE_PATH = join(
|
|
154
|
+
homedir(),
|
|
155
|
+
".claude",
|
|
156
|
+
"cache",
|
|
157
|
+
"gemini-session-cache.json",
|
|
158
|
+
);
|
|
159
|
+
export const TFX_PREFLIGHT_CACHE_PATH = join(
|
|
160
|
+
homedir(),
|
|
161
|
+
".claude",
|
|
162
|
+
"cache",
|
|
163
|
+
"tfx-preflight.json",
|
|
164
|
+
);
|
|
165
|
+
export const GEMINI_RPM_TRACKER_PATH = join(
|
|
166
|
+
homedir(),
|
|
167
|
+
".claude",
|
|
168
|
+
"cache",
|
|
169
|
+
"gemini-rpm-tracker.json",
|
|
170
|
+
);
|
|
171
|
+
export const SV_ACCUMULATOR_PATH = join(
|
|
172
|
+
homedir(),
|
|
173
|
+
".claude",
|
|
174
|
+
"cache",
|
|
175
|
+
"sv-accumulator.json",
|
|
176
|
+
);
|
|
52
177
|
// 이전 .omc/ 경로 fallback (기존 환경 호환)
|
|
53
|
-
export const LEGACY_GEMINI_QUOTA_CACHE = join(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
178
|
+
export const LEGACY_GEMINI_QUOTA_CACHE = join(
|
|
179
|
+
homedir(),
|
|
180
|
+
".omc",
|
|
181
|
+
"state",
|
|
182
|
+
"gemini_quota_cache.json",
|
|
183
|
+
);
|
|
184
|
+
export const LEGACY_GEMINI_PROJECT_CACHE = join(
|
|
185
|
+
homedir(),
|
|
186
|
+
".omc",
|
|
187
|
+
"state",
|
|
188
|
+
"gemini_project_id.json",
|
|
189
|
+
);
|
|
190
|
+
export const LEGACY_GEMINI_SESSION_CACHE = join(
|
|
191
|
+
homedir(),
|
|
192
|
+
".omc",
|
|
193
|
+
"state",
|
|
194
|
+
"gemini_session_tokens_cache.json",
|
|
195
|
+
);
|
|
196
|
+
export const LEGACY_GEMINI_RPM_TRACKER = join(
|
|
197
|
+
homedir(),
|
|
198
|
+
".omc",
|
|
199
|
+
"state",
|
|
200
|
+
"gemini_rpm_tracker.json",
|
|
201
|
+
);
|
|
202
|
+
export const LEGACY_SV_ACCUMULATOR = join(
|
|
203
|
+
homedir(),
|
|
204
|
+
".omc",
|
|
205
|
+
"state",
|
|
206
|
+
"sv-accumulator.json",
|
|
207
|
+
);
|
|
58
208
|
|
|
59
209
|
export const GEMINI_RPM_WINDOW_MS = 60 * 1000; // 60초 슬라이딩 윈도우
|
|
60
210
|
export const GEMINI_QUOTA_STALE_MS = 5 * 60 * 1000; // 5분
|
|
61
|
-
export const GEMINI_SESSION_STALE_MS =
|
|
211
|
+
export const GEMINI_SESSION_STALE_MS = 30 * 1000; // 30초
|
|
62
212
|
export const GEMINI_API_TIMEOUT_MS = 3000; // 3초
|
|
63
213
|
|
|
64
214
|
export const ACCOUNT_LABEL_WIDTH = 10;
|
|
@@ -84,5 +234,12 @@ export const ROWS_BUDGET_MEDIUM = 28;
|
|
|
84
234
|
export const ROWS_BUDGET_SMALL = 22;
|
|
85
235
|
|
|
86
236
|
// Gemini Pro 풀 공유 그룹: 같은 remainingFraction을 공유하는 모델 ID들
|
|
87
|
-
export const GEMINI_PRO_POOL = new Set([
|
|
88
|
-
|
|
237
|
+
export const GEMINI_PRO_POOL = new Set([
|
|
238
|
+
"gemini-2.5-pro",
|
|
239
|
+
"gemini-3-pro-preview",
|
|
240
|
+
"gemini-3.1-pro-preview",
|
|
241
|
+
]);
|
|
242
|
+
export const GEMINI_FLASH_POOL = new Set([
|
|
243
|
+
"gemini-2.5-flash",
|
|
244
|
+
"gemini-3-flash-preview",
|
|
245
|
+
]);
|