triflux 10.3.4 → 10.7.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/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
// mcp-gateway-integration-test.mjs — P0 integration test for MCP Gateway
|
|
3
3
|
// Usage: node scripts/mcp-gateway-integration-test.mjs
|
|
4
4
|
|
|
5
|
-
import { execSync } from
|
|
6
|
-
import {
|
|
7
|
-
import { fileURLToPath } from
|
|
5
|
+
import { execSync } from "node:child_process";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const START_SCRIPT = join(SCRIPTS_DIR,
|
|
11
|
-
const CONFIG_SCRIPT = join(SCRIPTS_DIR,
|
|
10
|
+
const START_SCRIPT = join(SCRIPTS_DIR, "mcp-gateway-start.mjs");
|
|
11
|
+
const CONFIG_SCRIPT = join(SCRIPTS_DIR, "mcp-gateway-config.mjs");
|
|
12
12
|
|
|
13
13
|
const HEALTH_TIMEOUT_MS = 3000;
|
|
14
14
|
const STARTUP_WAIT_MS = 12000;
|
|
15
15
|
const POLL_INTERVAL_MS = 500;
|
|
16
16
|
|
|
17
17
|
const SERVERS = [
|
|
18
|
-
{ name:
|
|
19
|
-
{ name:
|
|
20
|
-
{ name:
|
|
21
|
-
{ name:
|
|
22
|
-
{ name:
|
|
23
|
-
{ name:
|
|
24
|
-
{ name:
|
|
25
|
-
{ name:
|
|
18
|
+
{ name: "context7", port: 8100 },
|
|
19
|
+
{ name: "brave-search", port: 8101 },
|
|
20
|
+
{ name: "exa", port: 8102 },
|
|
21
|
+
{ name: "tavily", port: 8103 },
|
|
22
|
+
{ name: "jira", port: 8104 },
|
|
23
|
+
{ name: "serena", port: 8105 },
|
|
24
|
+
{ name: "notion", port: 8106 },
|
|
25
|
+
{ name: "notion-guest", port: 8107 },
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
// ── utilities ──
|
|
@@ -43,10 +43,10 @@ async function checkHealth(port) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function runScript(scriptPath, ...args) {
|
|
46
|
-
execSync(`node "${scriptPath}" ${args.join(
|
|
47
|
-
stdio:
|
|
46
|
+
execSync(`node "${scriptPath}" ${args.join(" ")}`, {
|
|
47
|
+
stdio: "inherit",
|
|
48
48
|
timeout: 30000,
|
|
49
|
-
env: { ...process.env, MSYS_NO_PATHCONV:
|
|
49
|
+
env: { ...process.env, MSYS_NO_PATHCONV: "1" },
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -57,10 +57,10 @@ function countSupergateways() {
|
|
|
57
57
|
`$procs = Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'"`,
|
|
58
58
|
`$hits = $procs | Where-Object { $_.CommandLine -match 'supergateway' }`,
|
|
59
59
|
`Write-Output $hits.Count`,
|
|
60
|
-
].join(
|
|
60
|
+
].join("\n");
|
|
61
61
|
const out = execSync(
|
|
62
|
-
`powershell -NoProfile -Command "${ps1.replace(/\n/g,
|
|
63
|
-
{ encoding:
|
|
62
|
+
`powershell -NoProfile -Command "${ps1.replace(/\n/g, "; ")}"`,
|
|
63
|
+
{ encoding: "utf8", timeout: 10000, stdio: ["pipe", "pipe", "ignore"] },
|
|
64
64
|
);
|
|
65
65
|
return parseInt(out.trim(), 10) || 0;
|
|
66
66
|
} catch {
|
|
@@ -80,7 +80,7 @@ function pass(label) {
|
|
|
80
80
|
passed++;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function fail(label, detail =
|
|
83
|
+
function fail(label, detail = "") {
|
|
84
84
|
const msg = detail ? `${label} — ${detail}` : label;
|
|
85
85
|
console.log(` [FAIL] ${msg}`);
|
|
86
86
|
results.push({ label, ok: false, detail });
|
|
@@ -88,45 +88,45 @@ function fail(label, detail = '') {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function printSummary() {
|
|
91
|
-
console.log(
|
|
92
|
-
console.log(
|
|
93
|
-
console.log(
|
|
91
|
+
console.log("\n" + "=".repeat(56));
|
|
92
|
+
console.log("Integration Test Summary");
|
|
93
|
+
console.log("=".repeat(56));
|
|
94
94
|
for (const r of results) {
|
|
95
|
-
const mark = r.ok ?
|
|
96
|
-
const detail = r.detail ? ` (${r.detail})` :
|
|
95
|
+
const mark = r.ok ? "\u2713" : "\u2717";
|
|
96
|
+
const detail = r.detail ? ` (${r.detail})` : "";
|
|
97
97
|
console.log(` ${mark} ${r.label}${detail}`);
|
|
98
98
|
}
|
|
99
|
-
console.log(
|
|
99
|
+
console.log("=".repeat(56));
|
|
100
100
|
const total = passed + failed;
|
|
101
101
|
console.log(`Result: ${passed}/${total} passed, ${failed} failed`);
|
|
102
102
|
if (failed > 0) {
|
|
103
|
-
console.log(
|
|
103
|
+
console.log("\n[FAIL] Integration test FAILED");
|
|
104
104
|
process.exitCode = 1;
|
|
105
105
|
} else {
|
|
106
|
-
console.log(
|
|
106
|
+
console.log("\n[PASS] Integration test PASSED");
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// ── main ──
|
|
111
111
|
|
|
112
112
|
async function main() {
|
|
113
|
-
console.log(
|
|
114
|
-
console.log(
|
|
113
|
+
console.log("\nMCP Gateway Integration Test");
|
|
114
|
+
console.log("=".repeat(56));
|
|
115
115
|
|
|
116
116
|
// STEP 1: Start gateways
|
|
117
|
-
console.log(
|
|
117
|
+
console.log("\n[STEP 1] Starting gateways...");
|
|
118
118
|
try {
|
|
119
119
|
runScript(START_SCRIPT);
|
|
120
|
-
pass(
|
|
120
|
+
pass("Gateway start script ran without error");
|
|
121
121
|
} catch (err) {
|
|
122
|
-
fail(
|
|
123
|
-
console.log(
|
|
122
|
+
fail("Gateway start script", err.message);
|
|
123
|
+
console.log("\n[ABORT] Cannot continue without gateways running");
|
|
124
124
|
printSummary();
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// STEP 2: Wait for health checks to pass (up to 12s)
|
|
129
|
-
console.log(
|
|
129
|
+
console.log("\n[STEP 2] Waiting for health checks (up to 12s)...");
|
|
130
130
|
const deadline = Date.now() + STARTUP_WAIT_MS;
|
|
131
131
|
const pending = new Set(SERVERS.map((s) => s.port));
|
|
132
132
|
|
|
@@ -147,35 +147,42 @@ async function main() {
|
|
|
147
147
|
healthOk++;
|
|
148
148
|
} else {
|
|
149
149
|
// Servers that stay down are expected when env vars (API keys) are missing
|
|
150
|
-
console.log(
|
|
150
|
+
console.log(
|
|
151
|
+
` [skip] ${srv.name.padEnd(16)} :${srv.port} (not running — likely missing env)`,
|
|
152
|
+
);
|
|
151
153
|
healthSkipped++;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
if (healthOk === 0) {
|
|
156
|
-
fail(
|
|
158
|
+
fail("Health check — no servers responded", `0/${SERVERS.length} up`);
|
|
157
159
|
} else {
|
|
158
|
-
pass(
|
|
160
|
+
pass(
|
|
161
|
+
`Health check — ${healthOk} server(s) responding (${healthSkipped} skipped)`,
|
|
162
|
+
);
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
// STEP 3: Switch Claude Code config to SSE
|
|
162
|
-
console.log(
|
|
166
|
+
console.log("\n[STEP 3] Switching Claude Code config to SSE mode...");
|
|
163
167
|
try {
|
|
164
|
-
runScript(CONFIG_SCRIPT,
|
|
165
|
-
pass(
|
|
168
|
+
runScript(CONFIG_SCRIPT, "--enable");
|
|
169
|
+
pass("Config switch to SSE (--enable)");
|
|
166
170
|
} catch (err) {
|
|
167
|
-
fail(
|
|
171
|
+
fail("Config switch to SSE", err.message);
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
// STEP 4: Verify SSE endpoints respond on each active port
|
|
171
|
-
console.log(
|
|
175
|
+
console.log("\n[STEP 4] Verifying SSE endpoints (/healthz)...");
|
|
172
176
|
const sseResults = await Promise.allSettled(
|
|
173
|
-
SERVERS.map(async (srv) => ({
|
|
177
|
+
SERVERS.map(async (srv) => ({
|
|
178
|
+
...srv,
|
|
179
|
+
alive: await checkHealth(srv.port),
|
|
180
|
+
})),
|
|
174
181
|
);
|
|
175
182
|
|
|
176
183
|
let sseOk = 0;
|
|
177
184
|
for (const r of sseResults) {
|
|
178
|
-
if (r.status !==
|
|
185
|
+
if (r.status !== "fulfilled") continue;
|
|
179
186
|
const { name, port, alive } = r.value;
|
|
180
187
|
if (alive) {
|
|
181
188
|
console.log(` [ok] ${name.padEnd(16)} :${port}`);
|
|
@@ -186,40 +193,45 @@ async function main() {
|
|
|
186
193
|
}
|
|
187
194
|
|
|
188
195
|
if (sseOk === 0 && healthOk > 0) {
|
|
189
|
-
fail(
|
|
196
|
+
fail("SSE endpoint verification — servers went down after config switch");
|
|
190
197
|
} else if (sseOk > 0) {
|
|
191
198
|
pass(`SSE endpoint verification — ${sseOk} endpoint(s) healthy`);
|
|
192
199
|
} else {
|
|
193
|
-
pass(
|
|
200
|
+
pass(
|
|
201
|
+
"SSE endpoint verification — no servers running (all skipped due to missing env)",
|
|
202
|
+
);
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
// STEP 5: Restore stdio config
|
|
197
|
-
console.log(
|
|
206
|
+
console.log("\n[STEP 5] Restoring stdio config...");
|
|
198
207
|
try {
|
|
199
|
-
runScript(CONFIG_SCRIPT,
|
|
200
|
-
pass(
|
|
208
|
+
runScript(CONFIG_SCRIPT, "--disable");
|
|
209
|
+
pass("Config restore to stdio (--disable)");
|
|
201
210
|
} catch (err) {
|
|
202
|
-
fail(
|
|
211
|
+
fail("Config restore to stdio", err.message);
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
// STEP 6: Stop gateways
|
|
206
|
-
console.log(
|
|
215
|
+
console.log("\n[STEP 6] Stopping gateways...");
|
|
207
216
|
try {
|
|
208
|
-
runScript(START_SCRIPT,
|
|
209
|
-
pass(
|
|
217
|
+
runScript(START_SCRIPT, "--stop");
|
|
218
|
+
pass("Gateway stop script ran without error");
|
|
210
219
|
} catch (err) {
|
|
211
|
-
fail(
|
|
220
|
+
fail("Gateway stop script", err.message);
|
|
212
221
|
}
|
|
213
222
|
|
|
214
223
|
// STEP 7: Orphan check — brief settle, then verify no supergateway processes remain
|
|
215
|
-
console.log(
|
|
224
|
+
console.log("\n[STEP 7] Checking for orphan supergateway processes...");
|
|
216
225
|
await sleep(2000);
|
|
217
226
|
|
|
218
227
|
const orphanCount = countSupergateways();
|
|
219
228
|
if (orphanCount === 0) {
|
|
220
|
-
pass(
|
|
229
|
+
pass("No orphan supergateway processes (WMI/tasklist clean)");
|
|
221
230
|
} else {
|
|
222
|
-
fail(
|
|
231
|
+
fail(
|
|
232
|
+
"Orphan processes found",
|
|
233
|
+
`${orphanCount} supergateway process(es) still running`,
|
|
234
|
+
);
|
|
223
235
|
}
|
|
224
236
|
|
|
225
237
|
printSummary();
|
|
@@ -4,27 +4,67 @@
|
|
|
4
4
|
// node mcp-gateway-start.mjs --stop # 중지
|
|
5
5
|
// node mcp-gateway-start.mjs --status # 상태 확인
|
|
6
6
|
|
|
7
|
-
import { execSync } from
|
|
8
|
-
import { existsSync, readFileSync,
|
|
9
|
-
import {
|
|
10
|
-
import { tmpdir } from
|
|
11
|
-
import {
|
|
12
|
-
import { isServerEnabled } from
|
|
13
|
-
|
|
14
|
-
const PID_FILE = join(tmpdir(),
|
|
7
|
+
import { execSync } from "node:child_process";
|
|
8
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { createConnection } from "node:net";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { isServerEnabled } from "./lib/mcp-manifest.mjs";
|
|
13
|
+
|
|
14
|
+
const PID_FILE = join(tmpdir(), "tfx-gateway-pids.json");
|
|
15
15
|
const STARTUP_WAIT_MS = 8000;
|
|
16
16
|
const POLL_INTERVAL_MS = 500;
|
|
17
17
|
const HEALTH_TIMEOUT_MS = 3000;
|
|
18
18
|
|
|
19
19
|
const SERVERS = [
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
|
|
20
|
+
{
|
|
21
|
+
name: "context7",
|
|
22
|
+
port: 8100,
|
|
23
|
+
cmd: "npx -y @upstash/context7-mcp@latest",
|
|
24
|
+
envVars: [],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "brave-search",
|
|
28
|
+
port: 8101,
|
|
29
|
+
cmd: "npx -y @brave/brave-search-mcp-server",
|
|
30
|
+
envVars: ["BRAVE_API_KEY"],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "exa",
|
|
34
|
+
port: 8102,
|
|
35
|
+
cmd: "npx -y exa-mcp-server",
|
|
36
|
+
envVars: ["EXA_API_KEY"],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "tavily",
|
|
40
|
+
port: 8103,
|
|
41
|
+
cmd: "npx -y tavily-mcp@latest",
|
|
42
|
+
envVars: ["TAVILY_API_KEY"],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "jira",
|
|
46
|
+
port: 8104,
|
|
47
|
+
cmd: "npx -y mcp-jira-cloud@latest",
|
|
48
|
+
envVars: ["JIRA_API_TOKEN", "JIRA_EMAIL", "JIRA_INSTANCE_URL"],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "serena",
|
|
52
|
+
port: 8105,
|
|
53
|
+
cmd: "uvx --from git+https://github.com/oraios/serena serena start-mcp-server",
|
|
54
|
+
envVars: [],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "notion",
|
|
58
|
+
port: 8106,
|
|
59
|
+
cmd: "npx -y @notionhq/notion-mcp-server",
|
|
60
|
+
envVars: ["NOTION_TOKEN"],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "notion-guest",
|
|
64
|
+
port: 8107,
|
|
65
|
+
cmd: "npx -y @notionhq/notion-mcp-server",
|
|
66
|
+
envVars: ["NOTION_TOKEN"],
|
|
67
|
+
},
|
|
28
68
|
];
|
|
29
69
|
|
|
30
70
|
export { SERVERS };
|
|
@@ -33,10 +73,16 @@ export { SERVERS };
|
|
|
33
73
|
|
|
34
74
|
function isPortInUse(port) {
|
|
35
75
|
return new Promise((resolve) => {
|
|
36
|
-
const sock = createConnection({ host:
|
|
37
|
-
sock.once(
|
|
38
|
-
|
|
39
|
-
|
|
76
|
+
const sock = createConnection({ host: "127.0.0.1", port });
|
|
77
|
+
sock.once("connect", () => {
|
|
78
|
+
sock.destroy();
|
|
79
|
+
resolve(true);
|
|
80
|
+
});
|
|
81
|
+
sock.once("error", () => resolve(false));
|
|
82
|
+
sock.setTimeout(1000, () => {
|
|
83
|
+
sock.destroy();
|
|
84
|
+
resolve(false);
|
|
85
|
+
});
|
|
40
86
|
});
|
|
41
87
|
}
|
|
42
88
|
|
|
@@ -65,31 +111,38 @@ function spawnGateway(srv) {
|
|
|
65
111
|
|
|
66
112
|
// PowerShell Start-Process: Windows Job Object에서 벗어나 부모 종료 후 생존
|
|
67
113
|
execSync(
|
|
68
|
-
`powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath cmd.exe -ArgumentList '/c','${cmdFile.replaceAll("'", "''")}'"
|
|
69
|
-
|
|
114
|
+
`powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath cmd.exe -ArgumentList '/c','${cmdFile.replaceAll("'", "''")}'"`,
|
|
115
|
+
{ stdio: "ignore", timeout: 10000 },
|
|
116
|
+
);
|
|
70
117
|
}
|
|
71
118
|
|
|
72
119
|
function ensureFirewallRule() {
|
|
73
|
-
if (process.platform !==
|
|
74
|
-
const ports = SERVERS.map((s) => s.port).join(
|
|
75
|
-
const ruleName =
|
|
120
|
+
if (process.platform !== "win32") return;
|
|
121
|
+
const ports = SERVERS.map((s) => s.port).join(",");
|
|
122
|
+
const ruleName = "TFX-MCP-Gateway-Block-External";
|
|
76
123
|
try {
|
|
77
124
|
// 기존 규칙 있으면 스킵
|
|
78
125
|
const check = execSync(
|
|
79
126
|
`netsh advfirewall firewall show rule name="${ruleName}" 2>&1`,
|
|
80
|
-
{ encoding:
|
|
127
|
+
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 },
|
|
81
128
|
);
|
|
82
129
|
if (check.includes(ruleName)) return;
|
|
83
|
-
} catch {
|
|
130
|
+
} catch {
|
|
131
|
+
/* 규칙 없음 — 생성 */
|
|
132
|
+
}
|
|
84
133
|
|
|
85
134
|
try {
|
|
86
135
|
execSync(
|
|
87
136
|
`netsh advfirewall firewall add rule name="${ruleName}" dir=in action=block protocol=tcp localport=${ports} remoteip=any profile=any`,
|
|
88
|
-
{ stdio:
|
|
137
|
+
{ stdio: "ignore", timeout: 5000 },
|
|
138
|
+
);
|
|
139
|
+
console.log(
|
|
140
|
+
`[SEC] Firewall rule added: block external access to ports ${ports}`,
|
|
89
141
|
);
|
|
90
|
-
console.log(`[SEC] Firewall rule added: block external access to ports ${ports}`);
|
|
91
142
|
} catch {
|
|
92
|
-
console.log(
|
|
143
|
+
console.log(
|
|
144
|
+
`[SEC] WARNING: Could not add firewall rule — run as admin or manually block ports ${ports}`,
|
|
145
|
+
);
|
|
93
146
|
}
|
|
94
147
|
}
|
|
95
148
|
|
|
@@ -113,7 +166,9 @@ async function startAll() {
|
|
|
113
166
|
// 필수 환경변수 체크
|
|
114
167
|
const missing = srv.envVars.filter((k) => !process.env[k]);
|
|
115
168
|
if (missing.length > 0) {
|
|
116
|
-
console.log(
|
|
169
|
+
console.log(
|
|
170
|
+
`[WARN] ${srv.name} skipped — missing env: ${missing.join(", ")}`,
|
|
171
|
+
);
|
|
117
172
|
continue;
|
|
118
173
|
}
|
|
119
174
|
|
|
@@ -123,7 +178,7 @@ async function startAll() {
|
|
|
123
178
|
}
|
|
124
179
|
|
|
125
180
|
if (launched.length === 0) {
|
|
126
|
-
console.log(
|
|
181
|
+
console.log("\n[gateway] No servers started (all running or skipped)");
|
|
127
182
|
return;
|
|
128
183
|
}
|
|
129
184
|
|
|
@@ -140,22 +195,27 @@ async function startAll() {
|
|
|
140
195
|
}
|
|
141
196
|
|
|
142
197
|
// 결과 출력
|
|
143
|
-
console.log(
|
|
144
|
-
console.log(
|
|
198
|
+
console.log("\nHealth Check");
|
|
199
|
+
console.log("=".repeat(50));
|
|
145
200
|
const pidEntries = [];
|
|
146
201
|
for (const srv of launched) {
|
|
147
202
|
const healthy = !pending.has(srv.port);
|
|
148
|
-
const mark = healthy ?
|
|
149
|
-
const status = healthy ?
|
|
203
|
+
const mark = healthy ? "\u2713" : "\u2717";
|
|
204
|
+
const status = healthy ? "ok" : "down";
|
|
150
205
|
console.log(` ${srv.name.padEnd(16)} :${srv.port} ${mark} ${status}`);
|
|
151
206
|
if (healthy) pidEntries.push({ name: srv.name, port: srv.port });
|
|
152
207
|
}
|
|
153
208
|
|
|
154
209
|
// PID 파일 대신 포트 매니페스트 저장 (프로세스 찾기는 포트 기반)
|
|
155
210
|
const existing = loadManifest();
|
|
156
|
-
const merged = [
|
|
211
|
+
const merged = [
|
|
212
|
+
...existing.filter((e) => !pidEntries.some((p) => p.port === e.port)),
|
|
213
|
+
...pidEntries,
|
|
214
|
+
];
|
|
157
215
|
writeFileSync(PID_FILE, JSON.stringify(merged, null, 2));
|
|
158
|
-
console.log(
|
|
216
|
+
console.log(
|
|
217
|
+
`\n[gateway] ${launched.length - pending.size}/${launched.length} healthy. Manifest: ${PID_FILE}`,
|
|
218
|
+
);
|
|
159
219
|
}
|
|
160
220
|
|
|
161
221
|
// ── 중지 ──
|
|
@@ -164,26 +224,32 @@ function stopAll() {
|
|
|
164
224
|
// supergateway + 하위 MCP 프로세스를 포트 기반으로 찾아 종료
|
|
165
225
|
try {
|
|
166
226
|
// temp .ps1 파일로 bash/cmd 쿼팅 충돌 회피
|
|
167
|
-
const psFile = join(tmpdir(),
|
|
168
|
-
writeFileSync(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
227
|
+
const psFile = join(tmpdir(), "tfx-sg-stop.ps1");
|
|
228
|
+
writeFileSync(
|
|
229
|
+
psFile,
|
|
230
|
+
[
|
|
231
|
+
`Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='cmd.exe'" |`,
|
|
232
|
+
` Where-Object { $_.CommandLine -match 'supergateway' } |`,
|
|
233
|
+
` ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null; Write-Output "[STOP] PID $($_.ProcessId)" }`,
|
|
234
|
+
].join("\n"),
|
|
235
|
+
);
|
|
236
|
+
const output = execSync(
|
|
237
|
+
`powershell -NoProfile -ExecutionPolicy Bypass -File "${psFile}"`,
|
|
238
|
+
{
|
|
239
|
+
encoding: "utf8",
|
|
240
|
+
timeout: 10000,
|
|
241
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
242
|
+
},
|
|
243
|
+
);
|
|
178
244
|
if (output.trim()) console.log(output.trim());
|
|
179
|
-
else console.log(
|
|
245
|
+
else console.log("[gateway] No supergateway processes found");
|
|
180
246
|
} catch {
|
|
181
|
-
console.log(
|
|
247
|
+
console.log("[gateway] No supergateway processes found");
|
|
182
248
|
}
|
|
183
249
|
|
|
184
250
|
if (existsSync(PID_FILE)) {
|
|
185
251
|
unlinkSync(PID_FILE);
|
|
186
|
-
console.log(
|
|
252
|
+
console.log("[gateway] Manifest removed");
|
|
187
253
|
}
|
|
188
254
|
}
|
|
189
255
|
|
|
@@ -192,15 +258,15 @@ function stopAll() {
|
|
|
192
258
|
async function showStatus() {
|
|
193
259
|
const manifest = loadManifest();
|
|
194
260
|
if (manifest.length === 0) {
|
|
195
|
-
console.log(
|
|
261
|
+
console.log("[gateway] No manifest — checking all ports...");
|
|
196
262
|
}
|
|
197
263
|
|
|
198
|
-
console.log(
|
|
199
|
-
console.log(
|
|
264
|
+
console.log("\nMCP Gateway Status");
|
|
265
|
+
console.log("=".repeat(50));
|
|
200
266
|
for (const srv of SERVERS) {
|
|
201
267
|
const healthy = await checkHealth(srv.port);
|
|
202
|
-
const mark = healthy ?
|
|
203
|
-
const status = healthy ?
|
|
268
|
+
const mark = healthy ? "\u2713" : "\u2717";
|
|
269
|
+
const status = healthy ? "ok" : "down";
|
|
204
270
|
console.log(` ${srv.name.padEnd(16)} :${srv.port} ${mark} ${status}`);
|
|
205
271
|
}
|
|
206
272
|
}
|
|
@@ -208,7 +274,7 @@ async function showStatus() {
|
|
|
208
274
|
function loadManifest() {
|
|
209
275
|
if (!existsSync(PID_FILE)) return [];
|
|
210
276
|
try {
|
|
211
|
-
return JSON.parse(readFileSync(PID_FILE,
|
|
277
|
+
return JSON.parse(readFileSync(PID_FILE, "utf8"));
|
|
212
278
|
} catch {
|
|
213
279
|
return [];
|
|
214
280
|
}
|
|
@@ -217,9 +283,9 @@ function loadManifest() {
|
|
|
217
283
|
// ── main ──
|
|
218
284
|
|
|
219
285
|
const flag = process.argv[2];
|
|
220
|
-
if (flag ===
|
|
286
|
+
if (flag === "--stop") {
|
|
221
287
|
stopAll();
|
|
222
|
-
} else if (flag ===
|
|
288
|
+
} else if (flag === "--status") {
|
|
223
289
|
await showStatus();
|
|
224
290
|
} else {
|
|
225
291
|
await startAll();
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// mcp-gateway-verify.mjs — supergateway SSE 엔드포인트 헬스체크
|
|
3
3
|
|
|
4
|
-
import { readManifest } from
|
|
4
|
+
import { readManifest } from "./lib/mcp-manifest.mjs";
|
|
5
5
|
|
|
6
6
|
const ALL_ENDPOINTS = [
|
|
7
|
-
{ name:
|
|
8
|
-
{ name:
|
|
9
|
-
{ name:
|
|
10
|
-
{ name:
|
|
11
|
-
{ name:
|
|
12
|
-
{ name:
|
|
13
|
-
{ name:
|
|
14
|
-
{ name:
|
|
7
|
+
{ name: "context7", port: 8100 },
|
|
8
|
+
{ name: "brave-search", port: 8101 },
|
|
9
|
+
{ name: "exa", port: 8102 },
|
|
10
|
+
{ name: "tavily", port: 8103 },
|
|
11
|
+
{ name: "jira", port: 8104 },
|
|
12
|
+
{ name: "serena", port: 8105 },
|
|
13
|
+
{ name: "notion", port: 8106 },
|
|
14
|
+
{ name: "notion-guest", port: 8107 },
|
|
15
15
|
];
|
|
16
16
|
|
|
17
17
|
const manifest = readManifest();
|
|
18
18
|
if (!manifest) {
|
|
19
|
-
console.log(
|
|
19
|
+
console.log("gateway: not configured (no manifest)");
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
22
22
|
const enabled = new Set(manifest.enabled || []);
|
|
23
23
|
const ENDPOINTS = ALL_ENDPOINTS.filter((e) => enabled.has(e.name));
|
|
24
24
|
if (ENDPOINTS.length === 0) {
|
|
25
|
-
console.log(
|
|
25
|
+
console.log("gateway: no enabled servers");
|
|
26
26
|
process.exit(0);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -34,12 +34,12 @@ async function checkHealth(name, port) {
|
|
|
34
34
|
});
|
|
35
35
|
const latencyMs = Date.now() - start;
|
|
36
36
|
return res.ok
|
|
37
|
-
? { name, port, status:
|
|
38
|
-
: { name, port, status:
|
|
37
|
+
? { name, port, status: "ok", latencyMs, error: null }
|
|
38
|
+
: { name, port, status: "down", latencyMs, error: `HTTP ${res.status}` };
|
|
39
39
|
} catch (err) {
|
|
40
40
|
const latencyMs = Date.now() - start;
|
|
41
|
-
const message = err?.cause?.code || err?.message ||
|
|
42
|
-
return { name, port, status:
|
|
41
|
+
const message = err?.cause?.code || err?.message || "unknown";
|
|
42
|
+
return { name, port, status: "down", latencyMs, error: message };
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -49,22 +49,24 @@ async function main() {
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
const entries = results.map((r) =>
|
|
52
|
-
r.status ===
|
|
52
|
+
r.status === "fulfilled"
|
|
53
|
+
? r.value
|
|
54
|
+
: { name: "?", port: 0, status: "down", error: r.reason },
|
|
53
55
|
);
|
|
54
56
|
|
|
55
|
-
console.log(
|
|
56
|
-
console.log(
|
|
57
|
+
console.log("\nMCP Gateway Health Check");
|
|
58
|
+
console.log("=".repeat(56));
|
|
57
59
|
|
|
58
60
|
let downCount = 0;
|
|
59
61
|
for (const e of entries) {
|
|
60
|
-
const mark = e.status ===
|
|
61
|
-
const detail = e.status ===
|
|
62
|
+
const mark = e.status === "ok" ? "\u2713" : "\u2717";
|
|
63
|
+
const detail = e.status === "ok" ? `(${e.latencyMs}ms)` : `(${e.error})`;
|
|
62
64
|
const line = ` ${e.name.padEnd(16)} :${String(e.port).padEnd(6)} ${mark} ${e.status.padEnd(6)} ${detail}`;
|
|
63
65
|
console.log(line);
|
|
64
|
-
if (e.status !==
|
|
66
|
+
if (e.status !== "ok") downCount++;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
console.log(
|
|
69
|
+
console.log("=".repeat(56));
|
|
68
70
|
console.log(
|
|
69
71
|
downCount === 0
|
|
70
72
|
? `All ${entries.length} gateways healthy`
|