triflux 10.9.1 → 10.9.3
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/.claude-plugin/marketplace.json +34 -0
- package/.claude-plugin/plugin.json +22 -0
- package/README.ko.md +14 -24
- package/README.md +7 -12
- package/bin/triflux.mjs +2 -3
- package/config/mcp-registry.json +29 -0
- package/hooks/hook-registry.json +2 -2
- package/hooks/session-start-fast.mjs +21 -10
- package/hub/pipe.mjs +2 -0
- package/hub/server.mjs +19 -0
- package/hub/state.mjs +2 -1
- package/hud/hud-qos-status.mjs +1 -3
- package/hud/renderers.mjs +5 -9
- package/package.json +56 -21
- package/scripts/hub-ensure.mjs +13 -25
- package/scripts/tfx-route.sh +1 -1
- package/skills/tfx-auto/SKILL.md +26 -22
- package/skills/tfx-hub/SKILL.md +273 -85
- package/skills/tfx-hub/skill.json +2 -2
- package/tui/codex-profile.mjs +457 -0
- package/tui/core.mjs +266 -0
- package/tui/doctor.mjs +375 -0
- package/tui/gemini-profile.mjs +299 -0
- package/tui/monitor-data.mjs +152 -0
- package/tui/monitor.mjs +339 -0
- package/tui/setup.mjs +598 -0
- package/CLAUDE.md +0 -212
- package/references/hosts.json +0 -46
- package/skills/tfx-workspace/async-tests/run-tests.sh +0 -203
- package/skills/tfx-workspace/evals/evals.json +0 -79
- package/skills/tfx-workspace/iteration-1/benchmark.json +0 -524
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +0 -25
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +0 -154
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +0 -25
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +0 -126
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +0 -11
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +0 -25
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +0 -119
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +0 -25
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +0 -115
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +0 -10
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +0 -20
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +0 -86
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +0 -20
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +0 -81
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +0 -316
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +0 -352
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/review.html +0 -1325
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +0 -97
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +0 -94
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +0 -12
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +0 -209
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +0 -30
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +0 -193
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/benchmark.json +0 -144
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +0 -13
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +0 -35
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +0 -382
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +0 -35
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +0 -333
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +0 -5
- package/skills/tfx-workspace/iteration-2/review.html +0 -1325
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +0 -217
- package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +0 -77
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +0 -65
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +0 -94
- package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +0 -82
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +0 -133
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +0 -426
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +0 -101
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3
|
+
"name": "triflux",
|
|
4
|
+
"description": "CLI-first multi-model orchestrator — Codex/Gemini/Claude routing with DAG execution, auto-triage, and cost optimization",
|
|
5
|
+
"owner": {
|
|
6
|
+
"name": "tellang"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "triflux",
|
|
11
|
+
"description": "CLI-first multi-model orchestrator for Claude Code. Routes tasks to Codex, Gemini, and Claude CLIs with automatic triage, DAG-based parallel execution, headless psmux sessions, and cost-optimized routing. Includes 41 skills, HUD status bar, hook orchestrator, and shell-based CLI routing.",
|
|
12
|
+
"version": "10.9.3",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "tellang"
|
|
15
|
+
},
|
|
16
|
+
"source": {
|
|
17
|
+
"source": "npm",
|
|
18
|
+
"package": "triflux"
|
|
19
|
+
},
|
|
20
|
+
"category": "productivity",
|
|
21
|
+
"homepage": "https://github.com/tellang/triflux",
|
|
22
|
+
"tags": [
|
|
23
|
+
"multi-model",
|
|
24
|
+
"codex",
|
|
25
|
+
"gemini",
|
|
26
|
+
"cli-routing",
|
|
27
|
+
"orchestration",
|
|
28
|
+
"cost-optimization",
|
|
29
|
+
"dag-execution"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"version": "10.3.2"
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "triflux",
|
|
3
|
+
"version": "10.3.4",
|
|
4
|
+
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "tellang"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/tellang/triflux",
|
|
9
|
+
"homepage": "https://github.com/tellang/triflux",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"claude-code",
|
|
13
|
+
"plugin",
|
|
14
|
+
"codex",
|
|
15
|
+
"gemini",
|
|
16
|
+
"cli-routing",
|
|
17
|
+
"orchestration",
|
|
18
|
+
"multi-model"
|
|
19
|
+
],
|
|
20
|
+
"skills": "./skills/",
|
|
21
|
+
"hooks": "./hooks/hooks.json"
|
|
22
|
+
}
|
package/README.ko.md
CHANGED
|
@@ -37,27 +37,22 @@
|
|
|
37
37
|
|
|
38
38
|
## 빠른 시작
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**Claude Code** (권장):
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
claude plugin add triflux
|
|
42
|
+
```
|
|
43
|
+
/plugin marketplace add tellang/triflux
|
|
44
|
+
/plugin install triflux@tellang
|
|
46
45
|
```
|
|
47
46
|
|
|
48
|
-
**npm
|
|
47
|
+
**npm**:
|
|
49
48
|
|
|
50
49
|
```bash
|
|
51
50
|
npm install -g triflux
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
tfx setup
|
|
58
|
-
```
|
|
53
|
+
`tfx setup`으로 환경을 설정하세요.
|
|
59
54
|
|
|
60
|
-
###
|
|
55
|
+
### 사용법
|
|
61
56
|
|
|
62
57
|
```bash
|
|
63
58
|
# Light — 단일 모델로 빠르게 실행
|
|
@@ -292,27 +287,22 @@ graph TD
|
|
|
292
287
|
|
|
293
288
|
## 빠른 시작
|
|
294
289
|
|
|
295
|
-
|
|
290
|
+
**Claude Code** (권장):
|
|
296
291
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
claude plugin add triflux
|
|
292
|
+
```
|
|
293
|
+
/plugin marketplace add tellang/triflux
|
|
294
|
+
/plugin install triflux@tellang
|
|
301
295
|
```
|
|
302
296
|
|
|
303
|
-
**npm
|
|
297
|
+
**npm**:
|
|
304
298
|
|
|
305
299
|
```bash
|
|
306
300
|
npm install -g triflux
|
|
307
301
|
```
|
|
308
302
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```bash
|
|
312
|
-
tfx setup
|
|
313
|
-
```
|
|
303
|
+
`tfx setup`으로 환경을 설정하세요.
|
|
314
304
|
|
|
315
|
-
###
|
|
305
|
+
### 사용법
|
|
316
306
|
|
|
317
307
|
```bash
|
|
318
308
|
# Light — 단일 모델로 빠르게 실행
|
package/README.md
CHANGED
|
@@ -61,27 +61,22 @@ You don't need to memorize commands. Say what you want in natural language — t
|
|
|
61
61
|
|
|
62
62
|
## Quick Start
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
**Claude Code** (recommended):
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
claude plugin add triflux
|
|
66
|
+
```
|
|
67
|
+
/plugin marketplace add tellang/triflux
|
|
68
|
+
/plugin install triflux@tellang
|
|
70
69
|
```
|
|
71
70
|
|
|
72
|
-
**npm
|
|
71
|
+
**npm**:
|
|
73
72
|
|
|
74
73
|
```bash
|
|
75
74
|
npm install -g triflux
|
|
76
75
|
```
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
tfx setup
|
|
82
|
-
```
|
|
77
|
+
Then run `tfx setup` to configure your environment.
|
|
83
78
|
|
|
84
|
-
###
|
|
79
|
+
### Use
|
|
85
80
|
|
|
86
81
|
```bash
|
|
87
82
|
# 3-party consensus — three models argue, only consensus survives
|
package/bin/triflux.mjs
CHANGED
|
@@ -1047,14 +1047,13 @@ function cmdSetup(options = {}) {
|
|
|
1047
1047
|
}
|
|
1048
1048
|
{
|
|
1049
1049
|
const claudeGuide = ensureGlobalClaudeRoutingSection(CLAUDE_DIR);
|
|
1050
|
-
if (claudeGuide.skipped)
|
|
1050
|
+
if (claudeGuide.skipped && claudeGuide.reason !== "global_sync_disabled")
|
|
1051
1051
|
warn(`CLAUDE.md 라우팅 섹션 확인 실패: ${claudeGuide.reason}`);
|
|
1052
1052
|
else if (
|
|
1053
1053
|
claudeGuide.action === "created" ||
|
|
1054
1054
|
claudeGuide.action === "updated"
|
|
1055
1055
|
)
|
|
1056
1056
|
ok("CLAUDE.md: 전역 triflux 라우팅 요약 갱신");
|
|
1057
|
-
else ok("CLAUDE.md: 전역 triflux 라우팅 요약 유지");
|
|
1058
1057
|
}
|
|
1059
1058
|
|
|
1060
1059
|
// 스킬 동기화 (~/.claude/skills/{name}/SKILL.md)
|
|
@@ -1847,7 +1846,7 @@ async function cmdDoctor(options = {}) {
|
|
|
1847
1846
|
}
|
|
1848
1847
|
{
|
|
1849
1848
|
const claudeGuide = ensureGlobalClaudeRoutingSection(CLAUDE_DIR);
|
|
1850
|
-
if (claudeGuide.skipped)
|
|
1849
|
+
if (claudeGuide.skipped && claudeGuide.reason !== "global_sync_disabled")
|
|
1851
1850
|
warn(`CLAUDE.md 라우팅 섹션 확인 실패: ${claudeGuide.reason}`);
|
|
1852
1851
|
else if (
|
|
1853
1852
|
claudeGuide.action === "created" ||
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "mcp-registry-schema",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"description": "MCP 서버 중앙 레지스트리 — 진실의 원천",
|
|
5
|
+
"defaults": {
|
|
6
|
+
"transport": "hub-url",
|
|
7
|
+
"hub_base": "http://127.0.0.1:27888"
|
|
8
|
+
},
|
|
9
|
+
"servers": {
|
|
10
|
+
"tfx-hub": {
|
|
11
|
+
"transport": "hub-url",
|
|
12
|
+
"url": "http://127.0.0.1:27888/mcp",
|
|
13
|
+
"safe": true,
|
|
14
|
+
"targets": ["claude", "gemini", "codex"],
|
|
15
|
+
"description": "triflux Hub MCP 서버"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"policies": {
|
|
19
|
+
"stdio_action": "replace-with-hub",
|
|
20
|
+
"unknown_server_action": "warn",
|
|
21
|
+
"watched_paths": [
|
|
22
|
+
"~/.gemini/settings.json",
|
|
23
|
+
"~/.codex/config.toml",
|
|
24
|
+
"~/.claude/settings.json",
|
|
25
|
+
"~/.claude/settings.local.json",
|
|
26
|
+
".mcp.json"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
package/hooks/hook-registry.json
CHANGED
|
@@ -167,8 +167,8 @@
|
|
|
167
167
|
"priority": 2,
|
|
168
168
|
"enabled": true,
|
|
169
169
|
"timeout": 8,
|
|
170
|
-
"blocking":
|
|
171
|
-
"description": "tfx-hub 서비스 헬스체크 및 시작"
|
|
170
|
+
"blocking": true,
|
|
171
|
+
"description": "tfx-hub 서비스 헬스체크 및 시작 (BLOCKING — 세션 시작 전 Hub 준비 보장)"
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
174
|
"id": "tfx-preflight-cache",
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// 6개 훅을 1개 node 프로세스 안에서 실행하여 콜드스타트 7회 → 1회로 줄인다.
|
|
5
5
|
//
|
|
6
6
|
// 분류:
|
|
7
|
-
// BLOCKING (직렬, stdout 반환 전 완료): setup.runCritical, mcp-safety-guard.run
|
|
8
|
-
// DEFERRED (병렬, 실패해도 안 죽음):
|
|
7
|
+
// BLOCKING (직렬, stdout 반환 전 완료): setup.runCritical, mcp-safety-guard.run, hub-ensure.run
|
|
8
|
+
// DEFERRED (병렬, 실패해도 안 죽음): mcp-gateway-ensure.run, setup.runDeferred
|
|
9
9
|
// BACKGROUND (fire-and-forget): preflight-cache.run
|
|
10
10
|
//
|
|
11
11
|
// external source 훅 (session-vault 등)은 여전히 execFile로 실행된다.
|
|
@@ -55,6 +55,24 @@ async function runBlocking(stdinData) {
|
|
|
55
55
|
log.error({ hook: "mcp-safety-guard", err: String(err.message || err) }, "hook.failed");
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// 3. hub-ensure — Hub 필수 인프라, BLOCKING으로 실행
|
|
59
|
+
try {
|
|
60
|
+
const t0 = performance.now();
|
|
61
|
+
const hubMod = await importMod(join(SCRIPTS, "hub-ensure.mjs"));
|
|
62
|
+
const result = await hubMod.run(stdinData);
|
|
63
|
+
const dur = performance.now() - t0;
|
|
64
|
+
timings.push({ hook: "hub-ensure", dur_ms: Math.round(dur) });
|
|
65
|
+
if (result?.stdout) output.stdout += result.stdout + "\n";
|
|
66
|
+
if (result?.stderr) output.stderr += result.stderr + "\n";
|
|
67
|
+
if (result?.code !== 0) {
|
|
68
|
+
log.warn({ hook: "hub-ensure", dur_ms: Math.round(dur), code: result?.code }, "hook.warn");
|
|
69
|
+
} else {
|
|
70
|
+
log.info({ hook: "hub-ensure", dur_ms: Math.round(dur) }, "hook.completed");
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
log.error({ hook: "hub-ensure", err: String(err.message || err) }, "hook.failed");
|
|
74
|
+
}
|
|
75
|
+
|
|
58
76
|
return { ...output, timings };
|
|
59
77
|
}
|
|
60
78
|
|
|
@@ -65,13 +83,6 @@ async function runBlocking(stdinData) {
|
|
|
65
83
|
*/
|
|
66
84
|
function runDeferred(stdinData) {
|
|
67
85
|
const tasks = [
|
|
68
|
-
{
|
|
69
|
-
name: "hub-ensure",
|
|
70
|
-
fn: async () => {
|
|
71
|
-
const mod = await importMod(join(SCRIPTS, "hub-ensure.mjs"));
|
|
72
|
-
return mod.run(stdinData);
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
86
|
{
|
|
76
87
|
name: "mcp-gateway-ensure",
|
|
77
88
|
fn: async () => {
|
|
@@ -134,7 +145,7 @@ export async function execute(stdinData, externalHooks = []) {
|
|
|
134
145
|
runBackground(stdinData);
|
|
135
146
|
|
|
136
147
|
const totalDur = performance.now() - totalStart;
|
|
137
|
-
log.info({ total_ms: Math.round(totalDur), blocking_count:
|
|
148
|
+
log.info({ total_ms: Math.round(totalDur), blocking_count: 3, deferred_count: 2, bg_count: 1 }, "session-start.done");
|
|
138
149
|
|
|
139
150
|
return {
|
|
140
151
|
stdout: blocking.stdout,
|
package/hub/pipe.mjs
CHANGED
|
@@ -125,6 +125,7 @@ export function createPipeServer({
|
|
|
125
125
|
heartbeatTtlMs = DEFAULT_HEARTBEAT_TTL_MS,
|
|
126
126
|
delegatorService = null,
|
|
127
127
|
hitlManager = null,
|
|
128
|
+
onActivity = null,
|
|
128
129
|
} = {}) {
|
|
129
130
|
if (!router) {
|
|
130
131
|
throw new Error("router is required");
|
|
@@ -164,6 +165,7 @@ export function createPipeServer({
|
|
|
164
165
|
|
|
165
166
|
function touchClient(client) {
|
|
166
167
|
client.lastHeartbeatMs = Date.now();
|
|
168
|
+
if (onActivity) onActivity();
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
function resolveAgentId(client, payload) {
|
package/hub/server.mjs
CHANGED
|
@@ -580,6 +580,7 @@ export async function startHub({
|
|
|
580
580
|
sessionId,
|
|
581
581
|
delegatorService,
|
|
582
582
|
hitlManager: hitl,
|
|
583
|
+
onActivity: markRequestActivity,
|
|
583
584
|
});
|
|
584
585
|
const assignCallbacks = createAssignCallbackServer({ store, sessionId });
|
|
585
586
|
const tools = createTools(store, router, hitl, pipe);
|
|
@@ -1660,6 +1661,23 @@ if (selfRun) {
|
|
|
1660
1661
|
const port = parseInt(process.env.TFX_HUB_PORT || "27888", 10);
|
|
1661
1662
|
const dbPath = process.env.TFX_HUB_DB || undefined;
|
|
1662
1663
|
|
|
1664
|
+
const cleanupPidFile = () => {
|
|
1665
|
+
try {
|
|
1666
|
+
unlinkSync(PID_FILE);
|
|
1667
|
+
} catch {}
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1670
|
+
process.on("unhandledRejection", (err) => {
|
|
1671
|
+
hubLog.fatal({ err }, "hub.unhandledRejection");
|
|
1672
|
+
cleanupPidFile();
|
|
1673
|
+
process.exit(1);
|
|
1674
|
+
});
|
|
1675
|
+
process.on("uncaughtException", (err) => {
|
|
1676
|
+
hubLog.fatal({ err }, "hub.uncaughtException");
|
|
1677
|
+
cleanupPidFile();
|
|
1678
|
+
process.exit(1);
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1663
1681
|
startHub({ port, dbPath })
|
|
1664
1682
|
.then((info) => {
|
|
1665
1683
|
const shutdown = async (signal) => {
|
|
@@ -1678,6 +1696,7 @@ if (selfRun) {
|
|
|
1678
1696
|
})
|
|
1679
1697
|
.catch((error) => {
|
|
1680
1698
|
hubLog.fatal({ err: error }, "hub.start_failed");
|
|
1699
|
+
cleanupPidFile();
|
|
1681
1700
|
process.exit(1);
|
|
1682
1701
|
});
|
|
1683
1702
|
}
|
package/hub/state.mjs
CHANGED
|
@@ -256,8 +256,9 @@ export async function acquireLock(options = {}) {
|
|
|
256
256
|
const raw = readFileSync(lockPath, "utf8");
|
|
257
257
|
const data = parseJson(raw, {});
|
|
258
258
|
const stats = statSync(lockPath);
|
|
259
|
+
const STALE_LOCK_AGE_MS = 60_000;
|
|
259
260
|
const staleByPid = !isPidAlive(data?.pid);
|
|
260
|
-
const staleByAge = Date.now() - stats.mtimeMs >
|
|
261
|
+
const staleByAge = Date.now() - stats.mtimeMs > STALE_LOCK_AGE_MS;
|
|
261
262
|
if (staleByPid || staleByAge) {
|
|
262
263
|
try {
|
|
263
264
|
unlinkSync(lockPath);
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -138,9 +138,7 @@ async function main() {
|
|
|
138
138
|
// 실측 데이터 추출
|
|
139
139
|
const stdin = await stdinPromise;
|
|
140
140
|
const contextView = buildContextUsageView(stdin, contextSnapshot);
|
|
141
|
-
const claudeUsage = claudeUsageSnapshot.
|
|
142
|
-
? { ...(claudeUsageSnapshot.data || {}), stale: true }
|
|
143
|
-
: claudeUsageSnapshot.data;
|
|
141
|
+
const claudeUsage = claudeUsageSnapshot.data;
|
|
144
142
|
const codexEmail = getCodexEmail();
|
|
145
143
|
const geminiEmail = getGeminiEmail();
|
|
146
144
|
const codexBuckets = codexSnapshot.buckets;
|
package/hud/renderers.mjs
CHANGED
|
@@ -263,8 +263,6 @@ export function getMicroLine(
|
|
|
263
263
|
combinedSvPct,
|
|
264
264
|
) {
|
|
265
265
|
const ctxView = contextView || buildContextUsageView({}, null);
|
|
266
|
-
const staleMarker = claudeUsage?.stale ? ` ${dim("[stale]")}` : "";
|
|
267
|
-
|
|
268
266
|
// Claude 5h/1w
|
|
269
267
|
const cF =
|
|
270
268
|
claudeUsage?.fiveHourPercent != null
|
|
@@ -319,7 +317,7 @@ export function getMicroLine(
|
|
|
319
317
|
`${bold(codexWhite("x"))}${dim(":")}${xVal} ` +
|
|
320
318
|
`${bold(geminiBlue("g"))}${dim(":")}${gVal} ` +
|
|
321
319
|
`${dim("sv:")}${sv} ` +
|
|
322
|
-
`${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}
|
|
320
|
+
`${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
|
|
323
321
|
return truncateAnsi(line, cols);
|
|
324
322
|
}
|
|
325
323
|
|
|
@@ -334,8 +332,6 @@ export function getClaudeRows(
|
|
|
334
332
|
) {
|
|
335
333
|
const ctxView = contextView || buildContextUsageView({}, null);
|
|
336
334
|
const prefix = `${bold(claudeOrange("c"))}:`;
|
|
337
|
-
const staleMarker = claudeUsage?.stale ? ` ${dim("[stale]")}` : "";
|
|
338
|
-
|
|
339
335
|
// 절약 퍼센트
|
|
340
336
|
const svStr = formatSvPct(combinedSvPct || 0);
|
|
341
337
|
const svSuffix = `${dim("sv:")}${svStr}`;
|
|
@@ -388,18 +384,18 @@ export function getClaudeRows(
|
|
|
388
384
|
hasData && weeklyPercent != null
|
|
389
385
|
? colorByProvider(weeklyPercent, `${weeklyPercent}%`, claudeOrange)
|
|
390
386
|
: dim("--");
|
|
391
|
-
const quotaSection = `${fShort}${dim("/")}${wShort}
|
|
387
|
+
const quotaSection = `${fShort}${dim("/")}${wShort}`;
|
|
392
388
|
return [{ prefix, left: quotaSection, right: "" }];
|
|
393
389
|
}
|
|
394
390
|
|
|
395
391
|
if (currentTier === "minimal") {
|
|
396
|
-
const quotaSection = `${dim("5h:")}${fStr} ${dim("1w:")}${wStr}
|
|
392
|
+
const quotaSection = `${dim("5h:")}${fStr} ${dim("1w:")}${wStr}`;
|
|
397
393
|
const right = `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
|
|
398
394
|
return [{ prefix, left: quotaSection, right }];
|
|
399
395
|
}
|
|
400
396
|
|
|
401
397
|
if (currentTier === "compact") {
|
|
402
|
-
const quotaSection = `${dim("5h:")}${fStr} ${dim(fTime)} ${dim("1w:")}${wStr} ${dim(wTime)}
|
|
398
|
+
const quotaSection = `${dim("5h:")}${fStr} ${dim(fTime)} ${dim("1w:")}${wStr} ${dim(wTime)}`;
|
|
403
399
|
const warning = ctxView.warningTag
|
|
404
400
|
? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
|
|
405
401
|
: "";
|
|
@@ -408,7 +404,7 @@ export function getClaudeRows(
|
|
|
408
404
|
}
|
|
409
405
|
|
|
410
406
|
// full tier (>= 120 cols)
|
|
411
|
-
const quotaSection = `${dim("5h:")}${fBar}${fStr} ${dim(fTime)} ${dim("1w:")}${wBar}${wStr} ${dim(wTime)}
|
|
407
|
+
const quotaSection = `${dim("5h:")}${fBar}${fStr} ${dim(fTime)} ${dim("1w:")}${wBar}${wStr} ${dim(wTime)}`;
|
|
412
408
|
const warning = ctxView.warningTag
|
|
413
409
|
? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
|
|
414
410
|
: "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "triflux",
|
|
3
|
-
"version": "10.9.
|
|
3
|
+
"version": "10.9.3",
|
|
4
4
|
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,26 +13,69 @@
|
|
|
13
13
|
"tfx-doctor-tui": "bin/tfx-doctor-tui.mjs",
|
|
14
14
|
"tfx-setup-tui": "bin/tfx-setup-tui.mjs"
|
|
15
15
|
},
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=18.0.0"
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"@triflux/core": "10.0.1",
|
|
21
|
-
"@triflux/remote": "^10.0.0-alpha.1"
|
|
22
|
-
},
|
|
23
16
|
"files": [
|
|
24
17
|
"bin",
|
|
18
|
+
"tui",
|
|
19
|
+
"hub",
|
|
20
|
+
"config",
|
|
25
21
|
"skills",
|
|
22
|
+
"!skills/tfx-workspace",
|
|
23
|
+
"!**/failure-reports",
|
|
24
|
+
"scripts",
|
|
26
25
|
"hooks",
|
|
27
26
|
"hud",
|
|
28
|
-
"scripts",
|
|
29
|
-
"hub",
|
|
30
27
|
"mesh",
|
|
31
|
-
"
|
|
32
|
-
"CLAUDE.md",
|
|
28
|
+
".claude-plugin",
|
|
33
29
|
"README.md",
|
|
30
|
+
"README.ko.md",
|
|
34
31
|
"LICENSE"
|
|
35
32
|
],
|
|
33
|
+
"workspaces": [
|
|
34
|
+
"packages/core",
|
|
35
|
+
"packages/remote",
|
|
36
|
+
"packages/triflux"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"pack": "node scripts/pack.mjs all",
|
|
40
|
+
"pack:core": "node scripts/pack.mjs core",
|
|
41
|
+
"pack:remote": "node scripts/pack.mjs remote",
|
|
42
|
+
"setup": "node scripts/setup.mjs",
|
|
43
|
+
"preinstall": "node scripts/preinstall.mjs",
|
|
44
|
+
"postinstall": "node scripts/setup.mjs",
|
|
45
|
+
"lint": "biome check .",
|
|
46
|
+
"lint:fix": "biome check --fix .",
|
|
47
|
+
"health": "npm test && npm run lint",
|
|
48
|
+
"test": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
49
|
+
"test:unit": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/unit/**/*.test.mjs",
|
|
50
|
+
"test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
|
|
51
|
+
"test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
|
|
52
|
+
"test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
|
|
53
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
|
|
54
|
+
"gen:skill-docs": "node scripts/gen-skill-docs.mjs",
|
|
55
|
+
"gen:skill-manifest": "node scripts/gen-skill-manifest.mjs"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18.0.0"
|
|
59
|
+
},
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/tellang/triflux.git"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/tellang/triflux#readme",
|
|
65
|
+
"author": "tellang",
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
69
|
+
"better-sqlite3": "^12.6.2",
|
|
70
|
+
"pino": "^10.3.1",
|
|
71
|
+
"pino-pretty": "^13.1.3",
|
|
72
|
+
"systray2": "^2.1.4",
|
|
73
|
+
"zod": "^4.0.0"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@biomejs/biome": "^2.0.0",
|
|
77
|
+
"knip": "^6.3.0"
|
|
78
|
+
},
|
|
36
79
|
"keywords": [
|
|
37
80
|
"claude-code",
|
|
38
81
|
"plugin",
|
|
@@ -43,13 +86,5 @@
|
|
|
43
86
|
"multi-model",
|
|
44
87
|
"triflux",
|
|
45
88
|
"tfx"
|
|
46
|
-
]
|
|
47
|
-
"author": "tellang",
|
|
48
|
-
"license": "MIT",
|
|
49
|
-
"homepage": "https://github.com/tellang/triflux#readme",
|
|
50
|
-
"repository": {
|
|
51
|
-
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/tellang/triflux.git",
|
|
53
|
-
"directory": "packages/triflux"
|
|
54
|
-
}
|
|
89
|
+
]
|
|
55
90
|
}
|
package/scripts/hub-ensure.mjs
CHANGED
|
@@ -52,12 +52,12 @@ function resolveHubTarget() {
|
|
|
52
52
|
|
|
53
53
|
async function isHubHealthy(host, port) {
|
|
54
54
|
try {
|
|
55
|
-
const res = await fetch(`${buildHubBaseUrl(host, port)}/
|
|
55
|
+
const res = await fetch(`${buildHubBaseUrl(host, port)}/health`, {
|
|
56
56
|
signal: AbortSignal.timeout(1000),
|
|
57
57
|
});
|
|
58
58
|
if (!res.ok) return false;
|
|
59
59
|
const data = await res.json();
|
|
60
|
-
return data?.
|
|
60
|
+
return data?.ok === true;
|
|
61
61
|
} catch {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
@@ -69,25 +69,13 @@ function startHubDetached(port) {
|
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
const env = { ...process.env, TFX_HUB_PORT: String(port) };
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
windowsHide: true,
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
child.unref();
|
|
83
|
-
} else {
|
|
84
|
-
const child = spawn(process.execPath, [serverPath], {
|
|
85
|
-
env,
|
|
86
|
-
detached: true,
|
|
87
|
-
stdio: "ignore",
|
|
88
|
-
});
|
|
89
|
-
child.unref();
|
|
90
|
-
}
|
|
72
|
+
const child = spawn(process.execPath, [serverPath], {
|
|
73
|
+
env,
|
|
74
|
+
detached: true,
|
|
75
|
+
stdio: "ignore",
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
});
|
|
78
|
+
child.unref();
|
|
91
79
|
return true;
|
|
92
80
|
} catch {
|
|
93
81
|
return false;
|
|
@@ -114,13 +102,13 @@ export async function run(stdinData) {
|
|
|
114
102
|
|
|
115
103
|
const started = startHubDetached(port);
|
|
116
104
|
if (!started) {
|
|
117
|
-
return { code:
|
|
105
|
+
return { code: 1, stdout: "", stderr: "[hub-ensure] hub 시작 실패" };
|
|
118
106
|
}
|
|
119
107
|
|
|
120
|
-
const ready = await waitForHubReady(host, port,
|
|
108
|
+
const ready = await waitForHubReady(host, port, 5000);
|
|
121
109
|
return {
|
|
122
|
-
code: 0,
|
|
123
|
-
stdout: ready ? "hub: ok" : "hub: starting",
|
|
110
|
+
code: ready ? 0 : 2,
|
|
111
|
+
stdout: ready ? "hub: ok" : "hub: starting (timeout)",
|
|
124
112
|
stderr: "",
|
|
125
113
|
};
|
|
126
114
|
}
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -920,7 +920,7 @@ route_agent() {
|
|
|
920
920
|
TFX_CLI_MODE="${TFX_CLI_MODE:-auto}"
|
|
921
921
|
TFX_NO_CLAUDE_NATIVE="${TFX_NO_CLAUDE_NATIVE:-0}"
|
|
922
922
|
TFX_VERIFIER_OVERRIDE="${TFX_VERIFIER_OVERRIDE:-auto}"
|
|
923
|
-
TFX_CODEX_TRANSPORT="${TFX_CODEX_TRANSPORT:-
|
|
923
|
+
TFX_CODEX_TRANSPORT="${TFX_CODEX_TRANSPORT:-auto}"
|
|
924
924
|
# Preflight 캐시 일괄 로드 — CLI/Hub 가용성 + Codex 요금제를 환경변수로 내보냄
|
|
925
925
|
# 하위 프로세스(스킬 포함)가 TFX_CODEX_OK, TFX_GEMINI_OK, TFX_HUB_OK로 즉시 참조 가능
|
|
926
926
|
if [[ -z "${TFX_PREFLIGHT_LOADED:-}" ]]; then
|