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/hub/cli-adapter-base.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// hub/cli-adapter-base.mjs — codex/gemini 공통 CLI adapter 인터페이스
|
|
2
2
|
// Phase 2: codex-adapter.mjs에서 추출한 재사용 가능 유틸리티
|
|
3
3
|
|
|
4
|
-
import { execSync, spawn } from
|
|
5
|
-
import { existsSync, readFileSync } from
|
|
4
|
+
import { execSync, spawn } from "node:child_process";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { IS_WINDOWS, killProcess } from "./platform.mjs";
|
|
8
8
|
|
|
9
9
|
// ── Codex CLI compatibility ─────────────────────────────────────
|
|
10
10
|
|
|
@@ -18,7 +18,10 @@ let _cachedVersion = null;
|
|
|
18
18
|
export function getCodexVersion() {
|
|
19
19
|
if (_cachedVersion !== null) return _cachedVersion;
|
|
20
20
|
try {
|
|
21
|
-
const out = execSync(
|
|
21
|
+
const out = execSync("codex --version", {
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
timeout: 5000,
|
|
24
|
+
}).trim();
|
|
22
25
|
const match = out.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
23
26
|
_cachedVersion = match ? Number.parseInt(match[2], 10) : 0;
|
|
24
27
|
} catch {
|
|
@@ -43,19 +46,27 @@ export function gte(minMinor) {
|
|
|
43
46
|
*/
|
|
44
47
|
export const FEATURES = {
|
|
45
48
|
/** exec 서브커맨드 사용 가능 여부 (0.110+ 이전부터 존재) */
|
|
46
|
-
get execSubcommand() {
|
|
49
|
+
get execSubcommand() {
|
|
50
|
+
return gte(110);
|
|
51
|
+
},
|
|
47
52
|
/** --output-last-message 플래그 지원 여부 (0.117+) */
|
|
48
|
-
get outputLastMessage() {
|
|
53
|
+
get outputLastMessage() {
|
|
54
|
+
return gte(117);
|
|
55
|
+
},
|
|
49
56
|
/** --color <COLOR> 플래그 지원 여부 (exec와 동시 도입) */
|
|
50
|
-
get colorNever() {
|
|
57
|
+
get colorNever() {
|
|
58
|
+
return gte(110);
|
|
59
|
+
},
|
|
51
60
|
/** 플러그인 시스템 지원 여부 (향후 확장용) */
|
|
52
|
-
get pluginSystem() {
|
|
61
|
+
get pluginSystem() {
|
|
62
|
+
return gte(120);
|
|
63
|
+
},
|
|
53
64
|
};
|
|
54
65
|
|
|
55
66
|
// ── Shell utilities ─────────────────────────────────────────────
|
|
56
67
|
|
|
57
68
|
export function normalizePathForShell(value) {
|
|
58
|
-
return IS_WINDOWS ? String(value).replace(/\\/g,
|
|
69
|
+
return IS_WINDOWS ? String(value).replace(/\\/g, "/") : String(value);
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
export function shellQuote(value) {
|
|
@@ -77,32 +88,38 @@ export const CODEX_MCP_EXECUTION_EXIT_CODE = 1;
|
|
|
77
88
|
* @returns {string} 실행할 셸 커맨드
|
|
78
89
|
*/
|
|
79
90
|
export function buildExecCommand(prompt, resultFile = null, opts = {}) {
|
|
80
|
-
const {
|
|
91
|
+
const {
|
|
92
|
+
profile,
|
|
93
|
+
skipGitRepoCheck = true,
|
|
94
|
+
sandboxBypass = true,
|
|
95
|
+
cwd,
|
|
96
|
+
mcpServers,
|
|
97
|
+
} = opts;
|
|
81
98
|
|
|
82
|
-
const parts = [
|
|
83
|
-
if (profile) parts.push(
|
|
99
|
+
const parts = ["codex"];
|
|
100
|
+
if (profile) parts.push("--profile", profile);
|
|
84
101
|
|
|
85
102
|
if (FEATURES.execSubcommand) {
|
|
86
|
-
parts.push(
|
|
87
|
-
if (sandboxBypass) parts.push(
|
|
88
|
-
if (skipGitRepoCheck) parts.push(
|
|
103
|
+
parts.push("exec");
|
|
104
|
+
if (sandboxBypass) parts.push("--dangerously-bypass-approvals-and-sandbox");
|
|
105
|
+
if (skipGitRepoCheck) parts.push("--skip-git-repo-check");
|
|
89
106
|
if (resultFile && FEATURES.outputLastMessage) {
|
|
90
|
-
parts.push(
|
|
107
|
+
parts.push("--output-last-message", resultFile);
|
|
91
108
|
}
|
|
92
|
-
if (FEATURES.colorNever) parts.push(
|
|
93
|
-
if (cwd) parts.push(
|
|
109
|
+
if (FEATURES.colorNever) parts.push("--color", "never");
|
|
110
|
+
if (cwd) parts.push("--cwd", `'${escapePwshSingleQuoted(cwd)}'`);
|
|
94
111
|
if (Array.isArray(mcpServers)) {
|
|
95
112
|
for (const server of mcpServers) {
|
|
96
|
-
parts.push(
|
|
113
|
+
parts.push("-c", `mcp_servers.${server}.enabled=true`);
|
|
97
114
|
}
|
|
98
115
|
}
|
|
99
116
|
} else {
|
|
100
|
-
parts.push(
|
|
101
|
-
if (skipGitRepoCheck) parts.push(
|
|
117
|
+
parts.push("--dangerously-bypass-approvals-and-sandbox");
|
|
118
|
+
if (skipGitRepoCheck) parts.push("--skip-git-repo-check");
|
|
102
119
|
}
|
|
103
120
|
|
|
104
121
|
parts.push(JSON.stringify(prompt));
|
|
105
|
-
return parts.join(
|
|
122
|
+
return parts.join(" ");
|
|
106
123
|
}
|
|
107
124
|
|
|
108
125
|
// ── Sleep ───────────────────────────────────────────────────────
|
|
@@ -119,24 +136,28 @@ export function sleep(ms) {
|
|
|
119
136
|
export function createResult(ok, extra = {}) {
|
|
120
137
|
return {
|
|
121
138
|
ok,
|
|
122
|
-
output:
|
|
123
|
-
stderr:
|
|
139
|
+
output: "",
|
|
140
|
+
stderr: "",
|
|
124
141
|
exitCode: null,
|
|
125
142
|
duration: 0,
|
|
126
143
|
retried: false,
|
|
127
144
|
fellBack: false,
|
|
128
|
-
failureMode: ok ? null :
|
|
145
|
+
failureMode: ok ? null : "crash",
|
|
129
146
|
...extra,
|
|
130
147
|
};
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
export function appendWarnings(stderr, warnings = []) {
|
|
134
|
-
const text = warnings.map((item) => `[preflight] ${item}`).join(
|
|
135
|
-
return [stderr, text].filter(Boolean).join(
|
|
151
|
+
const text = warnings.map((item) => `[preflight] ${item}`).join("\n");
|
|
152
|
+
return [stderr, text].filter(Boolean).join("\n");
|
|
136
153
|
}
|
|
137
154
|
|
|
138
155
|
// ── Circuit breaker factory ─────────────────────────────────────
|
|
139
156
|
|
|
157
|
+
/**
|
|
158
|
+
* @deprecated Use per-account circuit breakers in account-broker.mjs instead.
|
|
159
|
+
* Kept for backward compatibility with code that hasn't migrated yet.
|
|
160
|
+
*/
|
|
140
161
|
export function createCircuitBreaker(opts = {}) {
|
|
141
162
|
const state = {
|
|
142
163
|
failures: [],
|
|
@@ -147,7 +168,9 @@ export function createCircuitBreaker(opts = {}) {
|
|
|
147
168
|
};
|
|
148
169
|
|
|
149
170
|
function pruneFailures(now = Date.now()) {
|
|
150
|
-
state.failures = state.failures.filter(
|
|
171
|
+
state.failures = state.failures.filter(
|
|
172
|
+
(stamp) => now - stamp < state.windowMs,
|
|
173
|
+
);
|
|
151
174
|
}
|
|
152
175
|
|
|
153
176
|
function reset() {
|
|
@@ -167,8 +190,13 @@ export function createCircuitBreaker(opts = {}) {
|
|
|
167
190
|
|
|
168
191
|
function getState(now = Date.now()) {
|
|
169
192
|
pruneFailures(now);
|
|
170
|
-
const withinWindow =
|
|
171
|
-
|
|
193
|
+
const withinWindow =
|
|
194
|
+
state.openedAt && now - state.openedAt < state.windowMs;
|
|
195
|
+
const current = withinWindow
|
|
196
|
+
? "open"
|
|
197
|
+
: state.openedAt
|
|
198
|
+
? "half-open"
|
|
199
|
+
: "closed";
|
|
172
200
|
return {
|
|
173
201
|
state: current,
|
|
174
202
|
failures: [...state.failures],
|
|
@@ -181,9 +209,10 @@ export function createCircuitBreaker(opts = {}) {
|
|
|
181
209
|
|
|
182
210
|
function canExecute() {
|
|
183
211
|
const circuit = getState();
|
|
184
|
-
if (circuit.state ===
|
|
185
|
-
if (circuit.state ===
|
|
186
|
-
|
|
212
|
+
if (circuit.state === "open") return { allowed: false, halfOpen: false };
|
|
213
|
+
if (circuit.state === "half-open" && state.trialInFlight)
|
|
214
|
+
return { allowed: false, halfOpen: true };
|
|
215
|
+
const halfOpen = circuit.state === "half-open";
|
|
187
216
|
if (halfOpen) state.trialInFlight = true;
|
|
188
217
|
return { allowed: true, halfOpen };
|
|
189
218
|
}
|
|
@@ -195,14 +224,112 @@ export function createCircuitBreaker(opts = {}) {
|
|
|
195
224
|
return { getState, recordFailure, reset, canExecute, clearTrial };
|
|
196
225
|
}
|
|
197
226
|
|
|
227
|
+
// ── Broker-integrated execution ─────────────────────────────────
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Shared execute() logic that uses account-broker for per-account circuit
|
|
231
|
+
* breaking instead of a global breaker.
|
|
232
|
+
*
|
|
233
|
+
* @param {object} params
|
|
234
|
+
* @param {string} params.provider — 'codex' | 'gemini'
|
|
235
|
+
* @param {(prompt: string, workdir: string, preflight: object, attempt: object) => Promise<object>} params.runFn
|
|
236
|
+
* @param {(opts: object) => Promise<object>} params.preflightFn
|
|
237
|
+
* @param {(opts: object, preflight: object) => object[]} params.buildAttemptsFn
|
|
238
|
+
* @param {object} params.opts — caller-supplied execute options
|
|
239
|
+
* @returns {Promise<object>} createResult-shaped result
|
|
240
|
+
*/
|
|
241
|
+
export async function executeWithCircuitBroker({
|
|
242
|
+
provider,
|
|
243
|
+
runFn,
|
|
244
|
+
preflightFn,
|
|
245
|
+
buildAttemptsFn,
|
|
246
|
+
opts = {},
|
|
247
|
+
}) {
|
|
248
|
+
// late-import to avoid circular dependency at module load time
|
|
249
|
+
const brokerMod = await import("./account-broker.mjs");
|
|
250
|
+
const { withRetry } = await import("./workers/worker-utils.mjs");
|
|
251
|
+
|
|
252
|
+
// access broker as live binding property (not destructured) so reloadBroker() propagates
|
|
253
|
+
const lease = brokerMod.broker?.lease({ provider });
|
|
254
|
+
if (!lease) {
|
|
255
|
+
return createResult(false, { fellBack: true, failureMode: "circuit_open" });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const preflight = await preflightFn(opts);
|
|
259
|
+
if (!preflight.ok) {
|
|
260
|
+
brokerMod.broker.release(lease.id, { ok: false });
|
|
261
|
+
return createResult(false, {
|
|
262
|
+
stderr: appendWarnings("", preflight.warnings),
|
|
263
|
+
fellBack: opts.fallbackToClaude !== false,
|
|
264
|
+
failureMode: "crash",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const attempts = buildAttemptsFn(opts, preflight);
|
|
269
|
+
let attemptIndex = 0;
|
|
270
|
+
let lastResult = createResult(false);
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
lastResult = await withRetry(
|
|
274
|
+
async () => {
|
|
275
|
+
const result = await runFn(
|
|
276
|
+
opts.prompt || "",
|
|
277
|
+
opts.workdir || process.cwd(),
|
|
278
|
+
preflight,
|
|
279
|
+
attempts[attemptIndex],
|
|
280
|
+
);
|
|
281
|
+
const current = {
|
|
282
|
+
...result,
|
|
283
|
+
stderr: appendWarnings(result.stderr, preflight.warnings),
|
|
284
|
+
retried: attemptIndex > 0,
|
|
285
|
+
};
|
|
286
|
+
const canRetry = !current.ok && attemptIndex < attempts.length - 1;
|
|
287
|
+
attemptIndex += 1;
|
|
288
|
+
if (!canRetry) return current;
|
|
289
|
+
const error = new Error("retry");
|
|
290
|
+
error.retryable = true;
|
|
291
|
+
error.result = current;
|
|
292
|
+
throw error;
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
maxAttempts: attempts.length,
|
|
296
|
+
baseDelayMs: 250,
|
|
297
|
+
maxDelayMs: 750,
|
|
298
|
+
shouldRetry: (error) => error?.retryable === true,
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
lastResult =
|
|
303
|
+
error?.result ||
|
|
304
|
+
createResult(false, { stderr: String(error?.message || error) });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (lastResult.ok) {
|
|
308
|
+
brokerMod.broker.release(lease.id, { ok: true });
|
|
309
|
+
return lastResult;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
brokerMod.broker.release(lease.id, { ok: false });
|
|
313
|
+
return {
|
|
314
|
+
...lastResult,
|
|
315
|
+
retried: attempts.length > 1,
|
|
316
|
+
fellBack: opts.fallbackToClaude !== false,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
198
320
|
// ── Process termination ─────────────────────────────────────────
|
|
199
321
|
|
|
200
322
|
export async function terminateChild(pid, opts = {}) {
|
|
201
323
|
if (!pid) return;
|
|
202
324
|
const graceMs = opts.graceMs ?? 5000;
|
|
203
|
-
killProcess(pid, { signal:
|
|
325
|
+
killProcess(pid, { signal: "SIGTERM", tree: true, timeout: graceMs });
|
|
204
326
|
await sleep(graceMs);
|
|
205
|
-
killProcess(pid, {
|
|
327
|
+
killProcess(pid, {
|
|
328
|
+
signal: "SIGKILL",
|
|
329
|
+
tree: true,
|
|
330
|
+
force: true,
|
|
331
|
+
timeout: graceMs,
|
|
332
|
+
});
|
|
206
333
|
}
|
|
207
334
|
|
|
208
335
|
// ── Process execution with stall detection ──────────────────────
|
|
@@ -222,13 +349,13 @@ export async function terminateChild(pid, opts = {}) {
|
|
|
222
349
|
*/
|
|
223
350
|
export async function runProcess(command, workdir, timeout, opts = {}) {
|
|
224
351
|
const startedAt = Date.now();
|
|
225
|
-
const inferStallMode = opts.inferStallMode || (() =>
|
|
352
|
+
const inferStallMode = opts.inferStallMode || (() => "timeout");
|
|
226
353
|
const stallCheckIntervalMs = opts.stallCheckIntervalMs ?? 10_000;
|
|
227
354
|
const stallThresholdMs = opts.stallThresholdMs ?? 30_000;
|
|
228
355
|
const resultFile = opts.resultFile || null;
|
|
229
356
|
|
|
230
|
-
let stdout =
|
|
231
|
-
let stderr =
|
|
357
|
+
let stdout = "";
|
|
358
|
+
let stderr = "";
|
|
232
359
|
let exitCode = null;
|
|
233
360
|
let failureMode = null;
|
|
234
361
|
let child;
|
|
@@ -236,15 +363,29 @@ export async function runProcess(command, workdir, timeout, opts = {}) {
|
|
|
236
363
|
try {
|
|
237
364
|
child = spawn(command, { cwd: workdir, shell: true, windowsHide: true });
|
|
238
365
|
} catch (error) {
|
|
239
|
-
return createResult(false, {
|
|
366
|
+
return createResult(false, {
|
|
367
|
+
stderr: String(error?.message || error),
|
|
368
|
+
duration: Date.now() - startedAt,
|
|
369
|
+
});
|
|
240
370
|
}
|
|
241
371
|
|
|
242
372
|
let lastBytes = 0;
|
|
243
373
|
let lastChange = Date.now();
|
|
244
|
-
const touch = () => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
child.on(
|
|
374
|
+
const touch = () => {
|
|
375
|
+
lastChange = Date.now();
|
|
376
|
+
};
|
|
377
|
+
child.stdout?.on("data", (chunk) => {
|
|
378
|
+
stdout += String(chunk);
|
|
379
|
+
touch();
|
|
380
|
+
});
|
|
381
|
+
child.stderr?.on("data", (chunk) => {
|
|
382
|
+
stderr += String(chunk);
|
|
383
|
+
touch();
|
|
384
|
+
});
|
|
385
|
+
child.on("error", (error) => {
|
|
386
|
+
stderr += String(error?.message || error);
|
|
387
|
+
failureMode ||= "crash";
|
|
388
|
+
});
|
|
248
389
|
|
|
249
390
|
const stopFor = async (mode) => {
|
|
250
391
|
if (failureMode) return;
|
|
@@ -252,23 +393,34 @@ export async function runProcess(command, workdir, timeout, opts = {}) {
|
|
|
252
393
|
await terminateChild(child.pid);
|
|
253
394
|
};
|
|
254
395
|
|
|
255
|
-
const timeoutTimer = setTimeout(() => {
|
|
396
|
+
const timeoutTimer = setTimeout(() => {
|
|
397
|
+
void stopFor("timeout");
|
|
398
|
+
}, timeout);
|
|
256
399
|
const stallTimer = setInterval(() => {
|
|
257
400
|
const size = Buffer.byteLength(stdout) + Buffer.byteLength(stderr);
|
|
258
401
|
if (size !== lastBytes) {
|
|
259
402
|
lastBytes = size;
|
|
260
403
|
return;
|
|
261
404
|
}
|
|
262
|
-
if (Date.now() - lastChange >= stallThresholdMs)
|
|
405
|
+
if (Date.now() - lastChange >= stallThresholdMs)
|
|
406
|
+
void stopFor(inferStallMode(stdout, stderr));
|
|
263
407
|
}, stallCheckIntervalMs);
|
|
264
408
|
timeoutTimer.unref?.();
|
|
265
409
|
stallTimer.unref?.();
|
|
266
410
|
|
|
267
|
-
await new Promise((resolve) =>
|
|
411
|
+
await new Promise((resolve) =>
|
|
412
|
+
child.on("close", (code) => {
|
|
413
|
+
exitCode = code;
|
|
414
|
+
resolve();
|
|
415
|
+
}),
|
|
416
|
+
);
|
|
268
417
|
clearTimeout(timeoutTimer);
|
|
269
418
|
clearInterval(stallTimer);
|
|
270
419
|
|
|
271
|
-
const fileOutput =
|
|
420
|
+
const fileOutput =
|
|
421
|
+
resultFile && existsSync(resultFile)
|
|
422
|
+
? readFileSync(resultFile, "utf8")
|
|
423
|
+
: "";
|
|
272
424
|
const output = fileOutput || stdout;
|
|
273
425
|
const ok = failureMode == null && exitCode === 0;
|
|
274
426
|
return createResult(ok, {
|
|
@@ -276,6 +428,6 @@ export async function runProcess(command, workdir, timeout, opts = {}) {
|
|
|
276
428
|
stderr,
|
|
277
429
|
exitCode,
|
|
278
430
|
duration: Date.now() - startedAt,
|
|
279
|
-
failureMode: ok ? null :
|
|
431
|
+
failureMode: ok ? null : failureMode || "crash",
|
|
280
432
|
});
|
|
281
433
|
}
|
package/hub/codex-adapter.mjs
CHANGED
|
@@ -1,46 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { runPreflight } from './codex-preflight.mjs';
|
|
6
|
-
import { withRetry } from './workers/worker-utils.mjs';
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
7
4
|
import {
|
|
8
|
-
createCircuitBreaker,
|
|
9
|
-
createResult,
|
|
10
|
-
appendWarnings,
|
|
11
5
|
buildExecCommand,
|
|
6
|
+
executeWithCircuitBroker,
|
|
12
7
|
normalizePathForShell,
|
|
13
|
-
shellQuote,
|
|
14
8
|
runProcess,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
shellQuote,
|
|
10
|
+
} from "./cli-adapter-base.mjs";
|
|
11
|
+
import { runPreflight } from "./codex-preflight.mjs";
|
|
18
12
|
|
|
19
13
|
// ── Codex-specific stall inference ──────────────────────────────
|
|
20
14
|
|
|
21
15
|
function inferStallMode(stdout, stderr) {
|
|
22
16
|
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
if (
|
|
18
|
+
/(rate.?limit|quota|throttl|too.many.requests|429|usage.limit)/u.test(text)
|
|
19
|
+
)
|
|
20
|
+
return "rate_limited";
|
|
21
|
+
if (/(approval|approve|permission|sandbox|bypass)/u.test(text))
|
|
22
|
+
return "approval_stall";
|
|
23
|
+
if (
|
|
24
|
+
/\bmcp\b|context7|playwright|tavily|exa|brave|sequential|server/u.test(text)
|
|
25
|
+
)
|
|
26
|
+
return "mcp_stall";
|
|
27
|
+
return "timeout";
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// ── Codex command building ──────────────────────────────────────
|
|
30
31
|
|
|
31
32
|
function commandWithOverrides(command, prompt, codexPath, overrides = []) {
|
|
32
|
-
const next = codexPath
|
|
33
|
+
const next = codexPath
|
|
34
|
+
? command.replace(/^codex\b/u, shellQuote(codexPath))
|
|
35
|
+
: command;
|
|
33
36
|
if (!overrides.length) return next;
|
|
34
37
|
const promptArg = JSON.stringify(prompt);
|
|
35
|
-
const flags = overrides
|
|
38
|
+
const flags = overrides
|
|
39
|
+
.flatMap((value) => ["-c", shellQuote(value)])
|
|
40
|
+
.join(" ");
|
|
36
41
|
return next.endsWith(promptArg)
|
|
37
42
|
? `${next.slice(0, -promptArg.length)}${flags} ${promptArg}`
|
|
38
43
|
: `${next} ${flags}`;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
function buildOverrides(requested, excluded) {
|
|
42
|
-
return [
|
|
43
|
-
|
|
47
|
+
return [
|
|
48
|
+
...new Set(
|
|
49
|
+
(requested || []).filter((name) => (excluded || []).includes(name)),
|
|
50
|
+
),
|
|
51
|
+
].map((name) => `mcp_servers.${name}.enabled=false`);
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
function buildAttempts(opts, preflight) {
|
|
@@ -63,37 +71,37 @@ function buildAttempts(opts, preflight) {
|
|
|
63
71
|
// ── Launch script ───────────────────────────────────────────────
|
|
64
72
|
|
|
65
73
|
function createLaunchScriptText(opts) {
|
|
66
|
-
const parts = [
|
|
67
|
-
if (opts.profile) parts.push(
|
|
74
|
+
const parts = ["codex"];
|
|
75
|
+
if (opts.profile) parts.push("--profile", shellQuote(opts.profile));
|
|
68
76
|
parts.push(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
"exec",
|
|
78
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
79
|
+
"--skip-git-repo-check",
|
|
72
80
|
'$(cat "$PROMPT_FILE")',
|
|
73
81
|
);
|
|
74
82
|
return [
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
"#!/usr/bin/env bash",
|
|
84
|
+
"set -euo pipefail",
|
|
77
85
|
`cd ${shellQuote(normalizePathForShell(opts.workdir))}`,
|
|
78
86
|
`PROMPT_FILE=${shellQuote(normalizePathForShell(opts.promptFile))}`,
|
|
79
|
-
`TFX_CODEX_TIMEOUT_MS=${shellQuote(String(opts.timeout ??
|
|
80
|
-
parts.join(
|
|
81
|
-
|
|
82
|
-
].join(
|
|
87
|
+
`TFX_CODEX_TIMEOUT_MS=${shellQuote(String(opts.timeout ?? ""))}`,
|
|
88
|
+
parts.join(" "),
|
|
89
|
+
"",
|
|
90
|
+
].join("\n");
|
|
83
91
|
}
|
|
84
92
|
|
|
85
93
|
export function buildLaunchScript(opts = {}) {
|
|
86
|
-
const dir = join(tmpdir(),
|
|
94
|
+
const dir = join(tmpdir(), "triflux-codex-launch");
|
|
87
95
|
mkdirSync(dir, { recursive: true });
|
|
88
|
-
const path = join(dir, `${String(opts.id ||
|
|
89
|
-
writeFileSync(path, createLaunchScriptText(opts),
|
|
96
|
+
const path = join(dir, `${String(opts.id || "launch")}.sh`);
|
|
97
|
+
writeFileSync(path, createLaunchScriptText(opts), "utf8");
|
|
90
98
|
return path;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
// ── Exec args builder ───────────────────────────────────────────
|
|
94
102
|
|
|
95
103
|
export function buildExecArgs(opts = {}) {
|
|
96
|
-
const prompt = typeof opts.prompt ===
|
|
104
|
+
const prompt = typeof opts.prompt === "string" ? opts.prompt : "";
|
|
97
105
|
const command = buildExecCommand(prompt, opts.resultFile || null, {
|
|
98
106
|
profile: opts.profile,
|
|
99
107
|
skipGitRepoCheck: true,
|
|
@@ -101,11 +109,14 @@ export function buildExecArgs(opts = {}) {
|
|
|
101
109
|
cwd: opts.cwd,
|
|
102
110
|
});
|
|
103
111
|
|
|
104
|
-
if (!prompt) return command.replace(/\s+""$/u,
|
|
112
|
+
if (!prompt) return command.replace(/\s+""$/u, "");
|
|
105
113
|
|
|
106
114
|
let result;
|
|
107
115
|
const quotedPrompt = JSON.stringify(prompt);
|
|
108
|
-
if (
|
|
116
|
+
if (
|
|
117
|
+
/^\(Get-Content\b[\s\S]*\)$/u.test(prompt) &&
|
|
118
|
+
command.endsWith(quotedPrompt)
|
|
119
|
+
) {
|
|
109
120
|
result = `${command.slice(0, -quotedPrompt.length)}${prompt}`;
|
|
110
121
|
} else {
|
|
111
122
|
result = command;
|
|
@@ -121,9 +132,12 @@ export function buildExecArgs(opts = {}) {
|
|
|
121
132
|
// ── Codex execution ─────────────────────────────────────────────
|
|
122
133
|
|
|
123
134
|
async function runCodex(prompt, workdir, preflight, attempt) {
|
|
124
|
-
const dir = join(tmpdir(),
|
|
135
|
+
const dir = join(tmpdir(), "triflux-codex-exec");
|
|
125
136
|
mkdirSync(dir, { recursive: true });
|
|
126
|
-
const resultFile = join(
|
|
137
|
+
const resultFile = join(
|
|
138
|
+
dir,
|
|
139
|
+
`codex-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`,
|
|
140
|
+
);
|
|
127
141
|
const command = commandWithOverrides(
|
|
128
142
|
buildExecCommand(prompt, resultFile, {
|
|
129
143
|
profile: attempt.profile,
|
|
@@ -134,66 +148,32 @@ async function runCodex(prompt, workdir, preflight, attempt) {
|
|
|
134
148
|
preflight.codexPath,
|
|
135
149
|
buildOverrides(attempt.requested, attempt.excluded),
|
|
136
150
|
);
|
|
137
|
-
return runProcess(command, workdir, attempt.timeout, {
|
|
151
|
+
return runProcess(command, workdir, attempt.timeout, {
|
|
152
|
+
resultFile,
|
|
153
|
+
inferStallMode,
|
|
154
|
+
});
|
|
138
155
|
}
|
|
139
156
|
|
|
140
157
|
// ── Public API ──────────────────────────────────────────────────
|
|
141
158
|
|
|
142
|
-
export function getCircuitState(
|
|
143
|
-
|
|
159
|
+
export async function getCircuitState() {
|
|
160
|
+
const brokerMod = await import("./account-broker.mjs");
|
|
161
|
+
if (!brokerMod.broker) return { state: "closed", failures: [] };
|
|
162
|
+
const snap = brokerMod.broker
|
|
163
|
+
.snapshot()
|
|
164
|
+
.filter((a) => a.provider === "codex");
|
|
165
|
+
return snap.length
|
|
166
|
+
? { state: snap[0].circuitState, accounts: snap }
|
|
167
|
+
: { state: "closed", failures: [] };
|
|
144
168
|
}
|
|
145
169
|
|
|
146
|
-
export
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
breaker.recordFailure(entry.halfOpen);
|
|
156
|
-
return createResult(false, {
|
|
157
|
-
stderr: appendWarnings('', preflight.warnings),
|
|
158
|
-
fellBack: opts.fallbackToClaude !== false,
|
|
159
|
-
failureMode: 'crash',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const attempts = buildAttempts(opts, preflight);
|
|
164
|
-
let attemptIndex = 0;
|
|
165
|
-
let lastResult = createResult(false);
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
lastResult = await withRetry(async () => {
|
|
169
|
-
const result = await runCodex(opts.prompt || '', opts.workdir || process.cwd(), preflight, attempts[attemptIndex]);
|
|
170
|
-
const current = { ...result, stderr: appendWarnings(result.stderr, preflight.warnings), retried: attemptIndex > 0 };
|
|
171
|
-
const canRetry = !current.ok && attemptIndex < attempts.length - 1;
|
|
172
|
-
attemptIndex += 1;
|
|
173
|
-
if (!canRetry) return current;
|
|
174
|
-
const error = new Error('retry');
|
|
175
|
-
error.retryable = true;
|
|
176
|
-
error.result = current;
|
|
177
|
-
throw error;
|
|
178
|
-
}, {
|
|
179
|
-
maxAttempts: attempts.length,
|
|
180
|
-
baseDelayMs: 250,
|
|
181
|
-
maxDelayMs: 750,
|
|
182
|
-
shouldRetry: (error) => error?.retryable === true,
|
|
183
|
-
});
|
|
184
|
-
} catch (error) {
|
|
185
|
-
lastResult = error?.result || createResult(false, { stderr: String(error?.message || error) });
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (lastResult.ok) {
|
|
189
|
-
breaker.reset();
|
|
190
|
-
return lastResult;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
breaker.recordFailure(entry.halfOpen);
|
|
194
|
-
return {
|
|
195
|
-
...lastResult,
|
|
196
|
-
retried: attempts.length > 1,
|
|
197
|
-
fellBack: opts.fallbackToClaude !== false,
|
|
198
|
-
};
|
|
170
|
+
export function execute(opts = {}) {
|
|
171
|
+
return executeWithCircuitBroker({
|
|
172
|
+
provider: "codex",
|
|
173
|
+
runFn: runCodex,
|
|
174
|
+
preflightFn: (o) =>
|
|
175
|
+
runPreflight({ mcpServers: o.mcpServers, subcommand: "exec" }),
|
|
176
|
+
buildAttemptsFn: buildAttempts,
|
|
177
|
+
opts,
|
|
178
|
+
});
|
|
199
179
|
}
|
package/hub/codex-compat.mjs
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
/** @experimental 런타임 미연결 — 향후 통합 예정 */
|
|
4
4
|
export {
|
|
5
|
+
buildExecCommand,
|
|
5
6
|
CODEX_MCP_EXECUTION_EXIT_CODE,
|
|
6
7
|
CODEX_MCP_TRANSPORT_EXIT_CODE,
|
|
7
|
-
FEATURES,
|
|
8
|
-
buildExecCommand,
|
|
9
8
|
escapePwshSingleQuoted,
|
|
9
|
+
FEATURES,
|
|
10
10
|
getCodexVersion,
|
|
11
11
|
gte,
|
|
12
|
-
} from
|
|
12
|
+
} from "./cli-adapter-base.mjs";
|