@triflux/remote 10.25.1 → 10.26.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/hub/server.mjs +21 -8
- package/hub/team/agent-map.json +3 -3
- package/hub/team/backend.mjs +3 -9
- package/hub/team/build-worker-prompt.mjs +33 -2
- package/hub/team/claude-daemon-control.mjs +911 -0
- package/hub/team/claude-native-bridge.mjs +238 -11
- package/hub/team/claude-session-projection.mjs +10 -1
- package/hub/team/cli/commands/start/index.mjs +3 -6
- package/hub/team/cli/commands/start/parse-args.mjs +10 -3
- package/hub/team/cli/commands/start/start-headless.mjs +50 -20
- package/hub/team/cli/help.mjs +1 -1
- package/hub/team/cli/services/runtime-mode.mjs +6 -3
- package/hub/team/execution-mode.mjs +27 -12
- package/hub/team/headless.mjs +191 -43
- package/hub/team/interactive-native-launcher.mjs +106 -0
- package/hub/team/interactive-native-worker.mjs +80 -0
- package/hub/team/interactive-tui-transport.mjs +465 -0
- package/hub/team/launcher-template.mjs +28 -6
- package/hub/team/native.mjs +46 -7
- package/hub/team/pane.mjs +0 -5
- package/hub/team/psmux.mjs +3 -3
- package/hub/team/retry-state-machine.mjs +83 -14
- package/hub/team/session.mjs +5 -36
- package/hub/team/swarm-hypervisor.mjs +28 -6
- package/hub/team/swarm-preflight.mjs +3 -1
- package/hub/team/terminal-opener.mjs +1 -1
- package/hub/team/tui-core.mjs +4 -0
- package/hub/team/tui-lite.mjs +3 -2
- package/hub/team/tui-viewer.mjs +4 -4
- package/hub/team/tui.mjs +4 -2
- package/hub/team/worker-sandbox.mjs +4 -0
- package/hub/tools.mjs +4 -1
- package/hub/workers/antigravity-route-worker.mjs +169 -0
- package/hub/workers/delegator-mcp.mjs +58 -314
- package/hub/workers/factory.mjs +7 -4
- package/hub/workers/gemini-worker.mjs +55 -306
- package/hub/workers/interface.mjs +2 -1
- package/hub/workers/worker-utils.mjs +1 -1
- package/package.json +1 -1
- package/scripts/lib/cli-codex.mjs +3 -3
- package/scripts/lib/cli-gemini.mjs +20 -50
- package/scripts/lib/keyword-rules.mjs +1 -1
- package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
- package/scripts/lib/psmux-info.mjs +50 -5
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// scripts/lib/mcp-gateway-health-check.mjs
|
|
2
|
+
//
|
|
3
|
+
// `~/.local/state/triflux/mcp-gateway.out.log` 파싱 헬퍼.
|
|
4
|
+
// install-mcp-gateway-startup.mjs 가 LaunchAgent/systemd 로 띄운 gateway daemon
|
|
5
|
+
// (mcp-gateway-start.mjs) 의 stdout 로그에서 다음 신호를 추출한다.
|
|
6
|
+
//
|
|
7
|
+
// [WARN] <server> skipped — missing env: <KEY> → missing-env finding
|
|
8
|
+
// [START] <server> on :<port> → started
|
|
9
|
+
// [SKIP] <server> already running on :<port> → started (preExisting)
|
|
10
|
+
// [SKIP] <server> — manifest에서 비활성 → manifest-disabled skip
|
|
11
|
+
//
|
|
12
|
+
// 호출자는 `findings` 만 보고 doctor 진단을 출력할 수 있다.
|
|
13
|
+
// log 파일이 없으면 `available: false` 로 반환하므로, gateway 미설치 환경에서는
|
|
14
|
+
// 침묵한다 (false-positive 방지).
|
|
15
|
+
|
|
16
|
+
import { readFileSync, statSync } from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
const DEFAULT_LOG_PATH = join(
|
|
21
|
+
homedir(),
|
|
22
|
+
".local",
|
|
23
|
+
"state",
|
|
24
|
+
"triflux",
|
|
25
|
+
"mcp-gateway.out.log",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// producer (scripts/mcp-gateway-start.mjs:146) 가 missing env 를 `missing.join(", ")`
|
|
29
|
+
// 로 출력하므로 detail 은 공백 포함 다중 키 (예: "JIRA_API_TOKEN, JIRA_EMAIL, JIRA_INSTANCE_URL")
|
|
30
|
+
// 일 수 있다. line 끝까지 캡처한다.
|
|
31
|
+
const MISSING_ENV_RE = /^\[WARN\]\s+(\S+)\s+skipped\s+—\s+missing env:\s+(.+)$/;
|
|
32
|
+
const START_RE = /^\[START\]\s+(\S+)\s+on\s+:(\d+)$/;
|
|
33
|
+
const SKIP_RUNNING_RE = /^\[SKIP\]\s+(\S+)\s+already running on\s+:(\d+)$/;
|
|
34
|
+
const SKIP_DISABLED_RE = /^\[SKIP\]\s+(\S+)\s+—\s+manifest에서 비활성$/;
|
|
35
|
+
|
|
36
|
+
export function checkMcpGatewayHealth({
|
|
37
|
+
fs = { readFileSync, statSync },
|
|
38
|
+
logPath = DEFAULT_LOG_PATH,
|
|
39
|
+
} = {}) {
|
|
40
|
+
try {
|
|
41
|
+
fs.statSync(logPath);
|
|
42
|
+
} catch {
|
|
43
|
+
return {
|
|
44
|
+
available: false,
|
|
45
|
+
logPath,
|
|
46
|
+
findings: [],
|
|
47
|
+
started: [],
|
|
48
|
+
skipped: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let text;
|
|
53
|
+
try {
|
|
54
|
+
text = fs.readFileSync(logPath, "utf8");
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return {
|
|
57
|
+
available: true,
|
|
58
|
+
logPath,
|
|
59
|
+
findings: [
|
|
60
|
+
{
|
|
61
|
+
server: null,
|
|
62
|
+
reason: "log-read-error",
|
|
63
|
+
detail: error?.message || String(error),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
started: [],
|
|
67
|
+
skipped: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// log 는 append-only 라 옛 [WARN] 라인 뒤에 새 [START] 라인이 올 수 있다.
|
|
72
|
+
// server 별 마지막 이벤트가 현재 상태이므로 그것만 finding 으로 환원한다.
|
|
73
|
+
// (예: BRAVE_API_KEY missing 으로 한 번 skip → secrets 추가 후 restart 로 start)
|
|
74
|
+
const lastEvent = new Map();
|
|
75
|
+
|
|
76
|
+
for (const rawLine of text.split("\n")) {
|
|
77
|
+
const line = rawLine.trim();
|
|
78
|
+
if (!line) continue;
|
|
79
|
+
|
|
80
|
+
let m;
|
|
81
|
+
if ((m = MISSING_ENV_RE.exec(line))) {
|
|
82
|
+
lastEvent.set(m[1], {
|
|
83
|
+
type: "missing-env",
|
|
84
|
+
detail: m[2],
|
|
85
|
+
});
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if ((m = START_RE.exec(line))) {
|
|
89
|
+
lastEvent.set(m[1], {
|
|
90
|
+
type: "started",
|
|
91
|
+
port: Number(m[2]),
|
|
92
|
+
preExisting: false,
|
|
93
|
+
});
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if ((m = SKIP_RUNNING_RE.exec(line))) {
|
|
97
|
+
lastEvent.set(m[1], {
|
|
98
|
+
type: "started",
|
|
99
|
+
port: Number(m[2]),
|
|
100
|
+
preExisting: true,
|
|
101
|
+
});
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if ((m = SKIP_DISABLED_RE.exec(line))) {
|
|
105
|
+
lastEvent.set(m[1], {
|
|
106
|
+
type: "manifest-disabled",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const findings = [];
|
|
112
|
+
const started = [];
|
|
113
|
+
const skipped = [];
|
|
114
|
+
for (const [server, ev] of lastEvent) {
|
|
115
|
+
if (ev.type === "missing-env") {
|
|
116
|
+
findings.push({ server, reason: "missing-env", detail: ev.detail });
|
|
117
|
+
} else if (ev.type === "started") {
|
|
118
|
+
started.push({ server, port: ev.port, preExisting: ev.preExisting });
|
|
119
|
+
} else if (ev.type === "manifest-disabled") {
|
|
120
|
+
skipped.push({ server, reason: "manifest-disabled" });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
available: true,
|
|
126
|
+
logPath,
|
|
127
|
+
findings,
|
|
128
|
+
started,
|
|
129
|
+
skipped,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function summarizeMcpGatewayHealth(result) {
|
|
134
|
+
if (!result?.available) {
|
|
135
|
+
return {
|
|
136
|
+
level: "skip",
|
|
137
|
+
message: "mcp-gateway log 없음 (gateway 미설치 또는 미실행)",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (result.findings.length === 0) {
|
|
141
|
+
return {
|
|
142
|
+
level: "ok",
|
|
143
|
+
message: `mcp-gateway healthy (${result.started.length}개 server up)`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const missingEnv = result.findings.filter((f) => f.reason === "missing-env");
|
|
147
|
+
if (missingEnv.length > 0) {
|
|
148
|
+
const list = missingEnv.map((f) => `${f.server} (${f.detail})`).join(", ");
|
|
149
|
+
return {
|
|
150
|
+
level: "warn",
|
|
151
|
+
message: `${missingEnv.length}개 MCP 서버가 missing env 로 skip: ${list}`,
|
|
152
|
+
fix: "secrets 를 ~/.config/triflux/secrets.env 에 추가하고 `launchctl kickstart -k gui/$(id -u)/com.tellang.mcp-gateway` (macOS) 또는 `systemctl --user restart mcp-gateway` (linux)",
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
level: "warn",
|
|
157
|
+
message: `${result.findings.length}개 gateway 이슈`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export async function checkWrapperSourcing({
|
|
6
|
+
wrapperPath = path.join(
|
|
7
|
+
os.homedir(),
|
|
8
|
+
".local",
|
|
9
|
+
"bin",
|
|
10
|
+
"mcp-gateway-wrapper.sh",
|
|
11
|
+
),
|
|
12
|
+
marker = "secrets.env",
|
|
13
|
+
} = {}) {
|
|
14
|
+
let text;
|
|
15
|
+
try {
|
|
16
|
+
text = await readFile(wrapperPath, "utf8");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const result = { status: "missing", wrapperPath };
|
|
19
|
+
if (error?.code !== "ENOENT") {
|
|
20
|
+
result.message = `wrapper read failed: ${error?.message || String(error)}`;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (text.includes(marker)) {
|
|
26
|
+
return { status: "ok", wrapperPath };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
status: "warn",
|
|
31
|
+
wrapperPath,
|
|
32
|
+
message: "wrapper exists but does not source secrets.env",
|
|
33
|
+
suggestedFix: "Re-run: node scripts/install-mcp-gateway-startup.mjs",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -12,14 +12,14 @@ export const PSMUX_OPTIONAL_COMMANDS = ["detach-client"];
|
|
|
12
12
|
|
|
13
13
|
// Windows 패키지 매니저 — winget/scoop/choco 는 Windows 전용
|
|
14
14
|
export const PSMUX_INSTALL_COMMANDS = [
|
|
15
|
-
"winget install
|
|
15
|
+
"winget install psmux",
|
|
16
16
|
"scoop install psmux",
|
|
17
17
|
"choco install psmux",
|
|
18
18
|
"cargo install psmux",
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
export const PSMUX_UPDATE_COMMANDS = [
|
|
22
|
-
"winget upgrade
|
|
22
|
+
"winget upgrade psmux",
|
|
23
23
|
"scoop update psmux",
|
|
24
24
|
"choco upgrade psmux",
|
|
25
25
|
"cargo install psmux --force",
|
|
@@ -42,14 +42,15 @@ export const PSMUX_INSTALL_COMMANDS_LINUX = ["cargo install psmux"];
|
|
|
42
42
|
export const PSMUX_UPDATE_COMMANDS_LINUX = ["cargo install psmux --force"];
|
|
43
43
|
|
|
44
44
|
// PR #258 OS-primary 분기 정책: macOS/Linux 는 tmux 가 POSIX 표준 primary,
|
|
45
|
-
// psmux 는
|
|
45
|
+
// psmux 는 선택형이다. 따라서 macOS/Linux 에서 "psmux 미설치"는 fallback 사유가
|
|
46
|
+
// 아니며, tmux 까지 없을 때만 native/in-process fallback 을 고려한다.
|
|
46
47
|
export const PSMUX_FALLBACK_NOTE_DARWIN =
|
|
47
48
|
"macOS 에서는 tmux 가 표준 멀티플렉서이며, " +
|
|
48
|
-
"
|
|
49
|
+
"psmux 미설치는 fallback 사유가 아닙니다. tmux 도 없을 때만 in-process/native fallback 을 사용합니다.";
|
|
49
50
|
|
|
50
51
|
export const PSMUX_FALLBACK_NOTE_LINUX =
|
|
51
52
|
"Linux 에서는 tmux 가 표준 멀티플렉서이며, " +
|
|
52
|
-
"
|
|
53
|
+
"psmux 미설치는 fallback 사유가 아닙니다. tmux 도 없을 때만 in-process/native fallback 을 사용합니다.";
|
|
53
54
|
|
|
54
55
|
export function getPsmuxInstallCommandsFor(platform = process.platform) {
|
|
55
56
|
if (platform === "darwin") return PSMUX_INSTALL_COMMANDS_DARWIN;
|
|
@@ -177,3 +178,47 @@ export function probePsmuxSupport(options = {}) {
|
|
|
177
178
|
};
|
|
178
179
|
}
|
|
179
180
|
}
|
|
181
|
+
|
|
182
|
+
export function probePrimaryMultiplexerSupport(options = {}) {
|
|
183
|
+
const platform = options.platform || process.platform;
|
|
184
|
+
const execFileSyncFn = options.execFileSyncFn || execFileSync;
|
|
185
|
+
|
|
186
|
+
if (platform === "win32") {
|
|
187
|
+
return {
|
|
188
|
+
kind: "psmux",
|
|
189
|
+
...probePsmuxSupport(options),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const bin = options.bin || "tmux";
|
|
194
|
+
try {
|
|
195
|
+
const versionOutput = execFileSyncFn(bin, ["-V"], {
|
|
196
|
+
encoding: "utf8",
|
|
197
|
+
timeout: 2000,
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
199
|
+
windowsHide: true,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
installed: true,
|
|
204
|
+
kind: "tmux",
|
|
205
|
+
version: String(versionOutput || "").trim() || null,
|
|
206
|
+
recommendedVersion: null,
|
|
207
|
+
recommended: true,
|
|
208
|
+
missingCommands: [],
|
|
209
|
+
missingOptionalCommands: [],
|
|
210
|
+
};
|
|
211
|
+
} catch (error) {
|
|
212
|
+
return {
|
|
213
|
+
ok: false,
|
|
214
|
+
installed: false,
|
|
215
|
+
kind: "tmux",
|
|
216
|
+
version: null,
|
|
217
|
+
recommendedVersion: null,
|
|
218
|
+
recommended: false,
|
|
219
|
+
missingCommands: ["tmux"],
|
|
220
|
+
missingOptionalCommands: [],
|
|
221
|
+
error: error?.message || String(error),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|