triflux 7.3.2 → 7.4.0
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/plugin.json +1 -1
- package/hub/team/agent-map.json +2 -1
- package/hub/team/backend.mjs +3 -3
- package/hub/team/cli/services/native-control.mjs +118 -115
- package/hub/team/codex-compat.mjs +78 -0
- package/hub/team/pane.mjs +154 -150
- package/hub/team/psmux.mjs +1041 -1023
- package/hud/constants.mjs +3 -0
- package/package.json +1 -1
- package/scripts/headless-guard.mjs +94 -7
- package/scripts/setup.mjs +65 -0
- package/scripts/tfx-gate-activate.mjs +89 -0
- package/scripts/tfx-route-post.mjs +17 -13
- package/scripts/tfx-route.sh +118 -46
- package/skills/tfx-auto/SKILL.md +1 -1
- package/skills/tfx-multi/SKILL.md +1 -1
package/hub/team/agent-map.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"scientist": "codex", "scientist-deep": "codex", "document-specialist": "codex",
|
|
6
6
|
"spark": "codex",
|
|
7
7
|
"designer": "gemini", "writer": "gemini",
|
|
8
|
-
"explore": "claude",
|
|
8
|
+
"explore": "claude",
|
|
9
|
+
"verifier": "codex", "test-engineer": "codex", "qa-tester": "codex",
|
|
9
10
|
"codex": "codex", "gemini": "gemini", "claude": "claude"
|
|
10
11
|
}
|
package/hub/team/backend.mjs
CHANGED
|
@@ -19,7 +19,7 @@ export class CodexBackend {
|
|
|
19
19
|
*/
|
|
20
20
|
buildArgs(prompt, resultFile, opts = {}) {
|
|
21
21
|
const modelFlag = opts.model ? ` --model '${opts.model}'` : "";
|
|
22
|
-
return `codex exec ${prompt} -
|
|
22
|
+
return `codex exec --dangerously-bypass-approvals-and-sandbox ${prompt} --output-last-message '${resultFile}' --color never${modelFlag}`;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
env() { return {}; }
|
|
@@ -30,7 +30,7 @@ export class GeminiBackend {
|
|
|
30
30
|
command() { return "gemini"; }
|
|
31
31
|
|
|
32
32
|
buildArgs(prompt, resultFile, opts = {}) {
|
|
33
|
-
return `gemini
|
|
33
|
+
return `gemini --prompt ${prompt} -o text > '${resultFile}' 2>'${resultFile}.err'`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
env() { return {}; }
|
|
@@ -41,7 +41,7 @@ export class ClaudeBackend {
|
|
|
41
41
|
command() { return "claude"; }
|
|
42
42
|
|
|
43
43
|
buildArgs(prompt, resultFile, opts = {}) {
|
|
44
|
-
return `claude
|
|
44
|
+
return `claude --print ${prompt} --output-format text > '${resultFile}' 2>&1`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
env() { return {}; }
|
|
@@ -1,115 +1,118 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
import { buildLeadPrompt, buildPrompt } from "../../orchestrator.mjs";
|
|
6
|
-
import { HUB_PID_DIR, PKG_ROOT } from "./state-store.mjs";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
import { buildLeadPrompt, buildPrompt } from "../../orchestrator.mjs";
|
|
6
|
+
import { HUB_PID_DIR, PKG_ROOT } from "./state-store.mjs";
|
|
7
|
+
import { FEATURES } from "../../codex-compat.mjs";
|
|
8
|
+
|
|
9
|
+
export function buildNativeCliCommand(cli) {
|
|
10
|
+
switch (cli) {
|
|
11
|
+
case "codex":
|
|
12
|
+
return FEATURES.execSubcommand
|
|
13
|
+
? "codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check"
|
|
14
|
+
: "codex --dangerously-bypass-approvals-and-sandbox";
|
|
15
|
+
case "gemini":
|
|
16
|
+
return "gemini";
|
|
17
|
+
case "claude":
|
|
18
|
+
return "claude";
|
|
19
|
+
default:
|
|
20
|
+
return cli;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function startNativeSupervisor({ sessionId, task, lead, agents, subtasks, hubUrl }) {
|
|
25
|
+
const configPath = join(HUB_PID_DIR, `team-native-${sessionId}.config.json`);
|
|
26
|
+
const runtimePath = join(HUB_PID_DIR, `team-native-${sessionId}.runtime.json`);
|
|
27
|
+
const logsDir = join(HUB_PID_DIR, "team-logs", sessionId);
|
|
28
|
+
mkdirSync(logsDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
const leadMember = {
|
|
31
|
+
role: "lead",
|
|
32
|
+
name: "lead",
|
|
33
|
+
cli: lead,
|
|
34
|
+
agentId: `${lead}-lead`,
|
|
35
|
+
command: buildNativeCliCommand(lead),
|
|
36
|
+
};
|
|
37
|
+
const workers = agents.map((cli, index) => ({
|
|
38
|
+
role: "worker",
|
|
39
|
+
name: `${cli}-${index + 1}`,
|
|
40
|
+
cli,
|
|
41
|
+
agentId: `${cli}-w${index + 1}`,
|
|
42
|
+
command: buildNativeCliCommand(cli),
|
|
43
|
+
subtask: subtasks[index],
|
|
44
|
+
}));
|
|
45
|
+
const members = [
|
|
46
|
+
{
|
|
47
|
+
...leadMember,
|
|
48
|
+
prompt: buildLeadPrompt(task, {
|
|
49
|
+
agentId: leadMember.agentId,
|
|
50
|
+
hubUrl,
|
|
51
|
+
teammateMode: "in-process",
|
|
52
|
+
workers: workers.map((worker) => ({
|
|
53
|
+
agentId: worker.agentId,
|
|
54
|
+
cli: worker.cli,
|
|
55
|
+
subtask: worker.subtask,
|
|
56
|
+
})),
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
...workers.map((worker) => ({
|
|
60
|
+
...worker,
|
|
61
|
+
prompt: buildPrompt(worker.subtask, { cli: worker.cli, agentId: worker.agentId, hubUrl }),
|
|
62
|
+
})),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
writeFileSync(configPath, JSON.stringify({
|
|
66
|
+
sessionName: sessionId,
|
|
67
|
+
hubUrl,
|
|
68
|
+
startupDelayMs: 3000,
|
|
69
|
+
logsDir,
|
|
70
|
+
runtimeFile: runtimePath,
|
|
71
|
+
members,
|
|
72
|
+
}, null, 2) + "\n");
|
|
73
|
+
|
|
74
|
+
const child = spawn(process.execPath, [join(PKG_ROOT, "hub", "team", "native-supervisor.mjs"), "--config", configPath], {
|
|
75
|
+
detached: true,
|
|
76
|
+
stdio: "ignore",
|
|
77
|
+
env: { ...process.env },
|
|
78
|
+
windowsHide: true,
|
|
79
|
+
});
|
|
80
|
+
child.unref();
|
|
81
|
+
|
|
82
|
+
const deadline = Date.now() + 5000;
|
|
83
|
+
while (Date.now() < deadline) {
|
|
84
|
+
if (existsSync(runtimePath)) {
|
|
85
|
+
try {
|
|
86
|
+
const runtime = JSON.parse(readFileSync(runtimePath, "utf8"));
|
|
87
|
+
return { runtime, members };
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { runtime: null, members };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function nativeRequest(state, path, body = {}) {
|
|
97
|
+
if (!state?.native?.controlUrl) return null;
|
|
98
|
+
try {
|
|
99
|
+
const res = await fetch(`${state.native.controlUrl}${path}`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: { "Content-Type": "application/json" },
|
|
102
|
+
body: JSON.stringify(body),
|
|
103
|
+
});
|
|
104
|
+
return await res.json();
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function nativeGetStatus(state) {
|
|
111
|
+
if (!state?.native?.controlUrl) return null;
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch(`${state.native.controlUrl}/status`);
|
|
114
|
+
return await res.json();
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// hub/team/codex-compat.mjs — Codex CLI 버전 어댑터
|
|
2
|
+
// Codex 0.117.0+ (Rust 리라이트): exec 서브커맨드 기반
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
let _cachedVersion = null;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `codex --version` 실행 결과를 파싱하여 마이너 버전 숫자 반환.
|
|
9
|
+
* 파싱 실패 시 0 반환 (구버전으로 간주).
|
|
10
|
+
* @returns {number} 마이너 버전 (예: 0.117.0 → 117)
|
|
11
|
+
*/
|
|
12
|
+
export function getCodexVersion() {
|
|
13
|
+
if (_cachedVersion !== null) return _cachedVersion;
|
|
14
|
+
try {
|
|
15
|
+
const out = execSync("codex --version", { encoding: "utf8", timeout: 5000 }).trim();
|
|
16
|
+
// "codex 0.117.0" 또는 "0.117.0" 형식 대응
|
|
17
|
+
const m = out.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
18
|
+
_cachedVersion = m ? parseInt(m[2], 10) : 0;
|
|
19
|
+
} catch {
|
|
20
|
+
_cachedVersion = 0;
|
|
21
|
+
}
|
|
22
|
+
return _cachedVersion;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 최소 마이너 버전 이상인지 확인.
|
|
27
|
+
* @param {number} minMinor
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
export function gte(minMinor) {
|
|
31
|
+
return getCodexVersion() >= minMinor;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Codex CLI 기능별 분기 객체.
|
|
36
|
+
* 117 = 0.117.0 (Rust 리라이트, exec 서브커맨드 도입)
|
|
37
|
+
*/
|
|
38
|
+
export const FEATURES = {
|
|
39
|
+
/** exec 서브커맨드 사용 가능 여부 */
|
|
40
|
+
get execSubcommand() { return gte(117); },
|
|
41
|
+
/** --output-last-message 플래그 지원 여부 */
|
|
42
|
+
get outputLastMessage() { return gte(117); },
|
|
43
|
+
/** --color never 플래그 지원 여부 */
|
|
44
|
+
get colorNever() { return gte(117); },
|
|
45
|
+
/** 플러그인 시스템 지원 여부 (향후 확장용) */
|
|
46
|
+
get pluginSystem() { return gte(120); },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* long-form 플래그 기반 명령 빌더.
|
|
51
|
+
* @param {string} prompt
|
|
52
|
+
* @param {string|null} resultFile — null이면 --output-last-message 생략
|
|
53
|
+
* @param {{ profile?: string, skipGitRepoCheck?: boolean, sandboxBypass?: boolean }} [opts]
|
|
54
|
+
* @returns {string} 실행할 셸 커맨드
|
|
55
|
+
*/
|
|
56
|
+
export function buildExecCommand(prompt, resultFile = null, opts = {}) {
|
|
57
|
+
const { profile, skipGitRepoCheck = true, sandboxBypass = true } = opts;
|
|
58
|
+
|
|
59
|
+
const parts = ["codex"];
|
|
60
|
+
if (profile) parts.push("--profile", profile);
|
|
61
|
+
|
|
62
|
+
if (FEATURES.execSubcommand) {
|
|
63
|
+
parts.push("exec");
|
|
64
|
+
if (sandboxBypass) parts.push("--dangerously-bypass-approvals-and-sandbox");
|
|
65
|
+
if (skipGitRepoCheck) parts.push("--skip-git-repo-check");
|
|
66
|
+
if (resultFile && FEATURES.outputLastMessage) {
|
|
67
|
+
parts.push("--output-last-message", resultFile);
|
|
68
|
+
}
|
|
69
|
+
if (FEATURES.colorNever) parts.push("--color", "never");
|
|
70
|
+
} else {
|
|
71
|
+
// 구버전 fallback
|
|
72
|
+
parts.push("--dangerously-bypass-approvals-and-sandbox");
|
|
73
|
+
if (skipGitRepoCheck) parts.push("--skip-git-repo-check");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
parts.push(JSON.stringify(prompt));
|
|
77
|
+
return parts.join(" ");
|
|
78
|
+
}
|