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,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// tfx-route-worker.mjs — tfx-route.sh용 subprocess worker 러너
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { dirname, resolve } from
|
|
6
|
-
import { fileURLToPath, pathToFileURL } from
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
|
|
8
8
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const FACTORY_CANDIDATES = [
|
|
10
|
-
resolve(SCRIPT_DIR,
|
|
11
|
-
resolve(SCRIPT_DIR,
|
|
10
|
+
resolve(SCRIPT_DIR, "../hub/workers/factory.mjs"),
|
|
11
|
+
resolve(SCRIPT_DIR, "./hub/workers/factory.mjs"),
|
|
12
12
|
];
|
|
13
13
|
|
|
14
14
|
// MCP transport 실패 시 tfx-route.sh가 exec fallback을 수행할 수 있도록
|
|
@@ -16,10 +16,10 @@ const FACTORY_CANDIDATES = [
|
|
|
16
16
|
const MCP_TRANSPORT_EXIT_CODE = 70;
|
|
17
17
|
const GEMINI_RETRY_DELAY_MS = 5000;
|
|
18
18
|
const GEMINI_RETRY_PATTERN_SNIPPETS = [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
"429",
|
|
20
|
+
"quota",
|
|
21
|
+
"rate limit",
|
|
22
|
+
"resource_exhausted",
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
let createWorker = null;
|
|
@@ -30,8 +30,10 @@ for (const candidate of FACTORY_CANDIDATES) {
|
|
|
30
30
|
({ createWorker } = await import(pathToFileURL(candidate).href));
|
|
31
31
|
} catch (err) {
|
|
32
32
|
// 의존성 누락 (예: @modelcontextprotocol/sdk) → fallback 가능하도록 exit 70
|
|
33
|
-
if (err.code ===
|
|
34
|
-
process.stderr.write(
|
|
33
|
+
if (err.code === "ERR_MODULE_NOT_FOUND") {
|
|
34
|
+
process.stderr.write(
|
|
35
|
+
`[tfx-route-worker] 모듈 로드 실패: ${err.message}\n`,
|
|
36
|
+
);
|
|
35
37
|
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
36
38
|
}
|
|
37
39
|
throw err;
|
|
@@ -40,7 +42,9 @@ for (const candidate of FACTORY_CANDIDATES) {
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
if (!createWorker) {
|
|
43
|
-
process.stderr.write(
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
"[tfx-route-worker] worker factory를 찾지 못했습니다.\n",
|
|
47
|
+
);
|
|
44
48
|
process.exit(MCP_TRANSPORT_EXIT_CODE);
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -55,46 +59,46 @@ function parseArgs(argv) {
|
|
|
55
59
|
const next = argv[index + 1];
|
|
56
60
|
|
|
57
61
|
switch (token) {
|
|
58
|
-
case
|
|
62
|
+
case "--type":
|
|
59
63
|
args.type = next;
|
|
60
64
|
index += 1;
|
|
61
65
|
break;
|
|
62
|
-
case
|
|
66
|
+
case "--command":
|
|
63
67
|
args.command = next;
|
|
64
68
|
index += 1;
|
|
65
69
|
break;
|
|
66
|
-
case
|
|
70
|
+
case "--command-args-json":
|
|
67
71
|
args.commandArgsJson = next;
|
|
68
72
|
index += 1;
|
|
69
73
|
break;
|
|
70
|
-
case
|
|
74
|
+
case "--model":
|
|
71
75
|
args.model = next;
|
|
72
76
|
index += 1;
|
|
73
77
|
break;
|
|
74
|
-
case
|
|
78
|
+
case "--timeout-ms":
|
|
75
79
|
args.timeoutMs = Number(next);
|
|
76
80
|
index += 1;
|
|
77
81
|
break;
|
|
78
|
-
case
|
|
82
|
+
case "--approval-mode":
|
|
79
83
|
args.approvalMode = next;
|
|
80
84
|
index += 1;
|
|
81
85
|
break;
|
|
82
|
-
case
|
|
86
|
+
case "--permission-mode":
|
|
83
87
|
args.permissionMode = next;
|
|
84
88
|
index += 1;
|
|
85
89
|
break;
|
|
86
|
-
case
|
|
90
|
+
case "--allow-dangerously-skip-permissions":
|
|
87
91
|
args.allowDangerouslySkipPermissions = true;
|
|
88
92
|
break;
|
|
89
|
-
case
|
|
93
|
+
case "--allowed-mcp-server-name":
|
|
90
94
|
args.allowedMcpServerNames.push(next);
|
|
91
95
|
index += 1;
|
|
92
96
|
break;
|
|
93
|
-
case
|
|
97
|
+
case "--mcp-config":
|
|
94
98
|
args.mcpConfig.push(next);
|
|
95
99
|
index += 1;
|
|
96
100
|
break;
|
|
97
|
-
case
|
|
101
|
+
case "--cwd":
|
|
98
102
|
args.cwd = next;
|
|
99
103
|
index += 1;
|
|
100
104
|
break;
|
|
@@ -104,7 +108,7 @@ function parseArgs(argv) {
|
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
if (!args.type) {
|
|
107
|
-
throw new Error(
|
|
111
|
+
throw new Error("--type is required");
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
return args;
|
|
@@ -124,15 +128,17 @@ function parseJsonArray(raw, label) {
|
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
function readPromptFromStdin() {
|
|
127
|
-
return readFileSync(0,
|
|
131
|
+
return readFileSync(0, "utf8");
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
function resolveDefaultMcpConfig(cwd) {
|
|
131
|
-
const primary = resolve(cwd,
|
|
135
|
+
const primary = resolve(cwd, ".claude", "mcp.json");
|
|
132
136
|
if (existsSync(primary)) return [primary];
|
|
133
|
-
const legacy = resolve(cwd,
|
|
137
|
+
const legacy = resolve(cwd, ".mcp.json");
|
|
134
138
|
if (existsSync(legacy)) return [legacy];
|
|
135
|
-
process.stderr.write(
|
|
139
|
+
process.stderr.write(
|
|
140
|
+
"[tfx-route-worker] warning: no MCP config found, hub unavailable\n",
|
|
141
|
+
);
|
|
136
142
|
return [];
|
|
137
143
|
}
|
|
138
144
|
|
|
@@ -145,21 +151,19 @@ function isGeminiQuotaRetrySignal(error) {
|
|
|
145
151
|
return true;
|
|
146
152
|
}
|
|
147
153
|
|
|
148
|
-
const fragments = [
|
|
149
|
-
|
|
150
|
-
error?.stderr,
|
|
151
|
-
error?.result?.stderr,
|
|
152
|
-
]
|
|
153
|
-
.filter((value) => typeof value === 'string' && value.trim().length > 0)
|
|
154
|
+
const fragments = [error?.message, error?.stderr, error?.result?.stderr]
|
|
155
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
154
156
|
.map((value) => value.toLowerCase());
|
|
155
157
|
|
|
156
158
|
if (fragments.length === 0) return false;
|
|
157
|
-
const merged = fragments.join(
|
|
158
|
-
return GEMINI_RETRY_PATTERN_SNIPPETS.some((pattern) =>
|
|
159
|
+
const merged = fragments.join("\n");
|
|
160
|
+
return GEMINI_RETRY_PATTERN_SNIPPETS.some((pattern) =>
|
|
161
|
+
merged.includes(pattern),
|
|
162
|
+
);
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
async function runWorker(worker, type, prompt) {
|
|
162
|
-
const maxAttempts = type ===
|
|
166
|
+
const maxAttempts = type === "gemini" ? 2 : 1;
|
|
163
167
|
let lastError = null;
|
|
164
168
|
|
|
165
169
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -167,18 +171,17 @@ async function runWorker(worker, type, prompt) {
|
|
|
167
171
|
return await worker.run(prompt);
|
|
168
172
|
} catch (error) {
|
|
169
173
|
lastError = error;
|
|
170
|
-
const shouldRetry =
|
|
171
|
-
type ===
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
);
|
|
174
|
+
const shouldRetry =
|
|
175
|
+
type === "gemini" &&
|
|
176
|
+
attempt < maxAttempts &&
|
|
177
|
+
isGeminiQuotaRetrySignal(error);
|
|
175
178
|
|
|
176
179
|
if (!shouldRetry) {
|
|
177
180
|
throw error;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
process.stderr.write(
|
|
181
|
-
|
|
184
|
+
"[tfx-route-worker] Gemini 429/quota 감지 — 5초 후 1회 재시도합니다.\n",
|
|
182
185
|
);
|
|
183
186
|
await sleep(GEMINI_RETRY_DELAY_MS);
|
|
184
187
|
}
|
|
@@ -192,16 +195,17 @@ const prompt = readPromptFromStdin();
|
|
|
192
195
|
|
|
193
196
|
const worker = createWorker(args.type, {
|
|
194
197
|
command: args.command,
|
|
195
|
-
commandArgs: parseJsonArray(args.commandArgsJson,
|
|
198
|
+
commandArgs: parseJsonArray(args.commandArgsJson, "--command-args-json"),
|
|
196
199
|
model: args.model,
|
|
197
200
|
timeoutMs: args.timeoutMs,
|
|
198
201
|
approvalMode: args.approvalMode,
|
|
199
202
|
permissionMode: args.permissionMode,
|
|
200
203
|
allowDangerouslySkipPermissions: args.allowDangerouslySkipPermissions,
|
|
201
204
|
allowedMcpServerNames: args.allowedMcpServerNames,
|
|
202
|
-
mcpConfig:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
mcpConfig:
|
|
206
|
+
args.type === "claude" && args.mcpConfig.length === 0
|
|
207
|
+
? resolveDefaultMcpConfig(args.cwd || process.cwd())
|
|
208
|
+
: args.mcpConfig,
|
|
205
209
|
cwd: args.cwd || process.cwd(),
|
|
206
210
|
});
|
|
207
211
|
|
|
@@ -209,15 +213,17 @@ try {
|
|
|
209
213
|
const result = await runWorker(worker, args.type, prompt);
|
|
210
214
|
if (result.response) {
|
|
211
215
|
process.stdout.write(result.response);
|
|
212
|
-
if (!result.response.endsWith(
|
|
216
|
+
if (!result.response.endsWith("\n")) process.stdout.write("\n");
|
|
213
217
|
}
|
|
214
218
|
} catch (error) {
|
|
215
219
|
if (error.stderr) {
|
|
216
220
|
process.stderr.write(String(error.stderr));
|
|
217
|
-
if (!String(error.stderr).endsWith(
|
|
221
|
+
if (!String(error.stderr).endsWith("\n")) process.stderr.write("\n");
|
|
218
222
|
}
|
|
219
223
|
process.stderr.write(`${error.message}\n`);
|
|
220
|
-
process.exitCode = error.code ===
|
|
224
|
+
process.exitCode = error.code === "ETIMEDOUT" ? 124 : 1;
|
|
221
225
|
} finally {
|
|
222
|
-
try {
|
|
226
|
+
try {
|
|
227
|
+
await worker.stop();
|
|
228
|
+
} catch {}
|
|
223
229
|
}
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -56,6 +56,37 @@ resolve_tmp_dir() {
|
|
|
56
56
|
|
|
57
57
|
TFX_TMP="$(resolve_tmp_dir)"
|
|
58
58
|
|
|
59
|
+
# ── Worker PID 추적 (EXIT trap에서 정리) ──
|
|
60
|
+
_PID_TRACK="${TFX_TMP}/tfx-route-$$-pids"
|
|
61
|
+
|
|
62
|
+
track_worker_pid() {
|
|
63
|
+
echo "$1" >> "$_PID_TRACK"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
cleanup_workers() {
|
|
67
|
+
deregister_agent 2>/dev/null || true
|
|
68
|
+
[[ ! -f "$_PID_TRACK" ]] && return
|
|
69
|
+
while IFS= read -r pid; do
|
|
70
|
+
[[ -z "$pid" ]] && continue
|
|
71
|
+
kill -0 "$pid" 2>/dev/null || continue
|
|
72
|
+
case "$(uname -s)" in
|
|
73
|
+
MINGW*|MSYS*)
|
|
74
|
+
# Windows: taskkill /T /F로 프로세스 트리 전체 종료
|
|
75
|
+
MSYS_NO_PATHCONV=1 cmd.exe //c "taskkill /T /F /PID $pid" 2>/dev/null || true ;;
|
|
76
|
+
*)
|
|
77
|
+
# Unix: 프로세스 그룹 kill
|
|
78
|
+
local pgid
|
|
79
|
+
pgid=$(ps -o pgid= -p "$pid" 2>/dev/null | tr -d ' ')
|
|
80
|
+
if [[ -n "$pgid" && "$pgid" != "0" ]]; then
|
|
81
|
+
kill -- "-$pgid" 2>/dev/null || true
|
|
82
|
+
else
|
|
83
|
+
kill "$pid" 2>/dev/null || true
|
|
84
|
+
fi ;;
|
|
85
|
+
esac
|
|
86
|
+
done < "$_PID_TRACK"
|
|
87
|
+
rm -f "$_PID_TRACK"
|
|
88
|
+
}
|
|
89
|
+
|
|
59
90
|
# ── config.toml sandbox/approval_mode 감지 ──
|
|
60
91
|
# config.toml에 이미 설정되어 있으면 CLI 플래그 중복 시 Codex가 에러를 던짐
|
|
61
92
|
_CODEX_CONFIG="${HOME}/.codex/config.toml"
|
|
@@ -1375,6 +1406,7 @@ run_stream_worker() {
|
|
|
1375
1406
|
printf '%s' "$prompt" | "$TIMEOUT_BIN" "$TIMEOUT_SEC" "${worker_cmd[@]}" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
|
|
1376
1407
|
fi
|
|
1377
1408
|
worker_pid=$!
|
|
1409
|
+
track_worker_pid "$worker_pid"
|
|
1378
1410
|
|
|
1379
1411
|
heartbeat_monitor "$worker_pid" &
|
|
1380
1412
|
hb_pid=$!
|
|
@@ -1384,125 +1416,6 @@ run_stream_worker() {
|
|
|
1384
1416
|
return "$exit_code_local"
|
|
1385
1417
|
}
|
|
1386
1418
|
|
|
1387
|
-
# Gemini 429 지수 백오프 재시도 래퍼
|
|
1388
|
-
# 사용: gemini_with_retry <use_tee_flag> <gemini_args_array_name> <prompt>
|
|
1389
|
-
# 429/rate limit 감지 시 최대 3회 재시도 (2→4→8초 백오프)
|
|
1390
|
-
_gemini_run_once() {
|
|
1391
|
-
local use_tee_flag="$1"
|
|
1392
|
-
local prompt="$2"
|
|
1393
|
-
shift 2
|
|
1394
|
-
local -a g_args=("$@")
|
|
1395
|
-
|
|
1396
|
-
if [[ "$use_tee_flag" == "true" ]]; then
|
|
1397
|
-
"$TIMEOUT_BIN" "$TIMEOUT_SEC" "$CLI_CMD" "${g_args[@]}" "$prompt" 2>"$STDERR_LOG" | tee "$STDOUT_LOG" &
|
|
1398
|
-
else
|
|
1399
|
-
"$TIMEOUT_BIN" "$TIMEOUT_SEC" "$CLI_CMD" "${g_args[@]}" "$prompt" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
|
|
1400
|
-
fi
|
|
1401
|
-
GEMINI_RUN_PID=$!
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
gemini_with_retry() {
|
|
1405
|
-
local use_tee_flag="$1"
|
|
1406
|
-
local prompt="$2"
|
|
1407
|
-
shift 2
|
|
1408
|
-
local -a g_args=("$@")
|
|
1409
|
-
|
|
1410
|
-
local max_retries=3
|
|
1411
|
-
local attempt=0
|
|
1412
|
-
local delay=2
|
|
1413
|
-
local exit_code_local=0
|
|
1414
|
-
|
|
1415
|
-
while (( attempt < max_retries )); do
|
|
1416
|
-
exit_code_local=0
|
|
1417
|
-
local pid
|
|
1418
|
-
_gemini_run_once "$use_tee_flag" "$prompt" "${g_args[@]}"
|
|
1419
|
-
pid="${GEMINI_RUN_PID:-}"
|
|
1420
|
-
if [[ -z "$pid" ]]; then
|
|
1421
|
-
echo "[tfx-route] Gemini: worker pid 획득 실패" >&2
|
|
1422
|
-
return 1
|
|
1423
|
-
fi
|
|
1424
|
-
|
|
1425
|
-
local health_ok=true
|
|
1426
|
-
local intervals=(1 2 3 5 8)
|
|
1427
|
-
for wait_sec in "${intervals[@]}"; do
|
|
1428
|
-
sleep "$wait_sec"
|
|
1429
|
-
if [[ -s "$STDOUT_LOG" ]] || [[ -s "$STDERR_LOG" ]]; then
|
|
1430
|
-
break
|
|
1431
|
-
fi
|
|
1432
|
-
if ! kill -0 "$pid" 2>/dev/null; then
|
|
1433
|
-
health_ok=false
|
|
1434
|
-
echo "[tfx-route] Gemini: 출력 없이 프로세스 종료 (${wait_sec}초 체크)" >&2
|
|
1435
|
-
break
|
|
1436
|
-
fi
|
|
1437
|
-
done
|
|
1438
|
-
|
|
1439
|
-
local hb_pid
|
|
1440
|
-
if [[ "$health_ok" == "false" ]]; then
|
|
1441
|
-
wait "$pid" 2>/dev/null
|
|
1442
|
-
else
|
|
1443
|
-
heartbeat_monitor "$pid" &
|
|
1444
|
-
hb_pid=$!
|
|
1445
|
-
wait "$pid" || exit_code_local=$?
|
|
1446
|
-
kill "$hb_pid" 2>/dev/null; wait "$hb_pid" 2>/dev/null
|
|
1447
|
-
fi
|
|
1448
|
-
|
|
1449
|
-
# 성공 시 즉시 반환
|
|
1450
|
-
if [[ $exit_code_local -eq 0 ]]; then
|
|
1451
|
-
return 0
|
|
1452
|
-
fi
|
|
1453
|
-
|
|
1454
|
-
# 429 / rate limit 감지
|
|
1455
|
-
if grep -qiE '429|rate.limit|too many requests' "$STDERR_LOG" 2>/dev/null; then
|
|
1456
|
-
attempt=$(( attempt + 1 ))
|
|
1457
|
-
if (( attempt < max_retries )); then
|
|
1458
|
-
echo "[tfx-route] Gemini 429 감지. ${delay}초 후 재시도 ($attempt/$max_retries)..." >&2
|
|
1459
|
-
kill "$pid" 2>/dev/null
|
|
1460
|
-
wait "$pid" 2>/dev/null
|
|
1461
|
-
sleep "$delay"
|
|
1462
|
-
delay=$(( delay * 2 ))
|
|
1463
|
-
: > "$STDOUT_LOG"
|
|
1464
|
-
: > "$STDERR_LOG"
|
|
1465
|
-
continue
|
|
1466
|
-
else
|
|
1467
|
-
echo "[tfx-route] Gemini 429: ${max_retries}회 재시도 실패" >&2
|
|
1468
|
-
fi
|
|
1469
|
-
fi
|
|
1470
|
-
|
|
1471
|
-
# 비-429 에러 또는 최대 재시도 초과 시 즉시 반환
|
|
1472
|
-
return "$exit_code_local"
|
|
1473
|
-
done
|
|
1474
|
-
|
|
1475
|
-
return "$exit_code_local"
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
run_legacy_gemini() {
|
|
1479
|
-
local prompt="$1"
|
|
1480
|
-
local use_tee_flag="$2"
|
|
1481
|
-
local -a gemini_args=()
|
|
1482
|
-
read -r -a gemini_args <<< "$CLI_ARGS"
|
|
1483
|
-
|
|
1484
|
-
if [[ ${#GEMINI_ALLOWED_SERVERS[@]} -gt 0 ]]; then
|
|
1485
|
-
local gemini_mcp_filter prompt_index=-1
|
|
1486
|
-
gemini_mcp_filter=$(IFS=,; echo "${GEMINI_ALLOWED_SERVERS[*]}")
|
|
1487
|
-
for i in "${!gemini_args[@]}"; do
|
|
1488
|
-
if [[ "${gemini_args[$i]}" == "--prompt" ]]; then
|
|
1489
|
-
prompt_index="$i"
|
|
1490
|
-
break
|
|
1491
|
-
fi
|
|
1492
|
-
done
|
|
1493
|
-
if [[ "$prompt_index" -ge 0 ]]; then
|
|
1494
|
-
gemini_args=(
|
|
1495
|
-
"${gemini_args[@]:0:$prompt_index}"
|
|
1496
|
-
"--allowed-mcp-server-names" "$gemini_mcp_filter"
|
|
1497
|
-
"${gemini_args[@]:$prompt_index}"
|
|
1498
|
-
)
|
|
1499
|
-
echo "[tfx-route] Gemini MCP 필터: $gemini_mcp_filter" >&2
|
|
1500
|
-
fi
|
|
1501
|
-
fi
|
|
1502
|
-
|
|
1503
|
-
gemini_with_retry "$use_tee_flag" "$prompt" "${gemini_args[@]}"
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
1419
|
resolve_codex_mcp_script() {
|
|
1507
1420
|
if [[ -n "${TFX_CODEX_MCP_SCRIPT:-}" && -f "$TFX_CODEX_MCP_SCRIPT" ]]; then
|
|
1508
1421
|
printf '%s\n' "$TFX_CODEX_MCP_SCRIPT"
|
|
@@ -1547,6 +1460,7 @@ run_codex_exec() {
|
|
|
1547
1460
|
"$TIMEOUT_BIN" "$TIMEOUT_SEC" "$CLI_CMD" "${codex_args[@]}" "$prompt" < /dev/null >"$STDOUT_LOG" 2>"$STDERR_LOG" &
|
|
1548
1461
|
fi
|
|
1549
1462
|
worker_pid=$!
|
|
1463
|
+
track_worker_pid "$worker_pid"
|
|
1550
1464
|
|
|
1551
1465
|
heartbeat_monitor "$worker_pid" &
|
|
1552
1466
|
hb_pid=$!
|
|
@@ -1641,6 +1555,7 @@ run_codex_mcp() {
|
|
|
1641
1555
|
"$TIMEOUT_BIN" "$TIMEOUT_SEC" "$node_bin" "${mcp_args[@]}" >"$STDOUT_LOG" 2>"$STDERR_LOG" &
|
|
1642
1556
|
fi
|
|
1643
1557
|
worker_pid=$!
|
|
1558
|
+
track_worker_pid "$worker_pid"
|
|
1644
1559
|
|
|
1645
1560
|
heartbeat_monitor "$worker_pid" &
|
|
1646
1561
|
hb_pid=$!
|
|
@@ -1665,8 +1580,8 @@ run_codex_mcp() {
|
|
|
1665
1580
|
|
|
1666
1581
|
# ── 메인 실행 ──
|
|
1667
1582
|
main() {
|
|
1668
|
-
# 종료 시 per-process 에이전트 파일
|
|
1669
|
-
trap '
|
|
1583
|
+
# 종료 시 per-process 에이전트 파일 + 워커 프로세스 정리
|
|
1584
|
+
trap 'cleanup_workers' EXIT
|
|
1670
1585
|
|
|
1671
1586
|
route_agent "$AGENT_TYPE"
|
|
1672
1587
|
apply_cli_mode
|
|
@@ -1847,11 +1762,13 @@ FALLBACK_EOF
|
|
|
1847
1762
|
|
|
1848
1763
|
run_stream_worker "gemini" "$FULL_PROMPT" "$use_tee" "${gemini_worker_args[@]}" || exit_code=$?
|
|
1849
1764
|
if [[ "$exit_code" -ne 0 && "$exit_code" -ne 124 ]]; then
|
|
1850
|
-
echo "[tfx-route] Gemini stream wrapper 실패(exit=${exit_code}).
|
|
1851
|
-
|
|
1765
|
+
echo "[tfx-route] Gemini stream wrapper 실패(exit=${exit_code}). claude-native fallback." >&2
|
|
1766
|
+
cat > "$STDOUT_LOG" <<EOF
|
|
1767
|
+
$(emit_claude_native_metadata)
|
|
1768
|
+
EOF
|
|
1852
1769
|
: > "$STDERR_LOG"
|
|
1853
1770
|
exit_code=0
|
|
1854
|
-
|
|
1771
|
+
CLI_TYPE="claude-native"
|
|
1855
1772
|
fi
|
|
1856
1773
|
|
|
1857
1774
|
elif [[ "$CLI_TYPE" == "claude" ]]; then
|
package/scripts/tmp-cleanup.mjs
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* 7일 이상 된 파일을 삭제한다.
|
|
5
5
|
* SessionStart 훅 또는 독립 실행으로 사용.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, readdirSync,
|
|
8
|
-
import { join, resolve } from "node:path";
|
|
7
|
+
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
9
8
|
import { tmpdir } from "node:os";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
10
|
|
|
11
11
|
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7일
|
|
12
12
|
const TRIFLUX_CLI_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 1일
|
|
@@ -50,7 +50,11 @@ export async function cleanupTmpFiles({ protectPaths = [] } = {}) {
|
|
|
50
50
|
|
|
51
51
|
// 1) tmpdir() 직하위의 관리 대상 항목 정리
|
|
52
52
|
let topEntries;
|
|
53
|
-
try {
|
|
53
|
+
try {
|
|
54
|
+
topEntries = readdirSync(tmp);
|
|
55
|
+
} catch {
|
|
56
|
+
topEntries = [];
|
|
57
|
+
}
|
|
54
58
|
|
|
55
59
|
for (const entry of topEntries) {
|
|
56
60
|
const rule = TOP_LEVEL_RULES.find(({ prefix }) => entry.startsWith(prefix));
|
|
@@ -66,14 +70,20 @@ export async function cleanupTmpFiles({ protectPaths = [] } = {}) {
|
|
|
66
70
|
rmSync(full, { recursive: true, force: true });
|
|
67
71
|
cleaned++;
|
|
68
72
|
}
|
|
69
|
-
} catch {
|
|
73
|
+
} catch {
|
|
74
|
+
/* 권한 에러 등 무시 */
|
|
75
|
+
}
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
// 2) tfx-headless/ 내 오래된 결과 파일 정리 (디렉터리 자체는 유지)
|
|
73
79
|
const headlessDir = join(tmp, "tfx-headless");
|
|
74
80
|
if (existsSync(headlessDir)) {
|
|
75
81
|
let entries;
|
|
76
|
-
try {
|
|
82
|
+
try {
|
|
83
|
+
entries = readdirSync(headlessDir);
|
|
84
|
+
} catch {
|
|
85
|
+
entries = [];
|
|
86
|
+
}
|
|
77
87
|
|
|
78
88
|
for (const entry of entries) {
|
|
79
89
|
if (SKIP_FILES.has(entry)) continue;
|
|
@@ -84,7 +94,9 @@ export async function cleanupTmpFiles({ protectPaths = [] } = {}) {
|
|
|
84
94
|
rmSync(full, { recursive: true, force: true });
|
|
85
95
|
cleaned++;
|
|
86
96
|
}
|
|
87
|
-
} catch {
|
|
97
|
+
} catch {
|
|
98
|
+
/* 권한 에러 등 무시 */
|
|
99
|
+
}
|
|
88
100
|
}
|
|
89
101
|
}
|
|
90
102
|
|
|
@@ -97,7 +109,9 @@ if (process.argv[1]) {
|
|
|
97
109
|
if (fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
98
110
|
const cleaned = await cleanupTmpFiles();
|
|
99
111
|
if (cleaned > 0) {
|
|
100
|
-
process.stdout.write(
|
|
112
|
+
process.stdout.write(
|
|
113
|
+
JSON.stringify({ message: `tfx-cleanup: ${cleaned} files removed` }),
|
|
114
|
+
);
|
|
101
115
|
}
|
|
102
116
|
}
|
|
103
117
|
}
|