triflux 8.11.2 → 8.12.2
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/README.ko.md +1 -1
- package/README.md +1 -1
- package/hooks/hooks.json +56 -51
- package/hub/pipeline/index.mjs +318 -318
- package/hub/routing/q-learning.mjs +2 -1
- package/hub/schema.sql +146 -146
- package/hub/team/cli/commands/kill.mjs +37 -37
- package/hub/team/cli/commands/start/index.mjs +3 -2
- package/hub/team/cli/commands/start/parse-args.mjs +5 -0
- package/hub/team/cli/commands/start/start-headless.mjs +2 -1
- package/hub/team/cli/commands/stop.mjs +31 -31
- package/hub/team/cli/commands/task.mjs +30 -30
- package/hub/team/cli/help.mjs +41 -40
- package/hub/team/cli/services/hub-client.mjs +208 -208
- package/hub/team/cli/services/native-control.mjs +118 -118
- package/hub/team/cli/services/runtime-mode.mjs +62 -62
- package/hub/team/cli/services/state-store.mjs +48 -48
- package/hub/team/dashboard-anchor.mjs +14 -0
- package/hub/team/dashboard.mjs +274 -274
- package/hub/team/headless.mjs +44 -19
- package/hub/team/native.mjs +649 -649
- package/hub/tools.mjs +554 -554
- package/hub/workers/delegator-mcp.mjs +21 -1
- package/package.json +1 -1
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +117 -0
- package/scripts/__tests__/remote-spawn.test.mjs +78 -0
- package/scripts/cache-buildup.mjs +401 -0
- package/scripts/headless-guard.mjs +10 -3
- package/scripts/hub-ensure.mjs +120 -120
- package/scripts/lib/mcp-filter.mjs +720 -720
- package/scripts/lib/remote-spawn-transfer.mjs +196 -0
- package/scripts/mcp-check.mjs +237 -127
- package/scripts/mcp-gateway-ensure.mjs +6 -8
- package/scripts/mcp-gateway-integration-test.mjs +228 -0
- package/scripts/mcp-gateway-start.mjs +42 -16
- package/scripts/mcp-gateway-start.ps1 +17 -14
- package/scripts/mcp-gateway-verify.mjs +1 -1
- package/scripts/preflight-cache.mjs +137 -137
- package/scripts/remote-spawn.mjs +404 -40
- package/scripts/setup.mjs +18 -0
- package/scripts/tfx-route-worker.mjs +165 -165
- package/scripts/tfx-route.sh +85 -16
- package/scripts/token-snapshot.mjs +575 -575
- package/skills/tfx-analysis/SKILL.md +101 -101
- package/skills/tfx-autopilot/SKILL.md +112 -112
- package/skills/tfx-autoresearch/SKILL.md +1 -0
- package/skills/tfx-autoroute/SKILL.md +184 -184
- package/skills/tfx-codex-swarm/SKILL.md +449 -0
- package/skills/tfx-codex-swarm/evals/evals.json +26 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +33 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/eval_metadata.json +42 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +11 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/analysis.md +87 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/classification.md +35 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/commands.sh +275 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/outputs/routing.md +56 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +11 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/analysis.md +92 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/classification.md +71 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/commands.sh +264 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/outputs/routing.md +113 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/eval_metadata.json +32 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +9 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/analysis.md +96 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/classification.md +38 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/commands.sh +151 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/outputs/routing.md +51 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +9 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/analysis.md +127 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/classification.md +57 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/commands.sh +129 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/outputs/routing.md +84 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/eval_metadata.json +27 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +8 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/analysis.md +98 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/classification.md +65 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/commands.sh +123 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/outputs/routing.md +66 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/timing.json +5 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +8 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/analysis.md +88 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/classification.md +40 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/commands.sh +130 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/outputs/routing.md +61 -0
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/timing.json +5 -0
- package/skills/tfx-profile/SKILL.md +141 -51
- package/skills/tfx-qa/SKILL.md +117 -117
- package/skills/tfx-remote-spawn/references/hosts.json +16 -16
- package/skills/tfx-review/SKILL.md +51 -51
- package/skills/tfx-setup/SKILL.md +20 -0
- package/tui/gemini-profile.mjs +254 -0
- package/tui/setup.mjs +36 -1
- package/scripts/claude-logged.ps1 +0 -54
- package/scripts/demo-tui.mjs +0 -59
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +0 -1
- package/skills/.omc/state/idle-notif-cooldown.json +0 -3
- package/skills/.omc/state/last-tool-error.json +0 -7
- package/skills/.omc/state/subagent-tracking.json +0 -7
|
@@ -1,165 +1,165 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// tfx-route-worker.mjs — tfx-route.sh용 subprocess worker 러너
|
|
3
|
-
|
|
4
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
-
import { dirname, resolve } from 'node:path';
|
|
6
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
-
|
|
8
|
-
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const FACTORY_CANDIDATES = [
|
|
10
|
-
resolve(SCRIPT_DIR, '../hub/workers/factory.mjs'),
|
|
11
|
-
resolve(SCRIPT_DIR, './hub/workers/factory.mjs'),
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
// MCP transport 실패 시 tfx-route.sh가 exec fallback을 수행할 수 있도록
|
|
15
|
-
// CODEX_MCP_TRANSPORT_EXIT_CODE(70)으로 종료한다.
|
|
16
|
-
const MCP_TRANSPORT_EXIT_CODE = 70;
|
|
17
|
-
|
|
18
|
-
let createWorker = null;
|
|
19
|
-
|
|
20
|
-
for (const candidate of FACTORY_CANDIDATES) {
|
|
21
|
-
if (!existsSync(candidate)) continue;
|
|
22
|
-
try {
|
|
23
|
-
({ createWorker } = await import(pathToFileURL(candidate).href));
|
|
24
|
-
} catch (err) {
|
|
25
|
-
// 의존성 누락 (예: @modelcontextprotocol/sdk) → fallback 가능하도록 exit 70
|
|
26
|
-
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
27
|
-
process.stderr.write(`[tfx-route-worker] 모듈 로드 실패: ${err.message}\n`);
|
|
28
|
-
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
29
|
-
}
|
|
30
|
-
throw err;
|
|
31
|
-
}
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (!createWorker) {
|
|
36
|
-
process.stderr.write('[tfx-route-worker] worker factory를 찾지 못했습니다.\n');
|
|
37
|
-
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function parseArgs(argv) {
|
|
41
|
-
const args = {
|
|
42
|
-
allowedMcpServerNames: [],
|
|
43
|
-
mcpConfig: [],
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
47
|
-
const token = argv[index];
|
|
48
|
-
const next = argv[index + 1];
|
|
49
|
-
|
|
50
|
-
switch (token) {
|
|
51
|
-
case '--type':
|
|
52
|
-
args.type = next;
|
|
53
|
-
index += 1;
|
|
54
|
-
break;
|
|
55
|
-
case '--command':
|
|
56
|
-
args.command = next;
|
|
57
|
-
index += 1;
|
|
58
|
-
break;
|
|
59
|
-
case '--command-args-json':
|
|
60
|
-
args.commandArgsJson = next;
|
|
61
|
-
index += 1;
|
|
62
|
-
break;
|
|
63
|
-
case '--model':
|
|
64
|
-
args.model = next;
|
|
65
|
-
index += 1;
|
|
66
|
-
break;
|
|
67
|
-
case '--timeout-ms':
|
|
68
|
-
args.timeoutMs = Number(next);
|
|
69
|
-
index += 1;
|
|
70
|
-
break;
|
|
71
|
-
case '--approval-mode':
|
|
72
|
-
args.approvalMode = next;
|
|
73
|
-
index += 1;
|
|
74
|
-
break;
|
|
75
|
-
case '--permission-mode':
|
|
76
|
-
args.permissionMode = next;
|
|
77
|
-
index += 1;
|
|
78
|
-
break;
|
|
79
|
-
case '--allow-dangerously-skip-permissions':
|
|
80
|
-
args.allowDangerouslySkipPermissions = true;
|
|
81
|
-
break;
|
|
82
|
-
case '--allowed-mcp-server-name':
|
|
83
|
-
args.allowedMcpServerNames.push(next);
|
|
84
|
-
index += 1;
|
|
85
|
-
break;
|
|
86
|
-
case '--mcp-config':
|
|
87
|
-
args.mcpConfig.push(next);
|
|
88
|
-
index += 1;
|
|
89
|
-
break;
|
|
90
|
-
case '--cwd':
|
|
91
|
-
args.cwd = next;
|
|
92
|
-
index += 1;
|
|
93
|
-
break;
|
|
94
|
-
default:
|
|
95
|
-
throw new Error(`Unknown argument: ${token}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!args.type) {
|
|
100
|
-
throw new Error('--type is required');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return args;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function parseJsonArray(raw, label) {
|
|
107
|
-
if (!raw) return [];
|
|
108
|
-
try {
|
|
109
|
-
const parsed = JSON.parse(raw);
|
|
110
|
-
if (!Array.isArray(parsed)) {
|
|
111
|
-
throw new Error(`${label} must be a JSON array`);
|
|
112
|
-
}
|
|
113
|
-
return parsed.map((item) => String(item));
|
|
114
|
-
} catch (error) {
|
|
115
|
-
throw new Error(`${label} parse failed: ${error.message}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function readPromptFromStdin() {
|
|
120
|
-
return readFileSync(0, 'utf8');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function resolveDefaultMcpConfig(cwd) {
|
|
124
|
-
const primary = resolve(cwd, '.claude', 'mcp.json');
|
|
125
|
-
if (existsSync(primary)) return [primary];
|
|
126
|
-
const legacy = resolve(cwd, '.mcp.json');
|
|
127
|
-
if (existsSync(legacy)) return [legacy];
|
|
128
|
-
process.stderr.write('[tfx-route-worker] warning: no MCP config found, hub unavailable\n');
|
|
129
|
-
return [];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const args = parseArgs(process.argv.slice(2));
|
|
133
|
-
const prompt = readPromptFromStdin();
|
|
134
|
-
|
|
135
|
-
const worker = createWorker(args.type, {
|
|
136
|
-
command: args.command,
|
|
137
|
-
commandArgs: parseJsonArray(args.commandArgsJson, '--command-args-json'),
|
|
138
|
-
model: args.model,
|
|
139
|
-
timeoutMs: args.timeoutMs,
|
|
140
|
-
approvalMode: args.approvalMode,
|
|
141
|
-
permissionMode: args.permissionMode,
|
|
142
|
-
allowDangerouslySkipPermissions: args.allowDangerouslySkipPermissions,
|
|
143
|
-
allowedMcpServerNames: args.allowedMcpServerNames,
|
|
144
|
-
mcpConfig: args.type === 'claude' && args.mcpConfig.length === 0
|
|
145
|
-
? resolveDefaultMcpConfig(args.cwd || process.cwd())
|
|
146
|
-
: args.mcpConfig,
|
|
147
|
-
cwd: args.cwd || process.cwd(),
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
const result = await worker.run(prompt);
|
|
152
|
-
if (result.response) {
|
|
153
|
-
process.stdout.write(result.response);
|
|
154
|
-
if (!result.response.endsWith('\n')) process.stdout.write('\n');
|
|
155
|
-
}
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (error.stderr) {
|
|
158
|
-
process.stderr.write(String(error.stderr));
|
|
159
|
-
if (!String(error.stderr).endsWith('\n')) process.stderr.write('\n');
|
|
160
|
-
}
|
|
161
|
-
process.stderr.write(`${error.message}\n`);
|
|
162
|
-
process.exitCode = error.code === 'ETIMEDOUT' ? 124 : 1;
|
|
163
|
-
} finally {
|
|
164
|
-
try { await worker.stop(); } catch {}
|
|
165
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// tfx-route-worker.mjs — tfx-route.sh용 subprocess worker 러너
|
|
3
|
+
|
|
4
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
import { dirname, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const FACTORY_CANDIDATES = [
|
|
10
|
+
resolve(SCRIPT_DIR, '../hub/workers/factory.mjs'),
|
|
11
|
+
resolve(SCRIPT_DIR, './hub/workers/factory.mjs'),
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// MCP transport 실패 시 tfx-route.sh가 exec fallback을 수행할 수 있도록
|
|
15
|
+
// CODEX_MCP_TRANSPORT_EXIT_CODE(70)으로 종료한다.
|
|
16
|
+
const MCP_TRANSPORT_EXIT_CODE = 70;
|
|
17
|
+
|
|
18
|
+
let createWorker = null;
|
|
19
|
+
|
|
20
|
+
for (const candidate of FACTORY_CANDIDATES) {
|
|
21
|
+
if (!existsSync(candidate)) continue;
|
|
22
|
+
try {
|
|
23
|
+
({ createWorker } = await import(pathToFileURL(candidate).href));
|
|
24
|
+
} catch (err) {
|
|
25
|
+
// 의존성 누락 (예: @modelcontextprotocol/sdk) → fallback 가능하도록 exit 70
|
|
26
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
27
|
+
process.stderr.write(`[tfx-route-worker] 모듈 로드 실패: ${err.message}\n`);
|
|
28
|
+
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
29
|
+
}
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!createWorker) {
|
|
36
|
+
process.stderr.write('[tfx-route-worker] worker factory를 찾지 못했습니다.\n');
|
|
37
|
+
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseArgs(argv) {
|
|
41
|
+
const args = {
|
|
42
|
+
allowedMcpServerNames: [],
|
|
43
|
+
mcpConfig: [],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
47
|
+
const token = argv[index];
|
|
48
|
+
const next = argv[index + 1];
|
|
49
|
+
|
|
50
|
+
switch (token) {
|
|
51
|
+
case '--type':
|
|
52
|
+
args.type = next;
|
|
53
|
+
index += 1;
|
|
54
|
+
break;
|
|
55
|
+
case '--command':
|
|
56
|
+
args.command = next;
|
|
57
|
+
index += 1;
|
|
58
|
+
break;
|
|
59
|
+
case '--command-args-json':
|
|
60
|
+
args.commandArgsJson = next;
|
|
61
|
+
index += 1;
|
|
62
|
+
break;
|
|
63
|
+
case '--model':
|
|
64
|
+
args.model = next;
|
|
65
|
+
index += 1;
|
|
66
|
+
break;
|
|
67
|
+
case '--timeout-ms':
|
|
68
|
+
args.timeoutMs = Number(next);
|
|
69
|
+
index += 1;
|
|
70
|
+
break;
|
|
71
|
+
case '--approval-mode':
|
|
72
|
+
args.approvalMode = next;
|
|
73
|
+
index += 1;
|
|
74
|
+
break;
|
|
75
|
+
case '--permission-mode':
|
|
76
|
+
args.permissionMode = next;
|
|
77
|
+
index += 1;
|
|
78
|
+
break;
|
|
79
|
+
case '--allow-dangerously-skip-permissions':
|
|
80
|
+
args.allowDangerouslySkipPermissions = true;
|
|
81
|
+
break;
|
|
82
|
+
case '--allowed-mcp-server-name':
|
|
83
|
+
args.allowedMcpServerNames.push(next);
|
|
84
|
+
index += 1;
|
|
85
|
+
break;
|
|
86
|
+
case '--mcp-config':
|
|
87
|
+
args.mcpConfig.push(next);
|
|
88
|
+
index += 1;
|
|
89
|
+
break;
|
|
90
|
+
case '--cwd':
|
|
91
|
+
args.cwd = next;
|
|
92
|
+
index += 1;
|
|
93
|
+
break;
|
|
94
|
+
default:
|
|
95
|
+
throw new Error(`Unknown argument: ${token}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!args.type) {
|
|
100
|
+
throw new Error('--type is required');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return args;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseJsonArray(raw, label) {
|
|
107
|
+
if (!raw) return [];
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(raw);
|
|
110
|
+
if (!Array.isArray(parsed)) {
|
|
111
|
+
throw new Error(`${label} must be a JSON array`);
|
|
112
|
+
}
|
|
113
|
+
return parsed.map((item) => String(item));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(`${label} parse failed: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function readPromptFromStdin() {
|
|
120
|
+
return readFileSync(0, 'utf8');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveDefaultMcpConfig(cwd) {
|
|
124
|
+
const primary = resolve(cwd, '.claude', 'mcp.json');
|
|
125
|
+
if (existsSync(primary)) return [primary];
|
|
126
|
+
const legacy = resolve(cwd, '.mcp.json');
|
|
127
|
+
if (existsSync(legacy)) return [legacy];
|
|
128
|
+
process.stderr.write('[tfx-route-worker] warning: no MCP config found, hub unavailable\n');
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const args = parseArgs(process.argv.slice(2));
|
|
133
|
+
const prompt = readPromptFromStdin();
|
|
134
|
+
|
|
135
|
+
const worker = createWorker(args.type, {
|
|
136
|
+
command: args.command,
|
|
137
|
+
commandArgs: parseJsonArray(args.commandArgsJson, '--command-args-json'),
|
|
138
|
+
model: args.model,
|
|
139
|
+
timeoutMs: args.timeoutMs,
|
|
140
|
+
approvalMode: args.approvalMode,
|
|
141
|
+
permissionMode: args.permissionMode,
|
|
142
|
+
allowDangerouslySkipPermissions: args.allowDangerouslySkipPermissions,
|
|
143
|
+
allowedMcpServerNames: args.allowedMcpServerNames,
|
|
144
|
+
mcpConfig: args.type === 'claude' && args.mcpConfig.length === 0
|
|
145
|
+
? resolveDefaultMcpConfig(args.cwd || process.cwd())
|
|
146
|
+
: args.mcpConfig,
|
|
147
|
+
cwd: args.cwd || process.cwd(),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const result = await worker.run(prompt);
|
|
152
|
+
if (result.response) {
|
|
153
|
+
process.stdout.write(result.response);
|
|
154
|
+
if (!result.response.endsWith('\n')) process.stdout.write('\n');
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error.stderr) {
|
|
158
|
+
process.stderr.write(String(error.stderr));
|
|
159
|
+
if (!String(error.stderr).endsWith('\n')) process.stderr.write('\n');
|
|
160
|
+
}
|
|
161
|
+
process.stderr.write(`${error.message}\n`);
|
|
162
|
+
process.exitCode = error.code === 'ETIMEDOUT' ? 124 : 1;
|
|
163
|
+
} finally {
|
|
164
|
+
try { await worker.stop(); } catch {}
|
|
165
|
+
}
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -38,8 +38,26 @@ else
|
|
|
38
38
|
TIMEOUT_BIN="timeout" # Linux 기본
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
|
+
# ── 임시 디렉토리 정규화 ──
|
|
42
|
+
resolve_tmp_dir() {
|
|
43
|
+
local candidate=""
|
|
44
|
+
for candidate in "${TMPDIR:-}" "${TEMP:-}" "${TMP:-}" "/tmp"; do
|
|
45
|
+
[[ -n "$candidate" ]] || continue
|
|
46
|
+
if mkdir -p "$candidate" >/dev/null 2>&1; then
|
|
47
|
+
printf '%s\n' "$candidate"
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
candidate="$(pwd)/.tfx-tmp"
|
|
53
|
+
mkdir -p "$candidate" >/dev/null 2>&1 || true
|
|
54
|
+
printf '%s\n' "$candidate"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
TFX_TMP="$(resolve_tmp_dir)"
|
|
58
|
+
|
|
41
59
|
# ── Async Job 디렉토리 ──
|
|
42
|
-
TFX_JOBS_DIR="${
|
|
60
|
+
TFX_JOBS_DIR="${TFX_TMP}/tfx-jobs"
|
|
43
61
|
|
|
44
62
|
# ── --job-status / --job-result 핸들러 (인자 파싱 전에 처리) ──
|
|
45
63
|
if [[ "${1:-}" == "--job-status" ]]; then
|
|
@@ -148,13 +166,15 @@ CLAUDE_BIN="${CLAUDE_BIN:-$(command -v claude 2>/dev/null || echo claude)}"
|
|
|
148
166
|
GEMINI_BIN_ARGS_JSON="${GEMINI_BIN_ARGS_JSON:-[]}"
|
|
149
167
|
CLAUDE_BIN_ARGS_JSON="${CLAUDE_BIN_ARGS_JSON:-[]}"
|
|
150
168
|
|
|
169
|
+
# ── Gemini 프로필 경로 (Codex config.toml 대칭) ──
|
|
170
|
+
GEMINI_PROFILES_PATH="${GEMINI_PROFILES_PATH:-$(eval echo ~)/.gemini/triflux-profiles.json}"
|
|
171
|
+
|
|
151
172
|
# ── 상수 ──
|
|
152
173
|
MAX_STDOUT_BYTES=51200 # 50KB — Claude 컨텍스트 절약
|
|
153
174
|
TIMESTAMP=$(date +%s)
|
|
154
175
|
RUN_ID="${TIMESTAMP}-$$-${RANDOM}"
|
|
155
|
-
STDERR_LOG="/
|
|
156
|
-
STDOUT_LOG="/
|
|
157
|
-
TFX_TMP="${TMPDIR:-/tmp}"
|
|
176
|
+
STDERR_LOG="${TFX_TMP}/tfx-route-${AGENT_TYPE}-${RUN_ID}-stderr.log"
|
|
177
|
+
STDOUT_LOG="${TFX_TMP}/tfx-route-${AGENT_TYPE}-${RUN_ID}-stdout.log"
|
|
158
178
|
|
|
159
179
|
# ── 팀 환경변수 ──
|
|
160
180
|
TFX_TEAM_NAME="${TFX_TEAM_NAME:-}"
|
|
@@ -593,6 +613,33 @@ codex_gte() {
|
|
|
593
613
|
printf '%s\n%s' "$min" "$cur" | sort -V | head -1 | grep -q "^${min}$"
|
|
594
614
|
}
|
|
595
615
|
|
|
616
|
+
# ── Gemini 프로필 해석 (Codex --profile 대칭) ──
|
|
617
|
+
_GEMINI_PROFILE_CACHE=""
|
|
618
|
+
resolve_gemini_profile() {
|
|
619
|
+
local profile="$1"
|
|
620
|
+
if [[ -z "$_GEMINI_PROFILE_CACHE" && -f "$GEMINI_PROFILES_PATH" ]]; then
|
|
621
|
+
_GEMINI_PROFILE_CACHE=$(cat "$GEMINI_PROFILES_PATH" 2>/dev/null || echo "{}")
|
|
622
|
+
fi
|
|
623
|
+
local result
|
|
624
|
+
result=$("$NODE_BIN" -e "
|
|
625
|
+
const name = process.argv[1];
|
|
626
|
+
const defaults = {
|
|
627
|
+
pro31: 'gemini-3.1-pro-preview',
|
|
628
|
+
flash3: 'gemini-3-flash-preview',
|
|
629
|
+
pro25: 'gemini-2.5-pro',
|
|
630
|
+
flash25: 'gemini-2.5-flash',
|
|
631
|
+
lite25: 'gemini-2.5-flash-lite'
|
|
632
|
+
};
|
|
633
|
+
try {
|
|
634
|
+
const cfg = JSON.parse(process.argv[2] || '{}');
|
|
635
|
+
const p = cfg.profiles?.[name];
|
|
636
|
+
if (p?.model) { process.stdout.write(p.model); return; }
|
|
637
|
+
} catch {}
|
|
638
|
+
process.stdout.write(defaults[name] || 'gemini-3.1-pro-preview');
|
|
639
|
+
" "$profile" "$_GEMINI_PROFILE_CACHE" 2>/dev/null)
|
|
640
|
+
echo "${result:-gemini-3.1-pro-preview}"
|
|
641
|
+
}
|
|
642
|
+
|
|
596
643
|
# ── 라우팅 테이블 ──
|
|
597
644
|
# CLI_TYPE/CLI_CMD: agent-map.json 단일 소스. 상세 설정: 아래 case 문.
|
|
598
645
|
# 반환: CLI_TYPE, CLI_CMD, CLI_ARGS, CLI_EFFORT, DEFAULT_TIMEOUT, RUN_MODE, OPUS_OVERSIGHT
|
|
@@ -691,11 +738,11 @@ route_agent() {
|
|
|
691
738
|
|
|
692
739
|
# ─── UI/문서 레인 ───
|
|
693
740
|
designer)
|
|
694
|
-
CLI_ARGS="-m
|
|
695
|
-
CLI_EFFORT="
|
|
741
|
+
CLI_ARGS="-m $(resolve_gemini_profile pro31) -y --prompt"
|
|
742
|
+
CLI_EFFORT="pro31"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
696
743
|
writer)
|
|
697
|
-
CLI_ARGS="-m
|
|
698
|
-
CLI_EFFORT="
|
|
744
|
+
CLI_ARGS="-m $(resolve_gemini_profile flash3) -y --prompt"
|
|
745
|
+
CLI_EFFORT="flash3"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
699
746
|
|
|
700
747
|
# ─── 탐색 (Claude-native: Glob/Grep/Read 직접 접근) ───
|
|
701
748
|
explore)
|
|
@@ -721,8 +768,8 @@ route_agent() {
|
|
|
721
768
|
CLI_ARGS="exec --profile codex53_high ${codex_base}"
|
|
722
769
|
CLI_EFFORT="codex53_high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
723
770
|
gemini)
|
|
724
|
-
CLI_ARGS="-m
|
|
725
|
-
CLI_EFFORT="
|
|
771
|
+
CLI_ARGS="-m $(resolve_gemini_profile pro31) -y --prompt"
|
|
772
|
+
CLI_EFFORT="pro31"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
726
773
|
claude)
|
|
727
774
|
CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=600; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
728
775
|
# ─── agent-map.json에만 정의된 신규 에이전트 (CLI_TYPE별 기본값) ───
|
|
@@ -732,8 +779,8 @@ route_agent() {
|
|
|
732
779
|
CLI_ARGS="exec --profile codex53_high ${codex_base}"
|
|
733
780
|
CLI_EFFORT="codex53_high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
734
781
|
gemini)
|
|
735
|
-
CLI_ARGS="-m
|
|
736
|
-
CLI_EFFORT="
|
|
782
|
+
CLI_ARGS="-m $(resolve_gemini_profile pro31) -y --prompt"
|
|
783
|
+
CLI_EFFORT="pro31"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
737
784
|
claude-native)
|
|
738
785
|
CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=600; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
739
786
|
esac ;;
|
|
@@ -821,15 +868,31 @@ apply_cli_mode() {
|
|
|
821
868
|
fi ;;
|
|
822
869
|
gemini)
|
|
823
870
|
if [[ "$CLI_TYPE" == "codex" ]]; then
|
|
871
|
+
case "$AGENT_TYPE" in
|
|
872
|
+
verifier)
|
|
873
|
+
CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
|
|
874
|
+
CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=1200; RUN_MODE="fg"; OPUS_OVERSIGHT="false"
|
|
875
|
+
echo "[tfx-route] TFX_CLI_MODE=gemini: verifier는 claude-native 유지" >&2
|
|
876
|
+
return 0
|
|
877
|
+
;;
|
|
878
|
+
test-engineer)
|
|
879
|
+
CLI_TYPE="claude-native"; CLI_CMD=""; CLI_ARGS=""
|
|
880
|
+
CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=1200; RUN_MODE="bg"; OPUS_OVERSIGHT="false"
|
|
881
|
+
echo "[tfx-route] TFX_CLI_MODE=gemini: test-engineer는 claude-native 유지" >&2
|
|
882
|
+
return 0
|
|
883
|
+
;;
|
|
884
|
+
esac
|
|
824
885
|
CLI_TYPE="gemini"; CLI_CMD="gemini"
|
|
825
886
|
case "$AGENT_TYPE" in
|
|
826
887
|
executor|debugger|deep-executor|architect|planner|critic|analyst|\
|
|
827
|
-
code-reviewer|security-reviewer|quality-reviewer|scientist-deep)
|
|
828
|
-
CLI_ARGS="-m
|
|
888
|
+
code-reviewer|security-reviewer|quality-reviewer|scientist-deep|designer)
|
|
889
|
+
CLI_ARGS="-m $(resolve_gemini_profile pro31) -y --prompt"; CLI_EFFORT="pro31" ;;
|
|
829
890
|
build-fixer|spark)
|
|
830
|
-
CLI_ARGS="-m
|
|
891
|
+
CLI_ARGS="-m $(resolve_gemini_profile flash3) -y --prompt"; CLI_EFFORT="flash3"; DEFAULT_TIMEOUT=180 ;;
|
|
892
|
+
writer)
|
|
893
|
+
CLI_ARGS="-m $(resolve_gemini_profile flash3) -y --prompt"; CLI_EFFORT="flash3" ;;
|
|
831
894
|
*)
|
|
832
|
-
CLI_ARGS="-m
|
|
895
|
+
CLI_ARGS="-m $(resolve_gemini_profile flash3) -y --prompt"; CLI_EFFORT="flash3" ;;
|
|
833
896
|
esac
|
|
834
897
|
echo "[tfx-route] TFX_CLI_MODE=gemini: $AGENT_TYPE → gemini($CLI_EFFORT)로 리매핑" >&2
|
|
835
898
|
fi ;;
|
|
@@ -931,6 +994,9 @@ apply_verifier_override() {
|
|
|
931
994
|
;;
|
|
932
995
|
claude)
|
|
933
996
|
ORIGINAL_AGENT="${ORIGINAL_AGENT:-$AGENT_TYPE}"
|
|
997
|
+
CLI_TYPE="claude-native"
|
|
998
|
+
CLI_CMD=""
|
|
999
|
+
CLI_ARGS=""
|
|
934
1000
|
CLI_EFFORT="n/a"; DEFAULT_TIMEOUT=1200; RUN_MODE="fg"; OPUS_OVERSIGHT="false"
|
|
935
1001
|
echo "[tfx-route] TFX_VERIFIER_OVERRIDE=claude: verifier -> claude-native" >&2
|
|
936
1002
|
;;
|
|
@@ -1001,6 +1067,9 @@ resolve_mcp_policy() {
|
|
|
1001
1067
|
fi
|
|
1002
1068
|
|
|
1003
1069
|
available_servers=$(get_cached_servers "$CLI_TYPE")
|
|
1070
|
+
if [[ "$CLI_TYPE" == "codex" && "${TFX_CODEX_TRANSPORT:-auto}" != "mcp" ]]; then
|
|
1071
|
+
available_servers=""
|
|
1072
|
+
fi
|
|
1004
1073
|
# Codex 0.115+: 미등록 서버에 config override(enabled=true/false 모두)를 보내면
|
|
1005
1074
|
# "invalid transport" 에러 발생. 캐시 비어있으면 빈 문자열로 유지하여
|
|
1006
1075
|
# mcp-filter가 override를 생성하지 않도록 한다.
|