triflux 4.0.6 → 4.1.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/bin/triflux.mjs +53 -15
- package/hub/tray.mjs +56 -13
- package/package.json +1 -1
- package/scripts/setup.mjs +25 -0
package/bin/triflux.mjs
CHANGED
|
@@ -591,6 +591,56 @@ function getSetupSyncTargets() {
|
|
|
591
591
|
dst: join(CLAUDE_DIR, "scripts", "tfx-batch-stats.mjs"),
|
|
592
592
|
label: "tfx-batch-stats.mjs",
|
|
593
593
|
},
|
|
594
|
+
{
|
|
595
|
+
src: join(PKG_ROOT, "scripts", "lib", "mcp-filter.mjs"),
|
|
596
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "mcp-filter.mjs"),
|
|
597
|
+
label: "lib/mcp-filter.mjs",
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
src: join(PKG_ROOT, "scripts", "lib", "mcp-server-catalog.mjs"),
|
|
601
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "mcp-server-catalog.mjs"),
|
|
602
|
+
label: "lib/mcp-server-catalog.mjs",
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
src: join(PKG_ROOT, "scripts", "lib", "keyword-rules.mjs"),
|
|
606
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "keyword-rules.mjs"),
|
|
607
|
+
label: "lib/keyword-rules.mjs",
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
src: join(PKG_ROOT, "scripts", "tfx-route-worker.mjs"),
|
|
611
|
+
dst: join(CLAUDE_DIR, "scripts", "tfx-route-worker.mjs"),
|
|
612
|
+
label: "tfx-route-worker.mjs",
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
src: join(PKG_ROOT, "hub", "workers", "codex-mcp.mjs"),
|
|
616
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "codex-mcp.mjs"),
|
|
617
|
+
label: "hub/workers/codex-mcp.mjs",
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
src: join(PKG_ROOT, "hub", "workers", "delegator-mcp.mjs"),
|
|
621
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "delegator-mcp.mjs"),
|
|
622
|
+
label: "hub/workers/delegator-mcp.mjs",
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
src: join(PKG_ROOT, "hub", "workers", "interface.mjs"),
|
|
626
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "interface.mjs"),
|
|
627
|
+
label: "hub/workers/interface.mjs",
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
src: join(PKG_ROOT, "hub", "workers", "gemini-worker.mjs"),
|
|
631
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "gemini-worker.mjs"),
|
|
632
|
+
label: "hub/workers/gemini-worker.mjs",
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
src: join(PKG_ROOT, "hub", "workers", "claude-worker.mjs"),
|
|
636
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "claude-worker.mjs"),
|
|
637
|
+
label: "hub/workers/claude-worker.mjs",
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
src: join(PKG_ROOT, "hub", "workers", "factory.mjs"),
|
|
641
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "factory.mjs"),
|
|
642
|
+
label: "hub/workers/factory.mjs",
|
|
643
|
+
},
|
|
594
644
|
];
|
|
595
645
|
}
|
|
596
646
|
|
|
@@ -912,21 +962,9 @@ async function cmdDoctor(options = {}) {
|
|
|
912
962
|
// ── fix 모드: 파일 동기화 + 캐시 정리 후 진단 ──
|
|
913
963
|
if (fix) {
|
|
914
964
|
section("Auto Fix");
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
"tfx-route.sh"
|
|
919
|
-
);
|
|
920
|
-
syncFile(
|
|
921
|
-
join(PKG_ROOT, "hud", "hud-qos-status.mjs"),
|
|
922
|
-
join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"),
|
|
923
|
-
"hud-qos-status.mjs"
|
|
924
|
-
);
|
|
925
|
-
syncFile(
|
|
926
|
-
join(PKG_ROOT, "scripts", "notion-read.mjs"),
|
|
927
|
-
join(CLAUDE_DIR, "scripts", "notion-read.mjs"),
|
|
928
|
-
"notion-read.mjs"
|
|
929
|
-
);
|
|
965
|
+
for (const target of getSetupSyncTargets()) {
|
|
966
|
+
syncFile(target.src, target.dst, target.label);
|
|
967
|
+
}
|
|
930
968
|
// 스킬 동기화
|
|
931
969
|
const fSkillsSrc = join(PKG_ROOT, "skills");
|
|
932
970
|
const fSkillsDst = join(CLAUDE_DIR, "skills");
|
package/hub/tray.mjs
CHANGED
|
@@ -3,13 +3,31 @@
|
|
|
3
3
|
import _SysTrayModule from "systray2";
|
|
4
4
|
const SysTray = _SysTrayModule.default || _SysTrayModule;
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
|
-
import { readFileSync } from "node:fs";
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { join, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const HUB_PID_FILE = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
|
|
12
|
+
const DEFAULT_HUB_PORT = "27888";
|
|
13
|
+
|
|
14
|
+
function getHubBaseUrl() {
|
|
15
|
+
if (process.env.TFX_HUB_URL) return process.env.TFX_HUB_URL.replace(/\/+$/, "");
|
|
16
|
+
try {
|
|
17
|
+
const info = JSON.parse(readFileSync(HUB_PID_FILE, "utf8"));
|
|
18
|
+
if (info.port) return `http://${info.host || "127.0.0.1"}:${info.port}`;
|
|
19
|
+
} catch {}
|
|
20
|
+
const port = process.env.TFX_HUB_PORT || DEFAULT_HUB_PORT;
|
|
21
|
+
return `http://127.0.0.1:${port}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getDashboardUrl() {
|
|
25
|
+
return `${getHubBaseUrl()}/dashboard`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getHubStatusUrl() {
|
|
29
|
+
return `${getHubBaseUrl()}/status`;
|
|
30
|
+
}
|
|
13
31
|
const POLL_INTERVAL_MS = 10_000;
|
|
14
32
|
const HUB_TIMEOUT_MS = 3_000;
|
|
15
33
|
const AIMD_INITIAL = 3;
|
|
@@ -115,7 +133,7 @@ function getClaudePercent() {
|
|
|
115
133
|
|
|
116
134
|
async function getHubStatusLabel() {
|
|
117
135
|
try {
|
|
118
|
-
const response = await fetch(
|
|
136
|
+
const response = await fetch(getHubStatusUrl(), {
|
|
119
137
|
signal: AbortSignal.timeout(HUB_TIMEOUT_MS),
|
|
120
138
|
});
|
|
121
139
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
@@ -138,18 +156,40 @@ function formatMenuPercent(value) {
|
|
|
138
156
|
}
|
|
139
157
|
|
|
140
158
|
function buildTooltip(snapshot) {
|
|
141
|
-
|
|
159
|
+
const hubTag = snapshot.hubLabel.startsWith("Hub 미") ? "H:off" : "H:on";
|
|
160
|
+
return `tfx AIMD:${snapshot.aimd}/10 | C:${formatTooltipPercent(snapshot.claude)} X:${formatTooltipPercent(snapshot.codex)} G:${formatTooltipPercent(snapshot.gemini)} ${hubTag}`;
|
|
142
161
|
}
|
|
143
162
|
|
|
144
163
|
function buildUsageTitle(snapshot) {
|
|
145
164
|
return `C: ${formatMenuPercent(snapshot.claude)} | X: ${formatMenuPercent(snapshot.codex)} | G: ${formatMenuPercent(snapshot.gemini)}`;
|
|
146
165
|
}
|
|
147
166
|
|
|
167
|
+
function findChromePath() {
|
|
168
|
+
const candidates = [
|
|
169
|
+
join(process.env.ProgramFiles || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
170
|
+
join(process.env["ProgramFiles(x86)"] || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
171
|
+
join(process.env.LOCALAPPDATA || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
172
|
+
];
|
|
173
|
+
for (const p of candidates) {
|
|
174
|
+
try { if (existsSync(p)) return p; } catch {}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
148
179
|
function openDashboard() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
180
|
+
const url = getDashboardUrl();
|
|
181
|
+
const shell = process.env.ComSpec || "cmd.exe";
|
|
182
|
+
const chrome = findChromePath();
|
|
183
|
+
if (chrome) {
|
|
184
|
+
// Chrome --app: 주소바/탭 없는 앱 윈도우로 열기
|
|
185
|
+
exec(`start "" "${chrome}" "--app=${url}"`, { shell, windowsHide: true }, (err) => {
|
|
186
|
+
if (err) {
|
|
187
|
+
exec(`start "" "${url}"`, { shell, windowsHide: true }, () => {});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
exec(`start "" "${url}"`, { shell, windowsHide: true }, () => {});
|
|
192
|
+
}
|
|
153
193
|
}
|
|
154
194
|
|
|
155
195
|
const openDashboardItem = {
|
|
@@ -162,19 +202,22 @@ const openDashboardItem = {
|
|
|
162
202
|
const aimdItem = {
|
|
163
203
|
title: "AIMD: 3/10",
|
|
164
204
|
tooltip: "최근 30분 AIMD 동시 워커",
|
|
165
|
-
enabled:
|
|
205
|
+
enabled: true,
|
|
206
|
+
click: openDashboard,
|
|
166
207
|
};
|
|
167
208
|
|
|
168
209
|
const quotaItem = {
|
|
169
210
|
title: "C: --% | X: --% | G: --%",
|
|
170
211
|
tooltip: "Claude | Codex | Gemini 사용률",
|
|
171
|
-
enabled:
|
|
212
|
+
enabled: true,
|
|
213
|
+
click: openDashboard,
|
|
172
214
|
};
|
|
173
215
|
|
|
174
216
|
const hubItem = {
|
|
175
217
|
title: "Hub 미연결",
|
|
176
218
|
tooltip: "Hub 연결 상태",
|
|
177
|
-
enabled:
|
|
219
|
+
enabled: true,
|
|
220
|
+
click: openDashboard,
|
|
178
221
|
};
|
|
179
222
|
|
|
180
223
|
const refreshItem = {
|
|
@@ -198,7 +241,7 @@ const exitItem = {
|
|
|
198
241
|
const menu = {
|
|
199
242
|
icon: TRAY_ICON_BASE64,
|
|
200
243
|
title: "tfx",
|
|
201
|
-
tooltip: "tfx AIMD:3/10 | C:--% X:--% G:--%",
|
|
244
|
+
tooltip: "tfx AIMD:3/10 | C:--% X:--% G:--% H:off",
|
|
202
245
|
items: [
|
|
203
246
|
openDashboardItem,
|
|
204
247
|
SysTray.separator,
|
package/package.json
CHANGED
package/scripts/setup.mjs
CHANGED
|
@@ -92,6 +92,31 @@ const SYNC_MAP = [
|
|
|
92
92
|
dst: join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"),
|
|
93
93
|
label: "hud-qos-status.mjs",
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
src: join(PLUGIN_ROOT, "scripts", "notion-read.mjs"),
|
|
97
|
+
dst: join(CLAUDE_DIR, "scripts", "notion-read.mjs"),
|
|
98
|
+
label: "notion-read.mjs",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
src: join(PLUGIN_ROOT, "scripts", "tfx-batch-stats.mjs"),
|
|
102
|
+
dst: join(CLAUDE_DIR, "scripts", "tfx-batch-stats.mjs"),
|
|
103
|
+
label: "tfx-batch-stats.mjs",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
src: join(PLUGIN_ROOT, "scripts", "lib", "mcp-filter.mjs"),
|
|
107
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "mcp-filter.mjs"),
|
|
108
|
+
label: "lib/mcp-filter.mjs",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
src: join(PLUGIN_ROOT, "scripts", "lib", "mcp-server-catalog.mjs"),
|
|
112
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "mcp-server-catalog.mjs"),
|
|
113
|
+
label: "lib/mcp-server-catalog.mjs",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
src: join(PLUGIN_ROOT, "scripts", "lib", "keyword-rules.mjs"),
|
|
117
|
+
dst: join(CLAUDE_DIR, "scripts", "lib", "keyword-rules.mjs"),
|
|
118
|
+
label: "lib/keyword-rules.mjs",
|
|
119
|
+
},
|
|
95
120
|
];
|
|
96
121
|
|
|
97
122
|
function getVersion(filePath) {
|