triflux 6.0.3 → 6.0.5
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/team/headless.mjs +18 -11
- package/hub/team/psmux.mjs +11 -2
- package/package.json +66 -66
- package/scripts/preinstall.mjs +16 -0
package/hub/team/headless.mjs
CHANGED
|
@@ -128,7 +128,9 @@ export async function runHeadless(sessionName, assignments, opts = {}) {
|
|
|
128
128
|
} else {
|
|
129
129
|
// ─── 기존 모드: 모든 pane을 한 번에 생성 ───
|
|
130
130
|
const paneCount = assignments.length + 1;
|
|
131
|
-
|
|
131
|
+
// A2b fix: 2x2 레이아웃은 최대 4 pane — 초과 시 tiled로 자동 전환
|
|
132
|
+
const effectiveLayout = (layout === "2x2" && paneCount > 4) ? "tiled" : layout;
|
|
133
|
+
const session = createPsmuxSession(sessionName, { layout: effectiveLayout, paneCount });
|
|
132
134
|
applyTrifluxTheme(sessionName);
|
|
133
135
|
if (safeProgress) safeProgress({ type: "session_created", sessionName, panes: session.panes });
|
|
134
136
|
|
|
@@ -169,7 +171,9 @@ export async function runHeadless(sessionName, assignments, opts = {}) {
|
|
|
169
171
|
};
|
|
170
172
|
}
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
// dispatch 시 확정된 logPath를 전달 — 셸이 pane 타이틀 변경해도 캡처 로그 매칭 유지
|
|
175
|
+
if (d.logPath) pollOpts.logPath = d.logPath;
|
|
176
|
+
const completion = await waitForCompletion(sessionName, d.paneId || d.paneName, d.token, timeoutSec, pollOpts);
|
|
173
177
|
|
|
174
178
|
const output = completion.matched
|
|
175
179
|
? readResult(d.resultFile, d.paneId)
|
|
@@ -245,6 +249,8 @@ export function applyTrifluxTheme(sessionName) {
|
|
|
245
249
|
["pane-border-style", "fg=#45475a"],
|
|
246
250
|
// Status bar 위치
|
|
247
251
|
["status-position", "bottom"],
|
|
252
|
+
// 셸이 pane 타이틀을 변경하는 것 방지 (캡처 로그 경로 안정성)
|
|
253
|
+
["allow-rename", "off"],
|
|
248
254
|
];
|
|
249
255
|
for (const [key, value] of opts) {
|
|
250
256
|
try { psmuxExec(["set-option", "-t", sessionName, key, value]); } catch { /* 무시 */ }
|
|
@@ -270,18 +276,19 @@ export function autoAttachTerminal(sessionName, opts = {}) {
|
|
|
270
276
|
return false; // wt.exe 미설치 — 사용자에게 수동 attach 안내 필요
|
|
271
277
|
}
|
|
272
278
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
} catch {
|
|
279
|
+
// PowerShell 래핑 — wt가 psmux를 파일로 인식하는 문제 방지
|
|
280
|
+
// pwsh.exe (PS7) 우선, 없으면 powershell.exe (PS5.1) fallback
|
|
281
|
+
const shells = ["pwsh.exe", "powershell.exe"];
|
|
282
|
+
for (const shell of shells) {
|
|
278
283
|
try {
|
|
279
|
-
execFileSync("wt.exe", [
|
|
284
|
+
execFileSync("wt.exe", [
|
|
285
|
+
"nt", shell, "-NoExit", "-Command",
|
|
286
|
+
`psmux attach -t ${sessionName}`,
|
|
287
|
+
], { stdio: "ignore" });
|
|
280
288
|
return true;
|
|
281
|
-
} catch {
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
289
|
+
} catch { /* 다음 shell 시도 */ }
|
|
284
290
|
}
|
|
291
|
+
return false;
|
|
285
292
|
}
|
|
286
293
|
|
|
287
294
|
/**
|
package/hub/team/psmux.mjs
CHANGED
|
@@ -667,7 +667,10 @@ export async function waitForPattern(sessionName, paneNameOrTarget, pattern, tim
|
|
|
667
667
|
}
|
|
668
668
|
|
|
669
669
|
const paneName = pane.title || paneNameOrTarget;
|
|
670
|
-
|
|
670
|
+
// opts.logPath: dispatch 시 확정된 캡처 로그 경로 직접 지정 (타이틀 변경 내성)
|
|
671
|
+
const logPath = (opts.logPath && existsSync(opts.logPath))
|
|
672
|
+
? opts.logPath
|
|
673
|
+
: getCaptureLogPath(sessionName, paneName);
|
|
671
674
|
if (!existsSync(logPath)) {
|
|
672
675
|
throw new Error(`캡처 로그가 없습니다. 먼저 startCapture(${sessionName}, ${paneName})를 호출하세요.`);
|
|
673
676
|
}
|
|
@@ -679,7 +682,13 @@ export async function waitForPattern(sessionName, paneNameOrTarget, pattern, tim
|
|
|
679
682
|
while (Date.now() <= deadline) {
|
|
680
683
|
// E4 크래시 복구: capture 실패 시 세션 생존 체크
|
|
681
684
|
try {
|
|
682
|
-
|
|
685
|
+
if (opts.logPath) {
|
|
686
|
+
// logPath 직접 지정 시 — 셸 타이틀 변경과 무관하게 올바른 파일에 기록
|
|
687
|
+
const snapshot = psmuxExec(["capture-pane", "-t", pane.paneId, "-p"]);
|
|
688
|
+
writeFileSync(logPath, snapshot, "utf8");
|
|
689
|
+
} else {
|
|
690
|
+
refreshCaptureSnapshot(sessionName, pane.paneId);
|
|
691
|
+
}
|
|
683
692
|
} catch {
|
|
684
693
|
if (!psmuxSessionExists(sessionName)) {
|
|
685
694
|
return {
|
package/package.json
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "triflux",
|
|
3
|
-
"version": "6.0.
|
|
4
|
-
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"triflux": "bin/triflux.mjs",
|
|
8
|
-
"tfx": "bin/triflux.mjs",
|
|
9
|
-
"tfl": "bin/triflux.mjs",
|
|
10
|
-
"tfx-setup": "bin/tfx-setup.mjs",
|
|
11
|
-
"tfx-doctor": "bin/tfx-doctor.mjs"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"bin",
|
|
15
|
-
"hub",
|
|
16
|
-
"skills",
|
|
17
|
-
"!skills/tfx-workspace",
|
|
18
|
-
"!**/failure-reports",
|
|
19
|
-
"scripts",
|
|
20
|
-
"hooks",
|
|
21
|
-
"hud",
|
|
22
|
-
".claude-plugin",
|
|
23
|
-
".mcp.json",
|
|
24
|
-
"README.md",
|
|
25
|
-
"README.ko.md",
|
|
26
|
-
"LICENSE"
|
|
27
|
-
],
|
|
28
|
-
"scripts": {
|
|
29
|
-
"setup": "node scripts/setup.mjs",
|
|
30
|
-
"preinstall": "node scripts/preinstall.mjs",
|
|
31
|
-
"postinstall": "node scripts/setup.mjs",
|
|
32
|
-
"test": "node --test --test-force-exit --test-concurrency=1 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
33
|
-
"test:unit": "node --test --test-force-exit --test-concurrency=1 tests/unit/**/*.test.mjs",
|
|
34
|
-
"test:integration": "node --test --test-force-exit --test-concurrency=1 tests/integration/**/*.test.mjs",
|
|
35
|
-
"test:route-smoke": "node --test scripts/test-tfx-route-no-claude-native.mjs"
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=18.0.0"
|
|
39
|
-
},
|
|
40
|
-
"repository": {
|
|
41
|
-
"type": "git",
|
|
42
|
-
"url": "git+https://github.com/tellang/triflux.git"
|
|
43
|
-
},
|
|
44
|
-
"homepage": "https://github.com/tellang/triflux#readme",
|
|
45
|
-
"author": "tellang",
|
|
46
|
-
"license": "MIT",
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
49
|
-
"better-sqlite3": "^12.6.2",
|
|
50
|
-
"pino": "^10.3.1",
|
|
51
|
-
"pino-pretty": "^13.1.3",
|
|
52
|
-
"systray2": "^2.1.4",
|
|
53
|
-
"zod": "^4.0.0"
|
|
54
|
-
},
|
|
55
|
-
"keywords": [
|
|
56
|
-
"claude-code",
|
|
57
|
-
"plugin",
|
|
58
|
-
"codex",
|
|
59
|
-
"gemini",
|
|
60
|
-
"cli-routing",
|
|
61
|
-
"orchestration",
|
|
62
|
-
"multi-model",
|
|
63
|
-
"triflux",
|
|
64
|
-
"tfx"
|
|
65
|
-
]
|
|
66
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "triflux",
|
|
3
|
+
"version": "6.0.5",
|
|
4
|
+
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"triflux": "bin/triflux.mjs",
|
|
8
|
+
"tfx": "bin/triflux.mjs",
|
|
9
|
+
"tfl": "bin/triflux.mjs",
|
|
10
|
+
"tfx-setup": "bin/tfx-setup.mjs",
|
|
11
|
+
"tfx-doctor": "bin/tfx-doctor.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"hub",
|
|
16
|
+
"skills",
|
|
17
|
+
"!skills/tfx-workspace",
|
|
18
|
+
"!**/failure-reports",
|
|
19
|
+
"scripts",
|
|
20
|
+
"hooks",
|
|
21
|
+
"hud",
|
|
22
|
+
".claude-plugin",
|
|
23
|
+
".mcp.json",
|
|
24
|
+
"README.md",
|
|
25
|
+
"README.ko.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"setup": "node scripts/setup.mjs",
|
|
30
|
+
"preinstall": "node scripts/preinstall.mjs",
|
|
31
|
+
"postinstall": "node scripts/setup.mjs",
|
|
32
|
+
"test": "node --test --test-force-exit --test-concurrency=1 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
33
|
+
"test:unit": "node --test --test-force-exit --test-concurrency=1 tests/unit/**/*.test.mjs",
|
|
34
|
+
"test:integration": "node --test --test-force-exit --test-concurrency=1 tests/integration/**/*.test.mjs",
|
|
35
|
+
"test:route-smoke": "node --test scripts/test-tfx-route-no-claude-native.mjs"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/tellang/triflux.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/tellang/triflux#readme",
|
|
45
|
+
"author": "tellang",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
49
|
+
"better-sqlite3": "^12.6.2",
|
|
50
|
+
"pino": "^10.3.1",
|
|
51
|
+
"pino-pretty": "^13.1.3",
|
|
52
|
+
"systray2": "^2.1.4",
|
|
53
|
+
"zod": "^4.0.0"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"claude-code",
|
|
57
|
+
"plugin",
|
|
58
|
+
"codex",
|
|
59
|
+
"gemini",
|
|
60
|
+
"cli-routing",
|
|
61
|
+
"orchestration",
|
|
62
|
+
"multi-model",
|
|
63
|
+
"triflux",
|
|
64
|
+
"tfx"
|
|
65
|
+
]
|
|
66
|
+
}
|
package/scripts/preinstall.mjs
CHANGED
|
@@ -36,6 +36,22 @@ function stopHub() {
|
|
|
36
36
|
|
|
37
37
|
const pid = Number(info.pid);
|
|
38
38
|
|
|
39
|
+
// D4 fix: PID 소유자 검증 — node 프로세스인지 확인 (PID 재사용 보호)
|
|
40
|
+
if (process.platform === "win32") {
|
|
41
|
+
try {
|
|
42
|
+
const taskInfo = execFileSync("tasklist", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
|
|
43
|
+
encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 5000, windowsHide: true,
|
|
44
|
+
}).trim();
|
|
45
|
+
if (!taskInfo.toLowerCase().includes("node")) {
|
|
46
|
+
console.log(`[triflux preinstall] PID ${pid}은 node 프로세스가 아님 — kill 건너뜀`);
|
|
47
|
+
try { unlinkSync(HUB_PID_FILE); } catch {}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// tasklist 실패 — 안전하게 진행 (프로세스가 이미 죽었을 수 있음)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
// 1단계: 프로세스 종료 — Windows는 taskkill, Unix는 SIGTERM
|
|
40
56
|
try {
|
|
41
57
|
if (process.platform === "win32") {
|