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
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// hub/workers/codex-mcp.mjs — Codex MCP 서버 래퍼
|
|
2
|
-
import process from
|
|
3
|
-
import { fileURLToPath } from
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
5
|
-
import { Client } from
|
|
6
|
-
import { StdioClientTransport } from
|
|
5
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
CODEX_MCP_EXECUTION_EXIT_CODE,
|
|
10
10
|
CODEX_MCP_TRANSPORT_EXIT_CODE,
|
|
11
|
-
} from
|
|
12
|
-
import { withRetry } from
|
|
11
|
+
} from "../cli-adapter-base.mjs";
|
|
12
|
+
import { withRetry } from "./worker-utils.mjs";
|
|
13
13
|
|
|
14
|
-
const REQUIRED_TOOLS = [
|
|
14
|
+
const REQUIRED_TOOLS = ["codex", "codex-reply"];
|
|
15
15
|
|
|
16
16
|
export { CODEX_MCP_EXECUTION_EXIT_CODE, CODEX_MCP_TRANSPORT_EXIT_CODE };
|
|
17
17
|
export const DEFAULT_CODEX_MCP_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -29,36 +29,38 @@ export class CodexMcpTransportError extends Error {
|
|
|
29
29
|
*/
|
|
30
30
|
constructor(message, options = {}) {
|
|
31
31
|
super(message, { cause: options.cause });
|
|
32
|
-
this.name =
|
|
33
|
-
this.stderr = options.stderr ||
|
|
32
|
+
this.name = "CodexMcpTransportError";
|
|
33
|
+
this.stderr = options.stderr || "";
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function cloneEnv(env = process.env) {
|
|
38
38
|
return Object.fromEntries(
|
|
39
|
-
Object.entries(env).filter(([, value]) => typeof value ===
|
|
39
|
+
Object.entries(env).filter(([, value]) => typeof value === "string"),
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function collectTextContent(content = []) {
|
|
44
44
|
return content
|
|
45
|
-
.filter((item) => item?.type ===
|
|
45
|
+
.filter((item) => item?.type === "text" && typeof item.text === "string")
|
|
46
46
|
.map((item) => item.text)
|
|
47
|
-
.join(
|
|
47
|
+
.join("\n")
|
|
48
48
|
.trim();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function normalizeStructuredContent(structuredContent, fallbackText =
|
|
52
|
-
if (!structuredContent || typeof structuredContent !==
|
|
51
|
+
function normalizeStructuredContent(structuredContent, fallbackText = "") {
|
|
52
|
+
if (!structuredContent || typeof structuredContent !== "object") {
|
|
53
53
|
return { threadId: null, content: fallbackText };
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const threadId =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
const threadId =
|
|
57
|
+
typeof structuredContent.threadId === "string"
|
|
58
|
+
? structuredContent.threadId
|
|
59
|
+
: null;
|
|
60
|
+
const content =
|
|
61
|
+
typeof structuredContent.content === "string"
|
|
62
|
+
? structuredContent.content
|
|
63
|
+
: fallbackText;
|
|
62
64
|
|
|
63
65
|
return { threadId, content };
|
|
64
66
|
}
|
|
@@ -66,29 +68,34 @@ function normalizeStructuredContent(structuredContent, fallbackText = '') {
|
|
|
66
68
|
function buildCodexArguments(prompt, opts = {}) {
|
|
67
69
|
const args = { prompt };
|
|
68
70
|
|
|
69
|
-
if (typeof opts.cwd ===
|
|
70
|
-
if (typeof opts.model ===
|
|
71
|
-
if (typeof opts.profile ===
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
if (typeof opts.cwd === "string" && opts.cwd) args.cwd = opts.cwd;
|
|
72
|
+
if (typeof opts.model === "string" && opts.model) args.model = opts.model;
|
|
73
|
+
if (typeof opts.profile === "string" && opts.profile)
|
|
74
|
+
args.profile = opts.profile;
|
|
75
|
+
if (typeof opts.approvalPolicy === "string" && opts.approvalPolicy) {
|
|
76
|
+
args["approval-policy"] = opts.approvalPolicy;
|
|
74
77
|
}
|
|
75
|
-
if (typeof opts.sandbox ===
|
|
76
|
-
|
|
77
|
-
if (typeof opts.
|
|
78
|
-
|
|
78
|
+
if (typeof opts.sandbox === "string" && opts.sandbox)
|
|
79
|
+
args.sandbox = opts.sandbox;
|
|
80
|
+
if (opts.config && typeof opts.config === "object") args.config = opts.config;
|
|
81
|
+
if (typeof opts.baseInstructions === "string" && opts.baseInstructions) {
|
|
82
|
+
args["base-instructions"] = opts.baseInstructions;
|
|
79
83
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
84
|
+
if (
|
|
85
|
+
typeof opts.developerInstructions === "string" &&
|
|
86
|
+
opts.developerInstructions
|
|
87
|
+
) {
|
|
88
|
+
args["developer-instructions"] = opts.developerInstructions;
|
|
82
89
|
}
|
|
83
|
-
if (typeof opts.compactPrompt ===
|
|
84
|
-
args[
|
|
90
|
+
if (typeof opts.compactPrompt === "string" && opts.compactPrompt) {
|
|
91
|
+
args["compact-prompt"] = opts.compactPrompt;
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
return args;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
function pickToolName(threadId) {
|
|
91
|
-
return threadId ?
|
|
98
|
+
return threadId ? "codex-reply" : "codex";
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
function withTimeout(promise, timeoutMs, message) {
|
|
@@ -110,27 +117,38 @@ function withTimeout(promise, timeoutMs, message) {
|
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
function normalizeRetryOptions(retryOptions) {
|
|
113
|
-
if (!retryOptions || typeof retryOptions !==
|
|
120
|
+
if (!retryOptions || typeof retryOptions !== "object") {
|
|
114
121
|
return Object.freeze({});
|
|
115
122
|
}
|
|
116
123
|
return Object.freeze({ ...retryOptions });
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
function isCodexRetryable(error) {
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
return (
|
|
128
|
+
error instanceof CodexMcpTransportError ||
|
|
129
|
+
error?.code === "ETIMEDOUT" ||
|
|
130
|
+
error?.cause?.code === "ETIMEDOUT"
|
|
131
|
+
);
|
|
123
132
|
}
|
|
124
133
|
|
|
125
|
-
function detectWorkerCategory(error, fallbackCategory =
|
|
126
|
-
const combined =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
134
|
+
function detectWorkerCategory(error, fallbackCategory = "transient") {
|
|
135
|
+
const combined =
|
|
136
|
+
`${error?.message || ""}\n${error?.stderr || ""}`.toLowerCase();
|
|
137
|
+
|
|
138
|
+
if (error?.code === "INVALID_INPUT") return "input";
|
|
139
|
+
if (
|
|
140
|
+
/(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/i.test(
|
|
141
|
+
combined,
|
|
142
|
+
)
|
|
143
|
+
) {
|
|
144
|
+
return "auth";
|
|
131
145
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
146
|
+
if (
|
|
147
|
+
/(config|unknown option|invalid option|missing|필수 mcp 도구 누락)/i.test(
|
|
148
|
+
combined,
|
|
149
|
+
)
|
|
150
|
+
) {
|
|
151
|
+
return "config";
|
|
134
152
|
}
|
|
135
153
|
|
|
136
154
|
return fallbackCategory;
|
|
@@ -138,20 +156,25 @@ function detectWorkerCategory(error, fallbackCategory = 'transient') {
|
|
|
138
156
|
|
|
139
157
|
function buildCodexErrorInfo(error, attempts) {
|
|
140
158
|
const retryable = isCodexRetryable(error);
|
|
141
|
-
const code =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
const code =
|
|
160
|
+
error instanceof CodexMcpTransportError
|
|
161
|
+
? "CODEX_TRANSPORT_ERROR"
|
|
162
|
+
: error?.code || "CODEX_EXECUTION_ERROR";
|
|
163
|
+
const category = detectWorkerCategory(
|
|
164
|
+
error,
|
|
165
|
+
retryable ? "transient" : "config",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
let recovery =
|
|
169
|
+
"Review the Codex worker error output and retry after correcting the issue.";
|
|
170
|
+
if (code === "INVALID_INPUT") {
|
|
171
|
+
recovery = "Provide a non-empty prompt before invoking the Codex worker.";
|
|
149
172
|
} else if (retryable) {
|
|
150
|
-
recovery =
|
|
151
|
-
} else if (category ===
|
|
152
|
-
recovery =
|
|
153
|
-
} else if (category ===
|
|
154
|
-
recovery =
|
|
173
|
+
recovery = "Retry after reconnecting the Codex MCP transport.";
|
|
174
|
+
} else if (category === "auth") {
|
|
175
|
+
recovery = "Refresh the Codex authentication state and retry.";
|
|
176
|
+
} else if (category === "config") {
|
|
177
|
+
recovery = "Check the Codex MCP configuration and available tools.";
|
|
155
178
|
}
|
|
156
179
|
|
|
157
180
|
return Object.freeze({
|
|
@@ -167,7 +190,7 @@ function buildCodexErrorInfo(error, attempts) {
|
|
|
167
190
|
* Codex MCP 워커
|
|
168
191
|
*/
|
|
169
192
|
export class CodexMcpWorker {
|
|
170
|
-
type =
|
|
193
|
+
type = "codex";
|
|
171
194
|
|
|
172
195
|
/**
|
|
173
196
|
* @param {object} [options]
|
|
@@ -179,13 +202,17 @@ export class CodexMcpWorker {
|
|
|
179
202
|
* @param {number} [options.bootstrapTimeoutMs]
|
|
180
203
|
*/
|
|
181
204
|
constructor(options = {}) {
|
|
182
|
-
this.command = options.command || process.env.CODEX_BIN ||
|
|
183
|
-
this.args =
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
this.command = options.command || process.env.CODEX_BIN || "codex";
|
|
206
|
+
this.args =
|
|
207
|
+
Array.isArray(options.args) && options.args.length
|
|
208
|
+
? [...options.args]
|
|
209
|
+
: ["mcp-server"];
|
|
186
210
|
this.cwd = options.cwd || process.cwd();
|
|
187
211
|
this.env = cloneEnv({ ...cloneEnv(process.env), ...cloneEnv(options.env) });
|
|
188
|
-
this.clientInfo = options.clientInfo || {
|
|
212
|
+
this.clientInfo = options.clientInfo || {
|
|
213
|
+
name: "triflux-codex-mcp",
|
|
214
|
+
version: "1.0.0",
|
|
215
|
+
};
|
|
189
216
|
this.bootstrapTimeoutMs = Number.isFinite(options.bootstrapTimeoutMs)
|
|
190
217
|
? options.bootstrapTimeoutMs
|
|
191
218
|
: DEFAULT_CODEX_MCP_BOOTSTRAP_TIMEOUT_MS;
|
|
@@ -196,7 +223,7 @@ export class CodexMcpWorker {
|
|
|
196
223
|
this.ready = false;
|
|
197
224
|
this.availableTools = new Set();
|
|
198
225
|
this.threadIds = new Map();
|
|
199
|
-
this.serverStderr =
|
|
226
|
+
this.serverStderr = "";
|
|
200
227
|
}
|
|
201
228
|
|
|
202
229
|
isReady() {
|
|
@@ -227,12 +254,12 @@ export class CodexMcpWorker {
|
|
|
227
254
|
args: this.args,
|
|
228
255
|
cwd: this.cwd,
|
|
229
256
|
env: this.env,
|
|
230
|
-
stderr:
|
|
257
|
+
stderr: "pipe",
|
|
231
258
|
});
|
|
232
259
|
const client = new Client(this.clientInfo, { capabilities: {} });
|
|
233
260
|
|
|
234
|
-
this.serverStderr =
|
|
235
|
-
transport.stderr?.on(
|
|
261
|
+
this.serverStderr = "";
|
|
262
|
+
transport.stderr?.on("data", (chunk) => {
|
|
236
263
|
this.serverStderr += String(chunk);
|
|
237
264
|
if (this.serverStderr.length > 16000) {
|
|
238
265
|
this.serverStderr = this.serverStderr.slice(-16000);
|
|
@@ -240,17 +267,23 @@ export class CodexMcpWorker {
|
|
|
240
267
|
});
|
|
241
268
|
|
|
242
269
|
try {
|
|
243
|
-
await withTimeout(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
270
|
+
await withTimeout(
|
|
271
|
+
(async () => {
|
|
272
|
+
await client.connect(transport);
|
|
273
|
+
const tools = await client.listTools(undefined, {
|
|
274
|
+
timeout: this.bootstrapTimeoutMs,
|
|
275
|
+
});
|
|
276
|
+
this.availableTools = new Set(tools.tools.map((tool) => tool.name));
|
|
277
|
+
|
|
278
|
+
for (const requiredTool of REQUIRED_TOOLS) {
|
|
279
|
+
if (!this.availableTools.has(requiredTool)) {
|
|
280
|
+
throw new Error(`필수 MCP 도구 누락: ${requiredTool}`);
|
|
281
|
+
}
|
|
251
282
|
}
|
|
252
|
-
}
|
|
253
|
-
|
|
283
|
+
})(),
|
|
284
|
+
this.bootstrapTimeoutMs,
|
|
285
|
+
`Codex MCP bootstrap timeout (${this.bootstrapTimeoutMs}ms)`,
|
|
286
|
+
);
|
|
254
287
|
} catch (error) {
|
|
255
288
|
await client.close().catch(() => {});
|
|
256
289
|
transport.stderr?.destroy?.();
|
|
@@ -289,62 +322,78 @@ export class CodexMcpWorker {
|
|
|
289
322
|
* @returns {Promise<import('./interface.mjs').WorkerResult>}
|
|
290
323
|
*/
|
|
291
324
|
async execute(prompt, opts = {}) {
|
|
292
|
-
if (typeof prompt !==
|
|
325
|
+
if (typeof prompt !== "string" || !prompt.trim()) {
|
|
293
326
|
return {
|
|
294
|
-
output:
|
|
327
|
+
output: "prompt는 비어 있을 수 없습니다.",
|
|
295
328
|
exitCode: CODEX_MCP_EXECUTION_EXIT_CODE,
|
|
296
329
|
threadId: null,
|
|
297
330
|
sessionKey: opts.sessionKey || null,
|
|
298
|
-
error: buildCodexErrorInfo(
|
|
331
|
+
error: buildCodexErrorInfo(
|
|
332
|
+
{ code: "INVALID_INPUT", message: "prompt는 비어 있을 수 없습니다." },
|
|
333
|
+
0,
|
|
334
|
+
),
|
|
299
335
|
raw: null,
|
|
300
336
|
};
|
|
301
337
|
}
|
|
302
338
|
|
|
303
|
-
const sessionKey =
|
|
304
|
-
|
|
305
|
-
|
|
339
|
+
const sessionKey =
|
|
340
|
+
typeof opts.sessionKey === "string" && opts.sessionKey
|
|
341
|
+
? opts.sessionKey
|
|
342
|
+
: null;
|
|
306
343
|
|
|
307
344
|
if (opts.resetSession && sessionKey) {
|
|
308
345
|
this.clearThread(sessionKey);
|
|
309
346
|
}
|
|
310
347
|
|
|
311
|
-
const threadId =
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
348
|
+
const threadId =
|
|
349
|
+
typeof opts.threadId === "string" && opts.threadId
|
|
350
|
+
? opts.threadId
|
|
351
|
+
: sessionKey
|
|
352
|
+
? this.getThreadId(sessionKey)
|
|
353
|
+
: null;
|
|
354
|
+
const timeoutMs = Number.isFinite(opts.timeoutMs)
|
|
355
|
+
? opts.timeoutMs
|
|
356
|
+
: DEFAULT_CODEX_MCP_TIMEOUT_MS;
|
|
315
357
|
let attempts = 0;
|
|
316
358
|
let activeThreadId = threadId;
|
|
317
359
|
|
|
318
360
|
try {
|
|
319
|
-
const { rawResult, normalized } = await withRetry(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
361
|
+
const { rawResult, normalized } = await withRetry(
|
|
362
|
+
async () => {
|
|
363
|
+
attempts += 1;
|
|
364
|
+
if (attempts === 1) {
|
|
365
|
+
await this.start();
|
|
366
|
+
} else {
|
|
367
|
+
await this.stop();
|
|
368
|
+
await this.start();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const toolName = pickToolName(activeThreadId);
|
|
372
|
+
const toolArguments =
|
|
373
|
+
toolName === "codex-reply"
|
|
374
|
+
? { prompt, threadId: activeThreadId }
|
|
375
|
+
: buildCodexArguments(prompt, opts);
|
|
376
|
+
|
|
377
|
+
const nextRawResult = await this.client.callTool(
|
|
378
|
+
{ name: toolName, arguments: toolArguments },
|
|
379
|
+
undefined,
|
|
380
|
+
{ timeout: timeoutMs },
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const textContent = collectTextContent(nextRawResult.content);
|
|
384
|
+
const nextNormalized = normalizeStructuredContent(
|
|
385
|
+
nextRawResult.structuredContent,
|
|
386
|
+
textContent,
|
|
387
|
+
);
|
|
388
|
+
activeThreadId = nextNormalized.threadId || activeThreadId;
|
|
389
|
+
|
|
390
|
+
return { rawResult: nextRawResult, normalized: nextNormalized };
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
...this.retryOptions,
|
|
394
|
+
shouldRetry: (error) => isCodexRetryable(error),
|
|
395
|
+
},
|
|
396
|
+
);
|
|
348
397
|
|
|
349
398
|
if (sessionKey && normalized.threadId) {
|
|
350
399
|
this.setThreadId(sessionKey, normalized.threadId);
|
|
@@ -357,7 +406,7 @@ export class CodexMcpWorker {
|
|
|
357
406
|
threadId: normalized.threadId,
|
|
358
407
|
sessionKey,
|
|
359
408
|
error: buildCodexErrorInfo(
|
|
360
|
-
{ code:
|
|
409
|
+
{ code: "CODEX_TOOL_ERROR", message: normalized.content },
|
|
361
410
|
attempts,
|
|
362
411
|
),
|
|
363
412
|
raw: rawResult,
|
|
@@ -391,7 +440,7 @@ export function createCodexMcpWorker(options = {}) {
|
|
|
391
440
|
|
|
392
441
|
function parseCliArgs(argv) {
|
|
393
442
|
const options = {
|
|
394
|
-
command: process.env.CODEX_BIN ||
|
|
443
|
+
command: process.env.CODEX_BIN || "codex",
|
|
395
444
|
cwd: process.cwd(),
|
|
396
445
|
timeoutMs: DEFAULT_CODEX_MCP_TIMEOUT_MS,
|
|
397
446
|
};
|
|
@@ -408,49 +457,49 @@ function parseCliArgs(argv) {
|
|
|
408
457
|
};
|
|
409
458
|
|
|
410
459
|
switch (token) {
|
|
411
|
-
case
|
|
460
|
+
case "--prompt":
|
|
412
461
|
options.prompt = next();
|
|
413
462
|
break;
|
|
414
|
-
case
|
|
463
|
+
case "--thread-id":
|
|
415
464
|
options.threadId = next();
|
|
416
465
|
break;
|
|
417
|
-
case
|
|
466
|
+
case "--session-key":
|
|
418
467
|
options.sessionKey = next();
|
|
419
468
|
break;
|
|
420
|
-
case
|
|
469
|
+
case "--cwd":
|
|
421
470
|
options.cwd = next();
|
|
422
471
|
break;
|
|
423
|
-
case
|
|
472
|
+
case "--profile":
|
|
424
473
|
options.profile = next();
|
|
425
474
|
break;
|
|
426
|
-
case
|
|
475
|
+
case "--model":
|
|
427
476
|
options.model = next();
|
|
428
477
|
break;
|
|
429
|
-
case
|
|
478
|
+
case "--approval-policy":
|
|
430
479
|
options.approvalPolicy = next();
|
|
431
480
|
break;
|
|
432
|
-
case
|
|
481
|
+
case "--sandbox":
|
|
433
482
|
options.sandbox = next();
|
|
434
483
|
break;
|
|
435
|
-
case
|
|
484
|
+
case "--base-instructions":
|
|
436
485
|
options.baseInstructions = next();
|
|
437
486
|
break;
|
|
438
|
-
case
|
|
487
|
+
case "--developer-instructions":
|
|
439
488
|
options.developerInstructions = next();
|
|
440
489
|
break;
|
|
441
|
-
case
|
|
490
|
+
case "--compact-prompt":
|
|
442
491
|
options.compactPrompt = next();
|
|
443
492
|
break;
|
|
444
|
-
case
|
|
493
|
+
case "--timeout-ms":
|
|
445
494
|
options.timeoutMs = Number.parseInt(next(), 10);
|
|
446
495
|
break;
|
|
447
|
-
case
|
|
496
|
+
case "--config-json":
|
|
448
497
|
options.config = JSON.parse(next());
|
|
449
498
|
break;
|
|
450
|
-
case
|
|
499
|
+
case "--codex-command":
|
|
451
500
|
options.command = next();
|
|
452
501
|
break;
|
|
453
|
-
case
|
|
502
|
+
case "--reset-session":
|
|
454
503
|
options.resetSession = true;
|
|
455
504
|
break;
|
|
456
505
|
default:
|
|
@@ -458,8 +507,8 @@ function parseCliArgs(argv) {
|
|
|
458
507
|
}
|
|
459
508
|
}
|
|
460
509
|
|
|
461
|
-
if (typeof options.prompt !==
|
|
462
|
-
throw new Error(
|
|
510
|
+
if (typeof options.prompt !== "string" || !options.prompt) {
|
|
511
|
+
throw new Error("--prompt는 필수입니다.");
|
|
463
512
|
}
|
|
464
513
|
|
|
465
514
|
return options;
|
|
@@ -470,7 +519,9 @@ export async function runCodexMcpCli(argv = process.argv.slice(2)) {
|
|
|
470
519
|
try {
|
|
471
520
|
options = parseCliArgs(argv);
|
|
472
521
|
} catch (error) {
|
|
473
|
-
console.error(
|
|
522
|
+
console.error(
|
|
523
|
+
`[codex-mcp] ${error instanceof Error ? error.message : String(error)}`,
|
|
524
|
+
);
|
|
474
525
|
process.exitCode = 64;
|
|
475
526
|
return;
|
|
476
527
|
}
|
|
@@ -484,8 +535,8 @@ export async function runCodexMcpCli(argv = process.argv.slice(2)) {
|
|
|
484
535
|
const result = await worker.execute(options.prompt, options);
|
|
485
536
|
if (result.output) {
|
|
486
537
|
process.stdout.write(result.output);
|
|
487
|
-
if (!result.output.endsWith(
|
|
488
|
-
process.stdout.write(
|
|
538
|
+
if (!result.output.endsWith("\n")) {
|
|
539
|
+
process.stdout.write("\n");
|
|
489
540
|
}
|
|
490
541
|
}
|
|
491
542
|
process.exitCode = result.exitCode;
|
|
@@ -494,7 +545,7 @@ export async function runCodexMcpCli(argv = process.argv.slice(2)) {
|
|
|
494
545
|
if (error instanceof CodexMcpTransportError && error.stderr) {
|
|
495
546
|
lines.push(error.stderr);
|
|
496
547
|
}
|
|
497
|
-
console.error(`[codex-mcp] ${lines.join(
|
|
548
|
+
console.error(`[codex-mcp] ${lines.join("\n")}`);
|
|
498
549
|
process.exitCode = CODEX_MCP_TRANSPORT_EXIT_CODE;
|
|
499
550
|
} finally {
|
|
500
551
|
await worker.stop();
|