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/workers/factory.mjs
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// hub/workers/factory.mjs — Worker 생성 팩토리
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { ClaudeWorker } from "./claude-worker.mjs";
|
|
4
|
+
import { CodexMcpWorker } from "./codex-mcp.mjs";
|
|
5
|
+
import { DelegatorMcpWorker } from "./delegator-mcp.mjs";
|
|
6
|
+
import { GeminiWorker } from "./gemini-worker.mjs";
|
|
7
7
|
|
|
8
8
|
export function createWorker(type, opts = {}) {
|
|
9
9
|
switch (type) {
|
|
10
|
-
case
|
|
10
|
+
case "gemini":
|
|
11
11
|
return new GeminiWorker(opts);
|
|
12
|
-
case
|
|
12
|
+
case "claude":
|
|
13
13
|
return new ClaudeWorker(opts);
|
|
14
|
-
case
|
|
14
|
+
case "codex":
|
|
15
15
|
return new CodexMcpWorker(opts);
|
|
16
|
-
case
|
|
16
|
+
case "delegator":
|
|
17
17
|
return new DelegatorMcpWorker(opts);
|
|
18
18
|
default:
|
|
19
19
|
throw new Error(`Unknown worker type: ${type}`);
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
// hub/workers/gemini-worker.mjs — Gemini headless subprocess 래퍼
|
|
2
2
|
// ADR-006: --output-format stream-json 기반 단발 실행 워커.
|
|
3
3
|
|
|
4
|
-
import { spawn } from
|
|
5
|
-
import { isAbsolute } from
|
|
6
|
-
import readline from
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { isAbsolute } from "node:path";
|
|
6
|
+
import readline from "node:readline";
|
|
7
7
|
|
|
8
|
-
import { extractText, terminateChild, withRetry } from
|
|
8
|
+
import { extractText, terminateChild, withRetry } from "./worker-utils.mjs";
|
|
9
9
|
|
|
10
10
|
const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
11
11
|
const DEFAULT_KILL_GRACE_MS = 1000;
|
|
12
12
|
|
|
13
13
|
function toStringList(value) {
|
|
14
14
|
if (!Array.isArray(value)) return [];
|
|
15
|
-
return value
|
|
16
|
-
.map((item) => String(item ?? '').trim())
|
|
17
|
-
.filter(Boolean);
|
|
15
|
+
return value.map((item) => String(item ?? "").trim()).filter(Boolean);
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
function safeJsonParse(line) {
|
|
@@ -36,25 +34,25 @@ function buildGeminiArgs(options) {
|
|
|
36
34
|
const args = [];
|
|
37
35
|
|
|
38
36
|
if (options.model) {
|
|
39
|
-
args.push(
|
|
37
|
+
args.push("--model", options.model);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
if (options.approvalMode) {
|
|
43
|
-
args.push(
|
|
41
|
+
args.push("--approval-mode", options.approvalMode);
|
|
44
42
|
} else if (options.yolo !== false) {
|
|
45
|
-
args.push(
|
|
43
|
+
args.push("--yolo");
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
const allowedMcpServers = toStringList(options.allowedMcpServerNames);
|
|
49
47
|
if (allowedMcpServers.length) {
|
|
50
|
-
args.push(
|
|
48
|
+
args.push("--allowed-mcp-server-names", ...allowedMcpServers);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
const extraArgs = toStringList(options.extraArgs);
|
|
54
52
|
if (extraArgs.length) args.push(...extraArgs);
|
|
55
53
|
|
|
56
|
-
args.push(
|
|
57
|
-
args.push(
|
|
54
|
+
args.push("--prompt", options.promptArgument ?? "");
|
|
55
|
+
args.push("--output-format", "stream-json");
|
|
58
56
|
|
|
59
57
|
return args;
|
|
60
58
|
}
|
|
@@ -66,49 +64,59 @@ function createWorkerError(message, details = {}) {
|
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
function normalizeRetryOptions(retryOptions) {
|
|
69
|
-
if (!retryOptions || typeof retryOptions !==
|
|
67
|
+
if (!retryOptions || typeof retryOptions !== "object") {
|
|
70
68
|
return Object.freeze({});
|
|
71
69
|
}
|
|
72
70
|
return Object.freeze({ ...retryOptions });
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
function isGeminiRetryable(error) {
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
return (
|
|
75
|
+
error?.code === "WORKER_EXIT" &&
|
|
76
|
+
error?.result?.exitCode !== 0 &&
|
|
77
|
+
error?.result?.exitCode !== 2
|
|
78
|
+
);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
function detectGeminiCategory(error) {
|
|
82
|
-
const combined =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
const combined =
|
|
83
|
+
`${error?.message || ""}\n${error?.stderr || ""}`.toLowerCase();
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
/(unauthorized|forbidden|auth|login|token|credential|apikey|api key)/.test(
|
|
87
|
+
combined,
|
|
88
|
+
)
|
|
89
|
+
) {
|
|
90
|
+
return "auth";
|
|
86
91
|
}
|
|
87
|
-
if (
|
|
88
|
-
|
|
92
|
+
if (
|
|
93
|
+
error?.result?.exitCode === 2 ||
|
|
94
|
+
/expected stream-json|unknown option|invalid option|config/.test(combined)
|
|
95
|
+
) {
|
|
96
|
+
return "config";
|
|
89
97
|
}
|
|
90
|
-
if (error?.code ===
|
|
91
|
-
return
|
|
98
|
+
if (error?.code === "WORKER_EVENT_ERROR") {
|
|
99
|
+
return "input";
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
return
|
|
102
|
+
return "transient";
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
function buildGeminiErrorInfo(error, attempts) {
|
|
98
106
|
const category = detectGeminiCategory(error);
|
|
99
107
|
const retryable = isGeminiRetryable(error);
|
|
100
|
-
let recovery =
|
|
101
|
-
|
|
102
|
-
if (category ===
|
|
103
|
-
recovery =
|
|
104
|
-
} else if (category ===
|
|
105
|
-
recovery =
|
|
106
|
-
} else if (category ===
|
|
107
|
-
recovery =
|
|
108
|
+
let recovery = "Retry the Gemini worker after correcting the reported issue.";
|
|
109
|
+
|
|
110
|
+
if (category === "auth") {
|
|
111
|
+
recovery = "Refresh the Gemini authentication state and retry.";
|
|
112
|
+
} else if (category === "config") {
|
|
113
|
+
recovery = "Check the Gemini CLI flags and worker configuration.";
|
|
114
|
+
} else if (category === "input") {
|
|
115
|
+
recovery = "Check the Gemini request payload and streamed event format.";
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
return Object.freeze({
|
|
111
|
-
code: error?.code ||
|
|
119
|
+
code: error?.code || "GEMINI_EXECUTION_ERROR",
|
|
112
120
|
retryable,
|
|
113
121
|
attempts,
|
|
114
122
|
category,
|
|
@@ -120,10 +128,10 @@ function buildGeminiErrorInfo(error, attempts) {
|
|
|
120
128
|
* Gemini stream-json 래퍼
|
|
121
129
|
*/
|
|
122
130
|
export class GeminiWorker {
|
|
123
|
-
type =
|
|
131
|
+
type = "gemini";
|
|
124
132
|
|
|
125
133
|
constructor(options = {}) {
|
|
126
|
-
this.command = options.command ||
|
|
134
|
+
this.command = options.command || "gemini";
|
|
127
135
|
this.commandArgs = toStringList(options.commandArgs || options.args);
|
|
128
136
|
this.cwd = options.cwd || process.cwd();
|
|
129
137
|
this.env = { ...process.env, ...(options.env || {}) };
|
|
@@ -132,19 +140,26 @@ export class GeminiWorker {
|
|
|
132
140
|
this.yolo = options.yolo !== false;
|
|
133
141
|
this.allowedMcpServerNames = toStringList(options.allowedMcpServerNames);
|
|
134
142
|
this.extraArgs = toStringList(options.extraArgs);
|
|
135
|
-
this.timeoutMs =
|
|
136
|
-
|
|
143
|
+
this.timeoutMs =
|
|
144
|
+
Number(options.timeoutMs) > 0
|
|
145
|
+
? Number(options.timeoutMs)
|
|
146
|
+
: DEFAULT_TIMEOUT_MS;
|
|
147
|
+
this.killGraceMs =
|
|
148
|
+
Number(options.killGraceMs) > 0
|
|
149
|
+
? Number(options.killGraceMs)
|
|
150
|
+
: DEFAULT_KILL_GRACE_MS;
|
|
137
151
|
this.retryOptions = normalizeRetryOptions(options.retryOptions);
|
|
138
|
-
this.onEvent =
|
|
152
|
+
this.onEvent =
|
|
153
|
+
typeof options.onEvent === "function" ? options.onEvent : null;
|
|
139
154
|
|
|
140
|
-
this.state =
|
|
155
|
+
this.state = "idle";
|
|
141
156
|
this.child = null;
|
|
142
157
|
this.lastRun = null;
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
getStatus() {
|
|
146
161
|
return {
|
|
147
|
-
type:
|
|
162
|
+
type: "gemini",
|
|
148
163
|
state: this.state,
|
|
149
164
|
pid: this.child?.pid || null,
|
|
150
165
|
last_run_at_ms: this.lastRun?.finishedAtMs || null,
|
|
@@ -153,42 +168,47 @@ export class GeminiWorker {
|
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
async start() {
|
|
156
|
-
if (this.state ===
|
|
157
|
-
this.state =
|
|
171
|
+
if (this.state === "stopped") {
|
|
172
|
+
this.state = "idle";
|
|
158
173
|
}
|
|
159
174
|
return this.getStatus();
|
|
160
175
|
}
|
|
161
176
|
|
|
162
177
|
async stop() {
|
|
163
178
|
if (!this.child) {
|
|
164
|
-
this.state =
|
|
179
|
+
this.state = "stopped";
|
|
165
180
|
return this.getStatus();
|
|
166
181
|
}
|
|
167
182
|
const child = this.child;
|
|
168
183
|
terminateChild(child, this.killGraceMs);
|
|
169
184
|
await new Promise((resolve) => {
|
|
170
|
-
child.once(
|
|
185
|
+
child.once("close", resolve);
|
|
171
186
|
setTimeout(resolve, this.killGraceMs + 50).unref?.();
|
|
172
187
|
});
|
|
173
188
|
this.child = null;
|
|
174
|
-
this.state =
|
|
189
|
+
this.state = "stopped";
|
|
175
190
|
return this.getStatus();
|
|
176
191
|
}
|
|
177
192
|
|
|
178
193
|
async restart() {
|
|
179
194
|
await this.stop();
|
|
180
|
-
this.state =
|
|
195
|
+
this.state = "idle";
|
|
181
196
|
return this.getStatus();
|
|
182
197
|
}
|
|
183
198
|
|
|
184
199
|
async run(prompt, options = {}) {
|
|
185
200
|
if (this.child) {
|
|
186
|
-
throw createWorkerError(
|
|
201
|
+
throw createWorkerError("GeminiWorker is already running", {
|
|
202
|
+
code: "WORKER_BUSY",
|
|
203
|
+
});
|
|
187
204
|
}
|
|
188
205
|
|
|
189
206
|
await this.start();
|
|
190
207
|
|
|
191
|
-
const timeoutMs =
|
|
208
|
+
const timeoutMs =
|
|
209
|
+
Number(options.timeoutMs) > 0
|
|
210
|
+
? Number(options.timeoutMs)
|
|
211
|
+
: this.timeoutMs;
|
|
192
212
|
const startedAtMs = Date.now();
|
|
193
213
|
const args = [
|
|
194
214
|
...this.commandArgs,
|
|
@@ -196,22 +216,23 @@ export class GeminiWorker {
|
|
|
196
216
|
model: options.model || this.model,
|
|
197
217
|
approvalMode: options.approvalMode || this.approvalMode,
|
|
198
218
|
yolo: options.yolo ?? this.yolo,
|
|
199
|
-
allowedMcpServerNames:
|
|
219
|
+
allowedMcpServerNames:
|
|
220
|
+
options.allowedMcpServerNames || this.allowedMcpServerNames,
|
|
200
221
|
extraArgs: options.extraArgs || this.extraArgs,
|
|
201
|
-
promptArgument: options.promptArgument ??
|
|
222
|
+
promptArgument: options.promptArgument ?? "",
|
|
202
223
|
}),
|
|
203
224
|
];
|
|
204
225
|
|
|
205
226
|
const child = spawn(this.command, args, {
|
|
206
227
|
cwd: options.cwd || this.cwd,
|
|
207
228
|
env: { ...this.env, ...(options.env || {}) },
|
|
208
|
-
stdio: [
|
|
229
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
209
230
|
windowsHide: true,
|
|
210
|
-
shell: process.platform ===
|
|
231
|
+
shell: process.platform === "win32" && !isAbsolute(this.command),
|
|
211
232
|
});
|
|
212
233
|
|
|
213
234
|
this.child = child;
|
|
214
|
-
this.state =
|
|
235
|
+
this.state = "running";
|
|
215
236
|
|
|
216
237
|
const events = [];
|
|
217
238
|
const stdoutLines = [];
|
|
@@ -230,7 +251,7 @@ export class GeminiWorker {
|
|
|
230
251
|
crlfDelay: Infinity,
|
|
231
252
|
});
|
|
232
253
|
|
|
233
|
-
stdoutReader.on(
|
|
254
|
+
stdoutReader.on("line", (line) => {
|
|
234
255
|
if (!line) return;
|
|
235
256
|
const event = safeJsonParse(line);
|
|
236
257
|
if (!event) {
|
|
@@ -239,20 +260,22 @@ export class GeminiWorker {
|
|
|
239
260
|
}
|
|
240
261
|
|
|
241
262
|
events.push(event);
|
|
242
|
-
if (event.type ===
|
|
263
|
+
if (event.type === "error") lastErrorEvent = event;
|
|
243
264
|
if (this.onEvent) {
|
|
244
|
-
try {
|
|
265
|
+
try {
|
|
266
|
+
this.onEvent(event);
|
|
267
|
+
} catch {}
|
|
245
268
|
}
|
|
246
269
|
});
|
|
247
270
|
|
|
248
|
-
stderrReader.on(
|
|
271
|
+
stderrReader.on("line", (line) => {
|
|
249
272
|
if (!line) return;
|
|
250
273
|
stderrLines.push(line);
|
|
251
274
|
});
|
|
252
275
|
|
|
253
276
|
const closePromise = new Promise((resolve, reject) => {
|
|
254
|
-
child.once(
|
|
255
|
-
child.once(
|
|
277
|
+
child.once("error", reject);
|
|
278
|
+
child.once("close", (code, signal) => {
|
|
256
279
|
exitCode = code;
|
|
257
280
|
exitSignal = signal;
|
|
258
281
|
resolve();
|
|
@@ -265,8 +288,8 @@ export class GeminiWorker {
|
|
|
265
288
|
}, timeoutMs);
|
|
266
289
|
timeout.unref?.();
|
|
267
290
|
|
|
268
|
-
child.stdin.on(
|
|
269
|
-
child.stdin.end(String(prompt ??
|
|
291
|
+
child.stdin.on("error", () => {});
|
|
292
|
+
child.stdin.end(String(prompt ?? ""));
|
|
270
293
|
|
|
271
294
|
try {
|
|
272
295
|
await closePromise;
|
|
@@ -277,32 +300,37 @@ export class GeminiWorker {
|
|
|
277
300
|
if (this.child === child) {
|
|
278
301
|
this.child = null;
|
|
279
302
|
}
|
|
280
|
-
this.state =
|
|
303
|
+
this.state = "idle";
|
|
281
304
|
}
|
|
282
305
|
|
|
283
|
-
const resultEvent = findLastEvent(
|
|
306
|
+
const resultEvent = findLastEvent(
|
|
307
|
+
events,
|
|
308
|
+
(event) => event?.type === "result",
|
|
309
|
+
);
|
|
284
310
|
const response = [
|
|
285
311
|
extractText(resultEvent),
|
|
286
312
|
...events
|
|
287
|
-
.filter(
|
|
313
|
+
.filter(
|
|
314
|
+
(event) => event?.type === "message" || event?.type === "assistant",
|
|
315
|
+
)
|
|
288
316
|
.map((event) => extractText(event))
|
|
289
317
|
.filter(Boolean),
|
|
290
318
|
...stdoutLines,
|
|
291
319
|
]
|
|
292
320
|
.filter(Boolean)
|
|
293
|
-
.join(
|
|
321
|
+
.join("\n")
|
|
294
322
|
.trim();
|
|
295
323
|
|
|
296
324
|
const result = {
|
|
297
|
-
type:
|
|
325
|
+
type: "gemini",
|
|
298
326
|
command: this.command,
|
|
299
327
|
args,
|
|
300
328
|
response,
|
|
301
329
|
events,
|
|
302
330
|
resultEvent,
|
|
303
331
|
usage: resultEvent?.usage || null,
|
|
304
|
-
stdout: stdoutLines.join(
|
|
305
|
-
stderr: stderrLines.join(
|
|
332
|
+
stdout: stdoutLines.join("\n").trim(),
|
|
333
|
+
stderr: stderrLines.join("\n").trim(),
|
|
306
334
|
exitCode,
|
|
307
335
|
exitSignal,
|
|
308
336
|
timedOut,
|
|
@@ -314,7 +342,7 @@ export class GeminiWorker {
|
|
|
314
342
|
|
|
315
343
|
if (timedOut) {
|
|
316
344
|
throw createWorkerError(`Gemini worker timed out after ${timeoutMs}ms`, {
|
|
317
|
-
code:
|
|
345
|
+
code: "ETIMEDOUT",
|
|
318
346
|
result,
|
|
319
347
|
stderr: result.stderr,
|
|
320
348
|
});
|
|
@@ -322,15 +350,15 @@ export class GeminiWorker {
|
|
|
322
350
|
|
|
323
351
|
if (exitCode !== 0) {
|
|
324
352
|
throw createWorkerError(`Gemini worker exited with code ${exitCode}`, {
|
|
325
|
-
code:
|
|
353
|
+
code: "WORKER_EXIT",
|
|
326
354
|
result,
|
|
327
355
|
stderr: result.stderr,
|
|
328
356
|
});
|
|
329
357
|
}
|
|
330
358
|
|
|
331
359
|
if (lastErrorEvent) {
|
|
332
|
-
throw createWorkerError(
|
|
333
|
-
code:
|
|
360
|
+
throw createWorkerError("Gemini worker emitted an error event", {
|
|
361
|
+
code: "WORKER_EVENT_ERROR",
|
|
334
362
|
result,
|
|
335
363
|
stderr: result.stderr,
|
|
336
364
|
});
|
|
@@ -340,20 +368,23 @@ export class GeminiWorker {
|
|
|
340
368
|
}
|
|
341
369
|
|
|
342
370
|
isReady() {
|
|
343
|
-
return this.state !==
|
|
371
|
+
return this.state !== "stopped";
|
|
344
372
|
}
|
|
345
373
|
|
|
346
374
|
async execute(prompt, options = {}) {
|
|
347
375
|
let attempts = 0;
|
|
348
376
|
|
|
349
377
|
try {
|
|
350
|
-
const result = await withRetry(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
378
|
+
const result = await withRetry(
|
|
379
|
+
async () => {
|
|
380
|
+
attempts += 1;
|
|
381
|
+
return this.run(prompt, options);
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
...this.retryOptions,
|
|
385
|
+
shouldRetry: (error) => isGeminiRetryable(error),
|
|
386
|
+
},
|
|
387
|
+
);
|
|
357
388
|
|
|
358
389
|
return {
|
|
359
390
|
output: result.response,
|
|
@@ -363,8 +394,8 @@ export class GeminiWorker {
|
|
|
363
394
|
};
|
|
364
395
|
} catch (error) {
|
|
365
396
|
return {
|
|
366
|
-
output: error.stderr || error.message ||
|
|
367
|
-
exitCode: error.code ===
|
|
397
|
+
output: error.stderr || error.message || "Gemini worker failed",
|
|
398
|
+
exitCode: error.code === "ETIMEDOUT" ? 124 : 1,
|
|
368
399
|
sessionKey: options.sessionKey || null,
|
|
369
400
|
error: buildGeminiErrorInfo(error, attempts || 1),
|
|
370
401
|
raw: error.result || null,
|
|
@@ -49,4 +49,9 @@
|
|
|
49
49
|
* @property {string} type - 'codex' | 'gemini' | 'claude' | 'delegator'
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
|
-
export const WORKER_TYPES = Object.freeze([
|
|
52
|
+
export const WORKER_TYPES = Object.freeze([
|
|
53
|
+
"codex",
|
|
54
|
+
"gemini",
|
|
55
|
+
"claude",
|
|
56
|
+
"delegator",
|
|
57
|
+
]);
|
|
@@ -6,9 +6,7 @@ export const DEFAULT_KILL_GRACE_MS = 1000;
|
|
|
6
6
|
|
|
7
7
|
export function toStringList(value) {
|
|
8
8
|
if (!Array.isArray(value)) return [];
|
|
9
|
-
return value
|
|
10
|
-
.map((item) => String(item ?? '').trim())
|
|
11
|
-
.filter(Boolean);
|
|
9
|
+
return value.map((item) => String(item ?? "").trim()).filter(Boolean);
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
export function safeJsonParse(line) {
|
|
@@ -51,8 +49,9 @@ export async function withRetry(fn, opts = {}) {
|
|
|
51
49
|
throw error;
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
const delay =
|
|
55
|
-
* (
|
|
52
|
+
const delay =
|
|
53
|
+
Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs) *
|
|
54
|
+
(0.5 + Math.random() * 0.5);
|
|
56
55
|
await sleep(delay);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -63,7 +62,7 @@ export async function withRetry(fn, opts = {}) {
|
|
|
63
62
|
export function appendTextFragments(value, parts) {
|
|
64
63
|
if (value == null) return;
|
|
65
64
|
|
|
66
|
-
if (typeof value ===
|
|
65
|
+
if (typeof value === "string") {
|
|
67
66
|
const trimmed = value.trim();
|
|
68
67
|
if (trimmed) parts.push(trimmed);
|
|
69
68
|
return;
|
|
@@ -74,11 +73,13 @@ export function appendTextFragments(value, parts) {
|
|
|
74
73
|
return;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
if (typeof value !==
|
|
76
|
+
if (typeof value !== "object") return;
|
|
78
77
|
|
|
79
|
-
if (typeof value.text ===
|
|
80
|
-
if (typeof value.response ===
|
|
81
|
-
|
|
78
|
+
if (typeof value.text === "string") appendTextFragments(value.text, parts);
|
|
79
|
+
if (typeof value.response === "string")
|
|
80
|
+
appendTextFragments(value.response, parts);
|
|
81
|
+
if (typeof value.result === "string")
|
|
82
|
+
appendTextFragments(value.result, parts);
|
|
82
83
|
if (value.content != null) appendTextFragments(value.content, parts);
|
|
83
84
|
if (value.message != null) appendTextFragments(value.message, parts);
|
|
84
85
|
}
|
|
@@ -86,18 +87,24 @@ export function appendTextFragments(value, parts) {
|
|
|
86
87
|
export function extractText(value) {
|
|
87
88
|
const parts = [];
|
|
88
89
|
appendTextFragments(value, parts);
|
|
89
|
-
return parts.join(
|
|
90
|
+
return parts.join("\n").trim();
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
export function terminateChild(child, killGraceMs) {
|
|
93
94
|
if (!child || child.exitCode !== null || child.killed) return;
|
|
94
95
|
|
|
95
|
-
try {
|
|
96
|
-
|
|
96
|
+
try {
|
|
97
|
+
child.stdin.end();
|
|
98
|
+
} catch {}
|
|
99
|
+
try {
|
|
100
|
+
child.kill();
|
|
101
|
+
} catch {}
|
|
97
102
|
|
|
98
103
|
const timer = setTimeout(() => {
|
|
99
104
|
if (child.exitCode === null) {
|
|
100
|
-
try {
|
|
105
|
+
try {
|
|
106
|
+
child.kill("SIGKILL");
|
|
107
|
+
} catch {}
|
|
101
108
|
}
|
|
102
109
|
}, killGraceMs);
|
|
103
110
|
timer.unref?.();
|
package/hud/colors.mjs
CHANGED
|
@@ -12,15 +12,33 @@ export const CLAUDE_ORANGE = "\x1b[38;2;232;112;64m"; // #E87040 (Claude 공식
|
|
|
12
12
|
export const CODEX_WHITE = "\x1b[97m"; // bright white (SGR 37은 Windows Terminal에서 연회색 매핑)
|
|
13
13
|
export const GEMINI_BLUE = "\x1b[38;5;39m";
|
|
14
14
|
|
|
15
|
-
export function green(t) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
export function green(t) {
|
|
16
|
+
return `${GREEN}${t}${RESET}`;
|
|
17
|
+
}
|
|
18
|
+
export function yellow(t) {
|
|
19
|
+
return `${YELLOW}${t}${RESET}`;
|
|
20
|
+
}
|
|
21
|
+
export function red(t) {
|
|
22
|
+
return `${RED}${t}${RESET}`;
|
|
23
|
+
}
|
|
24
|
+
export function cyan(t) {
|
|
25
|
+
return `${CYAN}${t}${RESET}`;
|
|
26
|
+
}
|
|
27
|
+
export function dim(t) {
|
|
28
|
+
return `${DIM}${t}${RESET}`;
|
|
29
|
+
}
|
|
30
|
+
export function bold(t) {
|
|
31
|
+
return `${BOLD}${t}${RESET}`;
|
|
32
|
+
}
|
|
33
|
+
export function claudeOrange(t) {
|
|
34
|
+
return `${CLAUDE_ORANGE}${t}${RESET}`;
|
|
35
|
+
}
|
|
36
|
+
export function codexWhite(t) {
|
|
37
|
+
return `${CODEX_WHITE}${t}${RESET}`;
|
|
38
|
+
}
|
|
39
|
+
export function geminiBlue(t) {
|
|
40
|
+
return `${GEMINI_BLUE}${t}${RESET}`;
|
|
41
|
+
}
|
|
24
42
|
|
|
25
43
|
export function colorByPercent(value, text) {
|
|
26
44
|
if (value >= 85) return red(text);
|