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
package/scripts/mcp-check.mjs
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// MCP inventory cache for dynamic MCP filtering.
|
|
3
3
|
|
|
4
|
-
import { execSync } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
writeFileSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
MCP_SERVER_TOOL_CATALOG,
|
|
18
|
+
normalizeServerMetadata,
|
|
19
|
+
} from "./lib/mcp-server-catalog.mjs";
|
|
20
|
+
|
|
21
|
+
const CACHE_DIR = join(homedir(), ".claude", "cache");
|
|
22
|
+
const CACHE_FILE = join(CACHE_DIR, "mcp-inventory.json");
|
|
14
23
|
|
|
15
24
|
function countConfiguredTools(config = {}, fallbackToolCount = 0) {
|
|
16
|
-
const directKeys = [
|
|
25
|
+
const directKeys = ["tools", "toolNames", "allowedTools", "includeTools"];
|
|
17
26
|
for (const key of directKeys) {
|
|
18
27
|
if (Array.isArray(config[key])) return config[key].length;
|
|
19
28
|
}
|
|
@@ -26,7 +35,7 @@ function countConfiguredTools(config = {}, fallbackToolCount = 0) {
|
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
export function createServerRecord(name, status, config = {}) {
|
|
29
|
-
const normalizedName = typeof name ===
|
|
38
|
+
const normalizedName = typeof name === "string" ? name.trim() : "";
|
|
30
39
|
const fallback = normalizeServerMetadata(normalizedName, {});
|
|
31
40
|
const toolCount = countConfiguredTools(config, fallback.tool_count);
|
|
32
41
|
const domainTags = Array.isArray(config.domain_tags)
|
|
@@ -51,13 +60,16 @@ export function createServerRecord(name, status, config = {}) {
|
|
|
51
60
|
|
|
52
61
|
export function getCodexMcp() {
|
|
53
62
|
try {
|
|
54
|
-
const output = execSync(
|
|
55
|
-
encoding:
|
|
63
|
+
const output = execSync("codex mcp list", {
|
|
64
|
+
encoding: "utf8",
|
|
56
65
|
timeout: 15000,
|
|
57
|
-
stdio: [
|
|
66
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
58
67
|
windowsHide: true,
|
|
59
68
|
});
|
|
60
|
-
const lines = output
|
|
69
|
+
const lines = output
|
|
70
|
+
.trim()
|
|
71
|
+
.split(/\r?\n/)
|
|
72
|
+
.filter((line) => line.trim());
|
|
61
73
|
if (lines.length < 2) return [];
|
|
62
74
|
|
|
63
75
|
const servers = [];
|
|
@@ -67,8 +79,8 @@ export function getCodexMcp() {
|
|
|
67
79
|
|
|
68
80
|
const name = cols[0].trim();
|
|
69
81
|
const statusMatch = lines[i].match(/\b(enabled|disabled)\b/i);
|
|
70
|
-
const status = statusMatch ? statusMatch[1].toLowerCase() :
|
|
71
|
-
if (!name || name.startsWith(
|
|
82
|
+
const status = statusMatch ? statusMatch[1].toLowerCase() : "unknown";
|
|
83
|
+
if (!name || name.startsWith("-")) continue;
|
|
72
84
|
servers.push(createServerRecord(name, status));
|
|
73
85
|
}
|
|
74
86
|
return servers;
|
|
@@ -79,12 +91,14 @@ export function getCodexMcp() {
|
|
|
79
91
|
|
|
80
92
|
export function getGeminiMcp() {
|
|
81
93
|
try {
|
|
82
|
-
const settingsPath = join(homedir(),
|
|
94
|
+
const settingsPath = join(homedir(), ".gemini", "settings.json");
|
|
83
95
|
if (!existsSync(settingsPath)) return null;
|
|
84
96
|
|
|
85
|
-
const settings = JSON.parse(readFileSync(settingsPath,
|
|
97
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
86
98
|
const mcpServers = settings.mcpServers || {};
|
|
87
|
-
return Object.entries(mcpServers).map(([name, config]) =>
|
|
99
|
+
return Object.entries(mcpServers).map(([name, config]) =>
|
|
100
|
+
createServerRecord(name, "configured", config || {}),
|
|
101
|
+
);
|
|
88
102
|
} catch {
|
|
89
103
|
return null;
|
|
90
104
|
}
|
|
@@ -92,20 +106,20 @@ export function getGeminiMcp() {
|
|
|
92
106
|
|
|
93
107
|
// ── Claude MCP 서버 발견 ──
|
|
94
108
|
|
|
95
|
-
const CLAUDE_DIR = join(homedir(),
|
|
109
|
+
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
96
110
|
|
|
97
111
|
// Anthropic 클라우드 호스팅 MCP 서버 — Claude Code 런타임에서 항상 가용.
|
|
98
112
|
// mcp-server-catalog.mjs의 MCP_SERVER_TOOL_CATALOG에 정의된 서버 중
|
|
99
113
|
// 로컬 설치가 불필요한 클라우드 제공 서버.
|
|
100
114
|
const WELL_KNOWN_CLOUD_SERVERS = Object.freeze([
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
"brave-search",
|
|
116
|
+
"exa",
|
|
117
|
+
"context7",
|
|
104
118
|
]);
|
|
105
119
|
|
|
106
120
|
function readJsonSafe(filePath) {
|
|
107
121
|
try {
|
|
108
|
-
return JSON.parse(readFileSync(filePath,
|
|
122
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
109
123
|
} catch {
|
|
110
124
|
return null;
|
|
111
125
|
}
|
|
@@ -113,9 +127,9 @@ function readJsonSafe(filePath) {
|
|
|
113
127
|
|
|
114
128
|
function extractMcpServerNames(mcpJsonPath) {
|
|
115
129
|
const data = readJsonSafe(mcpJsonPath);
|
|
116
|
-
if (!data?.mcpServers || typeof data.mcpServers !==
|
|
130
|
+
if (!data?.mcpServers || typeof data.mcpServers !== "object") return [];
|
|
117
131
|
return Object.entries(data.mcpServers)
|
|
118
|
-
.filter(([name]) => typeof name ===
|
|
132
|
+
.filter(([name]) => typeof name === "string" && name.trim())
|
|
119
133
|
.map(([name, config]) => ({ name: name.trim(), config: config || {} }));
|
|
120
134
|
}
|
|
121
135
|
|
|
@@ -123,7 +137,7 @@ function walkUpForMcpJson(startDir, maxDepth = 5) {
|
|
|
123
137
|
const found = [];
|
|
124
138
|
let dir = resolve(startDir);
|
|
125
139
|
for (let i = 0; i < maxDepth; i += 1) {
|
|
126
|
-
const candidate = join(dir,
|
|
140
|
+
const candidate = join(dir, ".mcp.json");
|
|
127
141
|
if (existsSync(candidate)) found.push(candidate);
|
|
128
142
|
const parent = dirname(dir);
|
|
129
143
|
if (parent === dir) break;
|
|
@@ -133,22 +147,24 @@ function walkUpForMcpJson(startDir, maxDepth = 5) {
|
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
function scanPluginsMcpJson() {
|
|
136
|
-
const pluginsDir = join(CLAUDE_DIR,
|
|
150
|
+
const pluginsDir = join(CLAUDE_DIR, "plugins");
|
|
137
151
|
if (!existsSync(pluginsDir)) return [];
|
|
138
152
|
const found = [];
|
|
139
153
|
const walk = (dir, depth = 0) => {
|
|
140
154
|
if (depth > 4) return;
|
|
141
155
|
try {
|
|
142
156
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
143
|
-
if (entry.name ===
|
|
157
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
144
158
|
const full = join(dir, entry.name);
|
|
145
|
-
if (entry.isFile() && entry.name ===
|
|
159
|
+
if (entry.isFile() && entry.name === ".mcp.json") {
|
|
146
160
|
found.push(full);
|
|
147
161
|
} else if (entry.isDirectory()) {
|
|
148
162
|
walk(full, depth + 1);
|
|
149
163
|
}
|
|
150
164
|
}
|
|
151
|
-
} catch {
|
|
165
|
+
} catch {
|
|
166
|
+
/* permission errors */
|
|
167
|
+
}
|
|
152
168
|
};
|
|
153
169
|
walk(pluginsDir);
|
|
154
170
|
return found;
|
|
@@ -161,7 +177,7 @@ export function getClaudeMcp(cwd = process.cwd()) {
|
|
|
161
177
|
for (const mcpJson of walkUpForMcpJson(cwd)) {
|
|
162
178
|
for (const { name, config } of extractMcpServerNames(mcpJson)) {
|
|
163
179
|
if (!serverMap.has(name)) {
|
|
164
|
-
serverMap.set(name, createServerRecord(name,
|
|
180
|
+
serverMap.set(name, createServerRecord(name, "configured", config));
|
|
165
181
|
}
|
|
166
182
|
}
|
|
167
183
|
}
|
|
@@ -170,19 +186,22 @@ export function getClaudeMcp(cwd = process.cwd()) {
|
|
|
170
186
|
for (const mcpJson of scanPluginsMcpJson()) {
|
|
171
187
|
for (const { name, config } of extractMcpServerNames(mcpJson)) {
|
|
172
188
|
if (!serverMap.has(name)) {
|
|
173
|
-
serverMap.set(name, createServerRecord(name,
|
|
189
|
+
serverMap.set(name, createServerRecord(name, "configured", config));
|
|
174
190
|
}
|
|
175
191
|
}
|
|
176
192
|
}
|
|
177
193
|
|
|
178
194
|
// 3) ~/.claude/settings.json + settings.local.json의 mcpServers
|
|
179
|
-
for (const settingsFile of [
|
|
195
|
+
for (const settingsFile of ["settings.json", "settings.local.json"]) {
|
|
180
196
|
const data = readJsonSafe(join(CLAUDE_DIR, settingsFile));
|
|
181
197
|
if (!data?.mcpServers) continue;
|
|
182
198
|
for (const [name, config] of Object.entries(data.mcpServers)) {
|
|
183
|
-
if (typeof name !==
|
|
199
|
+
if (typeof name !== "string" || !name.trim()) continue;
|
|
184
200
|
if (!serverMap.has(name.trim())) {
|
|
185
|
-
serverMap.set(
|
|
201
|
+
serverMap.set(
|
|
202
|
+
name.trim(),
|
|
203
|
+
createServerRecord(name.trim(), "configured", config || {}),
|
|
204
|
+
);
|
|
186
205
|
}
|
|
187
206
|
}
|
|
188
207
|
}
|
|
@@ -191,7 +210,7 @@ export function getClaudeMcp(cwd = process.cwd()) {
|
|
|
191
210
|
for (const name of WELL_KNOWN_CLOUD_SERVERS) {
|
|
192
211
|
if (serverMap.has(name)) continue;
|
|
193
212
|
if (!MCP_SERVER_TOOL_CATALOG[name]) continue;
|
|
194
|
-
serverMap.set(name, createServerRecord(name,
|
|
213
|
+
serverMap.set(name, createServerRecord(name, "available"));
|
|
195
214
|
}
|
|
196
215
|
|
|
197
216
|
return [...serverMap.values()];
|
|
@@ -3,37 +3,63 @@
|
|
|
3
3
|
// Usage: node mcp-gateway-config.mjs --enable # stdio → SSE
|
|
4
4
|
// node mcp-gateway-config.mjs --disable # SSE → stdio (복원)
|
|
5
5
|
|
|
6
|
-
import { execSync } from
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync
|
|
8
|
-
import { homedir } from
|
|
9
|
-
import {
|
|
10
|
-
import { isServerEnabled } from
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { isServerEnabled } from "./lib/mcp-manifest.mjs";
|
|
11
11
|
|
|
12
|
-
const BACKUP_FILE = join(homedir(),
|
|
12
|
+
const BACKUP_FILE = join(homedir(), ".claude", "cache", "mcp-pre-gateway.json");
|
|
13
13
|
|
|
14
14
|
export const GATEWAY_SERVERS = [
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
{
|
|
16
|
+
name: "context7",
|
|
17
|
+
port: 8100,
|
|
18
|
+
stdioCmd: "cmd /c npx -y @upstash/context7-mcp@latest",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "brave-search",
|
|
22
|
+
port: 8101,
|
|
23
|
+
stdioCmd: "cmd /c npx -y @brave/brave-search-mcp-server",
|
|
24
|
+
},
|
|
25
|
+
{ name: "exa", port: 8102, stdioCmd: "cmd /c npx -y exa-mcp-server" },
|
|
26
|
+
{ name: "tavily", port: 8103, stdioCmd: "cmd /c npx -y tavily-mcp@latest" },
|
|
27
|
+
{ name: "jira", port: 8104, stdioCmd: "cmd /c npx -y mcp-jira-cloud@latest" },
|
|
28
|
+
{
|
|
29
|
+
name: "serena",
|
|
30
|
+
port: 8105,
|
|
31
|
+
stdioCmd:
|
|
32
|
+
"uvx --from git+https://github.com/oraios/serena serena start-mcp-server",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "notion",
|
|
36
|
+
port: 8106,
|
|
37
|
+
stdioCmd: "cmd /c npx -y @notionhq/notion-mcp-server",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "notion-guest",
|
|
41
|
+
port: 8107,
|
|
42
|
+
stdioCmd: "cmd /c npx -y @notionhq/notion-mcp-server",
|
|
43
|
+
},
|
|
23
44
|
];
|
|
24
45
|
|
|
25
46
|
const SKIP_SERVERS = new Set([
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
"playwright",
|
|
48
|
+
"claude.ai Notion",
|
|
49
|
+
"plugin:oh-my-claudecode:t",
|
|
29
50
|
]);
|
|
30
51
|
|
|
31
52
|
// Git Bash에서 /c → C:/ 경로 변환 방지
|
|
32
|
-
const EXEC_ENV = { ...process.env, MSYS_NO_PATHCONV:
|
|
53
|
+
const EXEC_ENV = { ...process.env, MSYS_NO_PATHCONV: "1" };
|
|
33
54
|
|
|
34
55
|
function run(cmd) {
|
|
35
56
|
try {
|
|
36
|
-
execSync(cmd, {
|
|
57
|
+
execSync(cmd, {
|
|
58
|
+
stdio: "pipe",
|
|
59
|
+
encoding: "utf8",
|
|
60
|
+
timeout: 15000,
|
|
61
|
+
env: EXEC_ENV,
|
|
62
|
+
});
|
|
37
63
|
return true;
|
|
38
64
|
} catch {
|
|
39
65
|
return false;
|
|
@@ -54,16 +80,22 @@ function captureCurrentMcpState() {
|
|
|
54
80
|
// claude mcp get으로 현재 등록 상태 확인
|
|
55
81
|
try {
|
|
56
82
|
const out = execSync(`claude mcp get "${name}" -s user`, {
|
|
57
|
-
stdio:
|
|
83
|
+
stdio: "pipe",
|
|
84
|
+
encoding: "utf8",
|
|
85
|
+
timeout: 5000,
|
|
86
|
+
env: EXEC_ENV,
|
|
58
87
|
}).trim();
|
|
59
|
-
servers[name] = { scope:
|
|
88
|
+
servers[name] = { scope: "user", raw: out };
|
|
60
89
|
} catch {
|
|
61
90
|
// user에 없으면 local 확인
|
|
62
91
|
try {
|
|
63
92
|
const out = execSync(`claude mcp get "${name}" -s local`, {
|
|
64
|
-
stdio:
|
|
93
|
+
stdio: "pipe",
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
timeout: 5000,
|
|
96
|
+
env: EXEC_ENV,
|
|
65
97
|
}).trim();
|
|
66
|
-
servers[name] = { scope:
|
|
98
|
+
servers[name] = { scope: "local", raw: out };
|
|
67
99
|
} catch {
|
|
68
100
|
servers[name] = null; // 기존에 없었음
|
|
69
101
|
}
|
|
@@ -83,14 +115,16 @@ function saveBackup(servers) {
|
|
|
83
115
|
function loadBackup() {
|
|
84
116
|
if (!existsSync(BACKUP_FILE)) return null;
|
|
85
117
|
try {
|
|
86
|
-
return JSON.parse(readFileSync(BACKUP_FILE,
|
|
87
|
-
} catch {
|
|
118
|
+
return JSON.parse(readFileSync(BACKUP_FILE, "utf8"));
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
88
122
|
}
|
|
89
123
|
|
|
90
124
|
// ── enable: 스냅샷 → remove → SSE add (실패 시 rollback) ──
|
|
91
125
|
|
|
92
126
|
function enableSse() {
|
|
93
|
-
console.log(
|
|
127
|
+
console.log("Switching MCP servers to SSE mode...\n");
|
|
94
128
|
|
|
95
129
|
// 1) 현재 상태 스냅샷
|
|
96
130
|
const snapshot = captureCurrentMcpState();
|
|
@@ -108,7 +142,9 @@ function enableSse() {
|
|
|
108
142
|
|
|
109
143
|
removeMcp(name);
|
|
110
144
|
const url = `http://localhost:${port}/sse`;
|
|
111
|
-
const success = run(
|
|
145
|
+
const success = run(
|
|
146
|
+
`claude mcp add --transport sse -s user "${name}" ${url}`,
|
|
147
|
+
);
|
|
112
148
|
|
|
113
149
|
if (success) {
|
|
114
150
|
console.log(` [SSE] ${name} → ${url}`);
|
|
@@ -119,9 +155,11 @@ function enableSse() {
|
|
|
119
155
|
const orig = snapshot[name];
|
|
120
156
|
if (orig) {
|
|
121
157
|
// H1 fix: orig.raw를 shell-escape하여 injection 방지
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
158
|
+
const safeRaw = (orig.raw || "").replace(/[;`$(){}|&<>]/g, "");
|
|
159
|
+
const restored = safeRaw
|
|
160
|
+
? run(`claude mcp add "${name}" -s ${orig.scope} -- ${safeRaw}`)
|
|
161
|
+
: false;
|
|
162
|
+
console.error(` [ROLLBACK] ${name}: ${restored ? "ok" : "FAIL"}`);
|
|
125
163
|
}
|
|
126
164
|
fail++;
|
|
127
165
|
}
|
|
@@ -133,12 +171,14 @@ function enableSse() {
|
|
|
133
171
|
// ── disable: 백업에서 서버별 원복 ──
|
|
134
172
|
|
|
135
173
|
function disableSse() {
|
|
136
|
-
console.log(
|
|
174
|
+
console.log("Restoring MCP servers from backup...\n");
|
|
137
175
|
const backup = loadBackup();
|
|
138
176
|
|
|
139
177
|
// C1 fix: 백업 없으면 전체 삭제 방지
|
|
140
178
|
if (!backup) {
|
|
141
|
-
console.error(
|
|
179
|
+
console.error(
|
|
180
|
+
"No backup found — cannot restore. Run --enable first to create a backup.",
|
|
181
|
+
);
|
|
142
182
|
process.exit(1);
|
|
143
183
|
}
|
|
144
184
|
|
|
@@ -160,9 +200,11 @@ function disableSse() {
|
|
|
160
200
|
|
|
161
201
|
// H2 fix: 백업의 scope/raw를 사용하여 원본 복원, fallback으로 stdioCmd
|
|
162
202
|
removeMcp(name);
|
|
163
|
-
const restoreScope = orig.scope ||
|
|
203
|
+
const restoreScope = orig.scope || "user";
|
|
164
204
|
const restoreCmd = orig.raw?.trim() ? orig.raw.trim() : stdioCmd;
|
|
165
|
-
const success = run(
|
|
205
|
+
const success = run(
|
|
206
|
+
`claude mcp add "${name}" -s ${restoreScope} -- ${restoreCmd}`,
|
|
207
|
+
);
|
|
166
208
|
|
|
167
209
|
if (success) {
|
|
168
210
|
console.log(` [RESTORE] ${name} → scope=${restoreScope}`);
|
|
@@ -171,7 +213,9 @@ function disableSse() {
|
|
|
171
213
|
// fallback: stdioCmd로 재시도
|
|
172
214
|
const fallback = run(`claude mcp add "${name}" -s user -- ${stdioCmd}`);
|
|
173
215
|
if (fallback) {
|
|
174
|
-
console.log(
|
|
216
|
+
console.log(
|
|
217
|
+
` [FALLBACK] ${name} → stdio (원본 복원 실패, 기본값 사용)`,
|
|
218
|
+
);
|
|
175
219
|
ok++;
|
|
176
220
|
} else {
|
|
177
221
|
console.error(` [FAIL] ${name}`);
|
|
@@ -190,16 +234,16 @@ function printUsage() {
|
|
|
190
234
|
--enable Switch Claude Code MCP servers from stdio to SSE (supergateway)
|
|
191
235
|
--disable Restore Claude Code MCP servers to original stdio mode
|
|
192
236
|
|
|
193
|
-
Servers managed: ${GATEWAY_SERVERS.map((s) => s.name).join(
|
|
194
|
-
Servers skipped: ${[...SKIP_SERVERS].join(
|
|
237
|
+
Servers managed: ${GATEWAY_SERVERS.map((s) => s.name).join(", ")}
|
|
238
|
+
Servers skipped: ${[...SKIP_SERVERS].join(", ")}`);
|
|
195
239
|
}
|
|
196
240
|
|
|
197
241
|
// ── main ──
|
|
198
242
|
const flag = process.argv[2];
|
|
199
243
|
|
|
200
|
-
if (flag ===
|
|
244
|
+
if (flag === "--enable") {
|
|
201
245
|
enableSse();
|
|
202
|
-
} else if (flag ===
|
|
246
|
+
} else if (flag === "--disable") {
|
|
203
247
|
disableSse();
|
|
204
248
|
} else {
|
|
205
249
|
printUsage();
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
// mcp-gateway-ensure.mjs — SessionStart 훅에서 supergateway MCP 서비스 보장
|
|
3
4
|
// hub-ensure.mjs 패턴을 따름. 가볍게 헬스체크만 수행하고 필요시 기동.
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { fileURLToPath } from
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
10
11
|
|
|
11
12
|
const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
12
|
-
const PID_FILE = join(tmpdir(),
|
|
13
|
+
const PID_FILE = join(tmpdir(), "tfx-gateway-pids.json");
|
|
13
14
|
const PROBE_PORT = 8100; // context7 — 첫 번째 게이트웨이 포트로 alive 프로브
|
|
14
15
|
const PROBE_TIMEOUT_MS = 1500;
|
|
15
16
|
const STARTUP_WAIT_MS = 4000;
|
|
16
17
|
const POLL_INTERVAL_MS = 500;
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
* 단일 포트 /healthz 프로브로 게이트웨이 클러스터 alive 판정.
|
|
20
|
-
* 모든 포트를 체크하면 hook timeout(8s)에 걸리므로 대표 포트 1개만 확인.
|
|
21
|
-
*/
|
|
22
19
|
async function isGatewayAlive() {
|
|
23
20
|
try {
|
|
24
21
|
const res = await fetch(`http://127.0.0.1:${PROBE_PORT}/healthz`, {
|
|
@@ -30,56 +27,67 @@ async function isGatewayAlive() {
|
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
/** 매니페스트 파일 존재 여부로 gateway 설치 판정 (빠른 경로) */
|
|
34
30
|
function hasManifest() {
|
|
35
31
|
return existsSync(PID_FILE);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
/** mcp-gateway-start.mjs를 독립 프로세스로 기동 */
|
|
39
34
|
function startGateway() {
|
|
40
|
-
const scriptPath = join(PLUGIN_ROOT,
|
|
35
|
+
const scriptPath = join(PLUGIN_ROOT, "scripts", "mcp-gateway-start.mjs");
|
|
41
36
|
if (!existsSync(scriptPath)) return false;
|
|
42
37
|
|
|
43
38
|
try {
|
|
44
|
-
// PowerShell Start-Process: Windows Job Object에서 벗어나 부모 종료 후 생존
|
|
45
39
|
execSync(
|
|
46
|
-
`powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"
|
|
47
|
-
|
|
40
|
+
`powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath '${process.execPath}' -ArgumentList '${scriptPath.replaceAll("'", "''")}'"`,
|
|
41
|
+
{ stdio: "ignore", timeout: 10000 },
|
|
42
|
+
);
|
|
48
43
|
return true;
|
|
49
44
|
} catch {
|
|
50
45
|
return false;
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
48
|
|
|
54
|
-
/** 게이트웨이 기동 후 프로브 포트 ready 대기 */
|
|
55
49
|
async function waitForGatewayReady(maxWaitMs = STARTUP_WAIT_MS) {
|
|
56
50
|
const deadline = Date.now() + maxWaitMs;
|
|
57
51
|
while (Date.now() < deadline) {
|
|
58
52
|
if (await isGatewayAlive()) return true;
|
|
59
|
-
await new Promise((
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
60
54
|
}
|
|
61
55
|
return false;
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
export async function run(stdinData) {
|
|
59
|
+
void stdinData;
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
process.exit(0);
|
|
70
|
-
}
|
|
61
|
+
if (hasManifest() && (await isGatewayAlive())) {
|
|
62
|
+
return { code: 0, stdout: "gateway: ok", stderr: "" };
|
|
63
|
+
}
|
|
71
64
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
if (!hasManifest()) {
|
|
66
|
+
return { code: 0, stdout: "gateway: not configured", stderr: "" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const started = startGateway();
|
|
70
|
+
if (!started) {
|
|
71
|
+
return { code: 0, stdout: "", stderr: "[gateway-ensure] start failed" };
|
|
72
|
+
}
|
|
77
73
|
|
|
78
|
-
// 느린 경로: 게이트웨이 기동 시도
|
|
79
|
-
const started = startGateway();
|
|
80
|
-
if (started) {
|
|
81
74
|
const ready = await waitForGatewayReady();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
return {
|
|
76
|
+
code: 0,
|
|
77
|
+
stdout: ready ? "gateway: ok" : "gateway: starting",
|
|
78
|
+
stderr: "",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const isMain =
|
|
83
|
+
process.argv[1] &&
|
|
84
|
+
import.meta.url.endsWith(
|
|
85
|
+
process.argv[1].replace(/\\/g, "/").split("/").pop(),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (isMain) {
|
|
89
|
+
const result = await run();
|
|
90
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
91
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
92
|
+
process.exit(result.code);
|
|
85
93
|
}
|