triflux 10.9.5 → 10.9.7
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/CLAUDE.md +212 -0
- package/hub/account-broker.mjs +5 -0
- package/hub/cli-adapter-base.mjs +48 -1
- package/hub/pipe.mjs +6 -2
- package/hub/server.mjs +79 -3
- package/package.json +21 -56
- package/references/hosts.json +46 -0
- package/scripts/headless-guard.mjs +19 -0
- package/scripts/session-stale-cleanup.mjs +8 -2
- package/scripts/setup.mjs +10 -0
- package/scripts/tfx-route-worker.mjs +0 -6
- package/scripts/tfx-route.sh +2 -26
- package/skills/tfx-deep-analysis/SKILL.md +7 -1
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +7 -1
- package/skills/tfx-deep-plan/SKILL.md +7 -1
- package/skills/tfx-deep-plan/SKILL.md.tmpl +7 -1
- package/skills/tfx-deep-review/SKILL.md +7 -1
- package/skills/tfx-deep-review/SKILL.md.tmpl +7 -1
- package/skills/tfx-persist/SKILL.md +7 -1
- package/skills/tfx-persist/SKILL.md.tmpl +7 -1
- package/skills/tfx-workspace/async-tests/run-tests.sh +203 -0
- package/skills/tfx-workspace/evals/evals.json +79 -0
- package/skills/tfx-workspace/iteration-1/benchmark.json +524 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/outputs/analysis.md +154 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/outputs/analysis.md +126 -0
- package/skills/tfx-workspace/iteration-1/codex-gemini-remap/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/eval_metadata.json +11 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/outputs/analysis.md +119 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/grading.json +25 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/outputs/analysis.md +115 -0
- package/skills/tfx-workspace/iteration-1/doctor-diagnosis/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/eval_metadata.json +10 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/grading.json +20 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/outputs/analysis.md +86 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/grading.json +20 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/outputs/analysis.md +81 -0
- package/skills/tfx-workspace/iteration-1/hub-start-sequence/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/outputs/analysis.md +316 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/outputs/analysis.md +352 -0
- package/skills/tfx-workspace/iteration-1/multi-team-creation/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/review.html +1325 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/outputs/analysis.md +97 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/outputs/analysis.md +94 -0
- package/skills/tfx-workspace/iteration-1/routing-implement-shortcut/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/eval_metadata.json +12 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/outputs/analysis.md +209 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/grading.json +30 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/outputs/analysis.md +193 -0
- package/skills/tfx-workspace/iteration-1/routing-multi-task-triage/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/benchmark.json +144 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/eval_metadata.json +13 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/grading.json +35 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/outputs/analysis.md +382 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/old_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/grading.json +35 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/outputs/analysis.md +333 -0
- package/skills/tfx-workspace/iteration-2/multi-team-creation-refactored/with_skill/timing.json +5 -0
- package/skills/tfx-workspace/iteration-2/review.html +1325 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto/SKILL.md +217 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-auto-codex/SKILL.md +77 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-codex/SKILL.md +65 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-doctor/SKILL.md +94 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-gemini/SKILL.md +82 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-hub/SKILL.md +133 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-multi/SKILL.md +426 -0
- package/skills/tfx-workspace/skill-snapshot/tfx-setup/SKILL.md +101 -0
- package/.claude-plugin/marketplace.json +0 -34
- package/.claude-plugin/plugin.json +0 -22
- package/config/mcp-registry.json +0 -29
- package/tui/codex-profile.mjs +0 -457
- package/tui/core.mjs +0 -266
- package/tui/doctor.mjs +0 -375
- package/tui/gemini-profile.mjs +0 -299
- package/tui/monitor-data.mjs +0 -152
- package/tui/monitor.mjs +0 -339
- package/tui/setup.mjs +0 -598
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<!-- prompt-hygiene:ignore line_count_warning -->
|
|
2
|
+
# triflux — Claude Code 운영 가이드
|
|
3
|
+
|
|
4
|
+
<core-systems>
|
|
5
|
+
## 핵심 스킬 시스템 (항상 인지)
|
|
6
|
+
|
|
7
|
+
이 프로젝트는 3개의 스킬 시스템을 동시에 사용한다. 어떤 작업이든 해당 시스템의 스킬이 있는지 먼저 확인한다.
|
|
8
|
+
|
|
9
|
+
| 시스템 | 접두사 | 용도 | 스킬 수 |
|
|
10
|
+
|--------|--------|------|---------|
|
|
11
|
+
| **triflux** | `/tfx-*` | CLI 라우팅, 멀티모델 오케스트레이션, 스웜, 원격 실행 | ~40개 |
|
|
12
|
+
| **gstack** | `/` (접두사 없음) | QA, ship, investigate, design, review, checkpoint | ~35개 |
|
|
13
|
+
| **omc** | `/oh-my-claudecode:*` | autopilot, ralph, team, ultrawork, ccg | ~25개 |
|
|
14
|
+
|
|
15
|
+
스킬을 모르면 자연어 라우팅(아래)으로 자동 매핑된다.
|
|
16
|
+
세션 종료 전 메모리 파일이 3개+ 변경됐으면 `/memory-hygiene` 제안을 검토한다.
|
|
17
|
+
</core-systems>
|
|
18
|
+
|
|
19
|
+
<psmux-wt>
|
|
20
|
+
## psmux/WT 규칙
|
|
21
|
+
|
|
22
|
+
psmux 세션·WT 패인을 생성/조작/정리할 때 `tfx-psmux-rules` 스킬을 참조한다.
|
|
23
|
+
WT 프리징 방지: exit → sleep 2 → kill 순서. 바로 kill하지 않는다.
|
|
24
|
+
|
|
25
|
+
### wt.exe → wt-manager 경유
|
|
26
|
+
|
|
27
|
+
safety-guard가 `wt.exe`, `wt new-tab`, `wt split-pane`, `Start-Process wt`를 차단한다.
|
|
28
|
+
`hub/team/wt-manager.mjs`의 API를 사용한다.
|
|
29
|
+
|
|
30
|
+
| 용도 | API |
|
|
31
|
+
|------|-----|
|
|
32
|
+
| 새 탭 | `createTab({ title, command, profile, cwd })` |
|
|
33
|
+
| 패인 분할 | `splitPane({ direction: 'H'\|'V', title, command })` |
|
|
34
|
+
| 다중 배치 | `applySplitLayout([{ title, command, direction }])` |
|
|
35
|
+
| 탭 정리 | `closeTab(title)` / `closeStale({ olderThanMs, titlePattern })` |
|
|
36
|
+
|
|
37
|
+
차단과 대안은 항상 쌍으로 존재해야 한다. 차단만 추가하고 대안을 안 만들면 데드락.
|
|
38
|
+
|
|
39
|
+
### psmux에서 Codex 실행
|
|
40
|
+
|
|
41
|
+
| 방식 | 동작 | 이유 |
|
|
42
|
+
|------|------|------|
|
|
43
|
+
| `codex` (interactive) | 불가 | psmux에서 TTY를 못 잡음 |
|
|
44
|
+
| `codex < prompt.md` | 불가 | "stdin is not a terminal" |
|
|
45
|
+
| `codex exec "$(cat prompt.md)" -s danger-full-access --dangerously-bypass-approvals-and-sandbox` | 사용 | 유일한 안전 경로 |
|
|
46
|
+
|
|
47
|
+
`codex exec`는 config.toml `approval_mode`를 무시하므로 `--dangerously-bypass-approvals-and-sandbox` 필수.
|
|
48
|
+
`-s` 유효값: read-only, workspace-write, danger-full-access.
|
|
49
|
+
</psmux-wt>
|
|
50
|
+
|
|
51
|
+
<codex-config>
|
|
52
|
+
## Codex config.toml
|
|
53
|
+
|
|
54
|
+
config.toml에 이미 설정된 값은 CLI 플래그로 중복 지정하지 않는다.
|
|
55
|
+
|
|
56
|
+
| config.toml에 있으면 | CLI에서 생략 |
|
|
57
|
+
|---------------------|-------------|
|
|
58
|
+
| `sandbox = "elevated"` | `--full-auto` |
|
|
59
|
+
| `approval_mode = "full-auto"` | `--full-auto` |
|
|
60
|
+
|
|
61
|
+
안전 패턴: config.toml에 기본값을 두고, CLI에서는 `--profile` 선택만 한다.
|
|
62
|
+
</codex-config>
|
|
63
|
+
|
|
64
|
+
<account-broker>
|
|
65
|
+
## AccountBroker (계정 브로커)
|
|
66
|
+
|
|
67
|
+
conductor, headless, swarm-hypervisor가 하나의 AccountBroker 싱글턴을 공유한다.
|
|
68
|
+
|
|
69
|
+
| 항목 | 설명 |
|
|
70
|
+
|------|------|
|
|
71
|
+
| 계정별 CircuitBreaker | 장애 격리 — 한 계정 오류가 다른 계정에 전파되지 않음 |
|
|
72
|
+
| busy 플래그 | 동일 계정 이중 임대(double-lease) 방지 |
|
|
73
|
+
| `/broker/reload` | 장시간 세션 중 accounts.json 핫리로드 |
|
|
74
|
+
| EventEmitter 이벤트 | `lease`, `release`, `cooldown`, `tierFallback`, `circuitOpen`, `circuitClose`, `noAvailableAccounts` — HUD 연동용 |
|
|
75
|
+
</account-broker>
|
|
76
|
+
|
|
77
|
+
<remote>
|
|
78
|
+
## 원격 실행
|
|
79
|
+
|
|
80
|
+
### 스킬 구분
|
|
81
|
+
|
|
82
|
+
| 스킬 | 대상 | 방식 |
|
|
83
|
+
|------|------|------|
|
|
84
|
+
| tfx-codex-swarm | 로컬 전용 | 로컬 worktree + psmux |
|
|
85
|
+
| tfx-remote-spawn | Claude Code 원격 | SSH → Claude Code 세션 → 내부 tfx 라우팅 |
|
|
86
|
+
|
|
87
|
+
codex를 SSH 너머로 직접 실행하지 않는다. config.toml 충돌 + TTY 문제.
|
|
88
|
+
원격에서 codex가 필요하면: remote-spawn → Claude Code → Claude가 내부에서 codex 호출.
|
|
89
|
+
|
|
90
|
+
### SSH 패턴
|
|
91
|
+
|
|
92
|
+
hosts.json `os` 필드로 대상 셸을 판단한다. safety-guard도 이 필드를 참조.
|
|
93
|
+
|
|
94
|
+
| 대상 OS | 셸 | 패턴 |
|
|
95
|
+
|---------|-----|------|
|
|
96
|
+
| windows | PowerShell | scp + `pwsh -File` 필수. `$var` → `$env:VAR`, `2>/dev/null` → `2>$null` |
|
|
97
|
+
| darwin | zsh | 인라인 가능. brew PATH 주의 (`/opt/homebrew/bin`) |
|
|
98
|
+
| linux | bash | 인라인 가능. 표준 POSIX |
|
|
99
|
+
|
|
100
|
+
- `~` → `$HOME` 변환은 모든 OS 공통
|
|
101
|
+
</remote>
|
|
102
|
+
|
|
103
|
+
<headless-retrieval>
|
|
104
|
+
## Headless 결과 회수
|
|
105
|
+
|
|
106
|
+
background로 실행한 headless 결과는 **반드시 task-notification 완료 후** 읽는다.
|
|
107
|
+
|
|
108
|
+
| 패턴 | 올바름 | 이유 |
|
|
109
|
+
|------|--------|------|
|
|
110
|
+
| task-notification 후 output 파일 읽기 | YES | 프로세스 종료 = 워커 전부 완료 |
|
|
111
|
+
| task-notification 전 output 파일 tail | NO | 시작 메시지만 보이고 "실패"로 오진 |
|
|
112
|
+
| psmux capture-pane으로 중간 체크 | NO | 워커 진행 중이면 빈 화면일 수 있음 |
|
|
113
|
+
|
|
114
|
+
완료 마커: `=== HEADLESS_COMPLETE succeeded=N failed=N total=N ===`
|
|
115
|
+
워커 상세: `$TMPDIR/tfx-headless/{sessionName}-worker-N.txt`
|
|
116
|
+
</headless-retrieval>
|
|
117
|
+
|
|
118
|
+
<cross-review>
|
|
119
|
+
## 교차 검증
|
|
120
|
+
|
|
121
|
+
- Claude 작성 코드 → Codex 리뷰
|
|
122
|
+
- Codex 작성 코드 → Claude 리뷰
|
|
123
|
+
- 동일 모델 self-approve 하지 않는다
|
|
124
|
+
- git commit 전 미검증 파일 감지 시 nudge
|
|
125
|
+
</cross-review>
|
|
126
|
+
|
|
127
|
+
<session-context>
|
|
128
|
+
## 맥락 이탈 판단
|
|
129
|
+
|
|
130
|
+
현재 세션 맥락과 무관한 요청이 감지되면 psmux 격리를 제안한다.
|
|
131
|
+
|
|
132
|
+
| 확신도 | 신호 | 행동 |
|
|
133
|
+
|--------|------|------|
|
|
134
|
+
| 확실 | "새 탭", "별도로", "새 세션" | 바로 psmux spawn |
|
|
135
|
+
| 높음 | 다른 프로젝트/스택 언급 | 분리 제안 |
|
|
136
|
+
| 중간 | 작업 유형 전환 | 분리 제안 + 현재 세션 옵션 |
|
|
137
|
+
| 낮음 | 현재 작업 연장 | 세션 유지 |
|
|
138
|
+
</session-context>
|
|
139
|
+
|
|
140
|
+
<routing>
|
|
141
|
+
## 자연어 → 스킬 라우팅
|
|
142
|
+
|
|
143
|
+
사용자가 스킬명을 모르더라도 자연어로 요청하면 아래 규칙에 따라 적절한 스킬을 호출한다.
|
|
144
|
+
|
|
145
|
+
### 행동 유형 → 스킬 매핑
|
|
146
|
+
|
|
147
|
+
| 의도 | 자연어 신호 | 스킬 |
|
|
148
|
+
|------|-----------|------|
|
|
149
|
+
| 구현/수정 | 만들어, 고쳐, 구현해, 짜줘, 수정해, 바꿔 | tfx-auto |
|
|
150
|
+
| 리뷰 | 봐줘, 리뷰해, 검토해, 괜찮아? | tfx-review |
|
|
151
|
+
| 분석 | 분석해, 어떻게 돌아가?, 구조가 뭐야 | tfx-analysis |
|
|
152
|
+
| 계획 | 계획, 어떻게 하지, 설계해 | tfx-plan |
|
|
153
|
+
| 검색 | 찾아, 어디있어, 파일 찾아 | tfx-find |
|
|
154
|
+
| 리서치 (빠른) | 검색해줘, 찾아봐, 공식문서, 이거 뭐야 | tfx-research |
|
|
155
|
+
| 리서치 (자율) | 자율 리서치, 검색하고 정리해, research and plan | tfx-autoresearch |
|
|
156
|
+
| 테스트 | 테스트, 검증, 돌려봐, QA | tfx-qa |
|
|
157
|
+
| 정리 | 정리해, 슬롭 제거, 클린업 | tfx-prune |
|
|
158
|
+
| 토론 | 뭐가 나을까, 비교해, A vs B | tfx-debate |
|
|
159
|
+
|
|
160
|
+
### 깊이 수정자
|
|
161
|
+
|
|
162
|
+
| 수정자 | 신호 | 효과 |
|
|
163
|
+
|--------|------|------|
|
|
164
|
+
| 기본 | (없음), 빠르게, 간단히 | Light 스킬 |
|
|
165
|
+
| 깊이 | 제대로, 꼼꼼히, 철저히 | Deep 스킬 (tfx-deep-*). 예외: tfx-deep-interview는 Gemini 단독 |
|
|
166
|
+
| 합의 | 3자, 교차, 다각도 | consensus 프로토콜 |
|
|
167
|
+
| 반복 | 끝까지, 멈추지마, ralph | persist 모드 |
|
|
168
|
+
| 자율 | 알아서, 자동으로, autopilot | autopilot 모드 |
|
|
169
|
+
|
|
170
|
+
### CLI 라우팅
|
|
171
|
+
|
|
172
|
+
headless-guard가 `codex exec` / `gemini -y -p` 직접 호출을 차단한다. tfx 스킬 경유 필수.
|
|
173
|
+
|
|
174
|
+
**Layer 1 — Light** (tfx-route.sh → 단일 CLI)
|
|
175
|
+
|
|
176
|
+
| 스킬 | CLI | 용도 |
|
|
177
|
+
|------|-----|------|
|
|
178
|
+
| tfx-auto | 자동 | 통합 진입점 |
|
|
179
|
+
| tfx-codex | Codex | Codex 전용 |
|
|
180
|
+
| tfx-gemini | Gemini | Gemini 전용 |
|
|
181
|
+
| tfx-autopilot | Codex→검증 | 단일 파일, 5분 이내 |
|
|
182
|
+
| tfx-autoroute | 자동 승격 | 실패→더 강한 모델 |
|
|
183
|
+
|
|
184
|
+
**Layer 2 — Deep** (headless 3-CLI 합의)
|
|
185
|
+
|
|
186
|
+
tfx-deep-review, tfx-deep-qa, tfx-deep-plan, tfx-deep-research, tfx-consensus, tfx-debate, tfx-panel, tfx-fullcycle, tfx-persist
|
|
187
|
+
|
|
188
|
+
**Layer 3 — Remote/병렬**
|
|
189
|
+
|
|
190
|
+
| 스킬 | 용도 |
|
|
191
|
+
|------|------|
|
|
192
|
+
| tfx-multi | 2+개 태스크 headless 병렬 |
|
|
193
|
+
| tfx-swarm | PRD별 worktree + 다중 모델(Codex/Gemini/Claude) + 다중 기기(로컬+원격) |
|
|
194
|
+
| tfx-remote-spawn | Claude Code 원격 세션 (SSH, setup 필수) |
|
|
195
|
+
|
|
196
|
+
**Claude 네이티브** (CLI 불필요): tfx-find, tfx-forge, tfx-prune, tfx-index, tfx-setup, tfx-doctor, tfx-hooks, tfx-hub
|
|
197
|
+
|
|
198
|
+
자원 우선순위: remote-spawn > swarm > multi > Light > 로컬 단독
|
|
199
|
+
|
|
200
|
+
### 충돌 해소
|
|
201
|
+
|
|
202
|
+
- ralph = persist alias
|
|
203
|
+
- "auto" 단독 → tfx-auto. "알아서 해" → tfx-autopilot
|
|
204
|
+
- "코드에서 찾아" → tfx-find. "알아봐" → tfx-research
|
|
205
|
+
- 복합 의도: "구현하고 리뷰까지" → tfx-auto → cross-review hook
|
|
206
|
+
|
|
207
|
+
### Q-Learning 동적 라우팅 (실험적)
|
|
208
|
+
|
|
209
|
+
- `TRIFLUX_DYNAMIC_ROUTING=true` 또는 `1` 설정 시 Q-Learning 기반 동적 스킬 라우팅 활성화
|
|
210
|
+
- `routing-weights.json` + Q-table로 스킬 선택 최적화
|
|
211
|
+
- 기본 비활성
|
|
212
|
+
</routing>
|
package/hub/account-broker.mjs
CHANGED
|
@@ -45,6 +45,11 @@ const ConfigSchema = z.object({
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
const DEFAULT_COOLDOWN_MS = 300_000; // 5 minutes
|
|
48
|
+
const QUOTA_COOLDOWN_MS = {
|
|
49
|
+
codex: 5 * 60 * 60_000, // 5 hours (단기 쿼터)
|
|
50
|
+
codex_weekly: 7 * 24 * 60 * 60_000, // 7 days (주간 쿼터)
|
|
51
|
+
gemini: 24 * 60 * 60_000, // 24 hours
|
|
52
|
+
};
|
|
48
53
|
const TIER_PRIORITY = { pro: 0, plus: 1, unknown: 2, free: 3 };
|
|
49
54
|
const LEASE_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
50
55
|
const CIRCUIT_WINDOW_MS = 10 * 60_000; // 10 minutes
|
package/hub/cli-adapter-base.mjs
CHANGED
|
@@ -6,6 +6,46 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
6
6
|
|
|
7
7
|
import { IS_WINDOWS, killProcess } from "./platform.mjs";
|
|
8
8
|
|
|
9
|
+
// ── Quota retry-after 파싱 ──────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const FALLBACK_COOLDOWN_MS = { codex: 5 * 3600_000, gemini: 86400_000 };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 에러 텍스트에서 retry-after 시간을 동적 파싱.
|
|
15
|
+
* "try again at 2026-04-11T10:00:00Z", "retry after 3600 seconds",
|
|
16
|
+
* "resets in 7 days", "wait 5 hours" 등을 감지.
|
|
17
|
+
*/
|
|
18
|
+
export function parseRetryAfterMs(text, provider) {
|
|
19
|
+
// 1. ISO timestamp: "try again at 2026-04-11T10:00:00"
|
|
20
|
+
const isoMatch = text.match(/(?:try again|retry|available|resets?)\s+(?:at|after)\s+(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)/i);
|
|
21
|
+
if (isoMatch) {
|
|
22
|
+
const target = new Date(isoMatch[1]).getTime();
|
|
23
|
+
if (target > Date.now()) return target - Date.now();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. Duration: "N seconds/minutes/hours/days"
|
|
27
|
+
const durMatch = text.match(/(?:retry|wait|resets?|again)\s+(?:in\s+|after\s+)?(\d+)\s*(second|minute|hour|day|week)/i);
|
|
28
|
+
if (durMatch) {
|
|
29
|
+
const n = Number(durMatch[1]);
|
|
30
|
+
const unit = durMatch[2].toLowerCase();
|
|
31
|
+
const multipliers = { second: 1000, minute: 60_000, hour: 3600_000, day: 86400_000, week: 604800_000 };
|
|
32
|
+
return n * (multipliers[unit] || 3600_000);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3. "7 days" / "24 hours" 단독 (context 없이)
|
|
36
|
+
const standaloneMatch = text.match(/(\d+)\s*(day|hour|week|minute)/i);
|
|
37
|
+
if (standaloneMatch) {
|
|
38
|
+
const n = Number(standaloneMatch[1]);
|
|
39
|
+
const unit = standaloneMatch[2].toLowerCase();
|
|
40
|
+
const multipliers = { minute: 60_000, hour: 3600_000, day: 86400_000, week: 604800_000 };
|
|
41
|
+
const parsed = n * (multipliers[unit] || 3600_000);
|
|
42
|
+
if (parsed >= 3600_000) return parsed; // 1시간 이상만 신뢰
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 4. fallback: provider 기본값
|
|
46
|
+
return FALLBACK_COOLDOWN_MS[provider] || 5 * 3600_000;
|
|
47
|
+
}
|
|
48
|
+
|
|
9
49
|
// ── Codex CLI compatibility ─────────────────────────────────────
|
|
10
50
|
|
|
11
51
|
let _cachedVersion = null;
|
|
@@ -237,7 +277,14 @@ export async function executeWithCircuitBroker({
|
|
|
237
277
|
return lastResult;
|
|
238
278
|
}
|
|
239
279
|
|
|
240
|
-
|
|
280
|
+
if (lastResult.failureMode === "rate_limited") {
|
|
281
|
+
const text = `${lastResult.output || ""}\n${lastResult.stderr || ""}`;
|
|
282
|
+
const coolMs = parseRetryAfterMs(text, provider);
|
|
283
|
+
brokerMod.broker.markRateLimited(lease.id, coolMs);
|
|
284
|
+
brokerMod.broker.emit("cooldown", { id: lease.id, provider, coolMs, reason: "quota_exhausted" });
|
|
285
|
+
} else {
|
|
286
|
+
brokerMod.broker.release(lease.id, { ok: false });
|
|
287
|
+
}
|
|
241
288
|
return {
|
|
242
289
|
...lastResult,
|
|
243
290
|
retried: attempts.length > 1,
|
package/hub/pipe.mjs
CHANGED
|
@@ -670,8 +670,12 @@ export function createPipeServer({
|
|
|
670
670
|
const line = client.buffer.slice(0, newlineIndex).trim();
|
|
671
671
|
client.buffer = client.buffer.slice(newlineIndex + 1);
|
|
672
672
|
if (line) {
|
|
673
|
-
|
|
674
|
-
|
|
673
|
+
try {
|
|
674
|
+
const frame = safeJsonParse(line);
|
|
675
|
+
await handleFrame(client, frame);
|
|
676
|
+
} catch (err) {
|
|
677
|
+
pipeLog.error({ clientId: client.id, err: String(err?.message || err) }, "pipe.frame_handler_error");
|
|
678
|
+
}
|
|
675
679
|
}
|
|
676
680
|
newlineIndex = client.buffer.indexOf("\n");
|
|
677
681
|
}
|
package/hub/server.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
ListToolsRequestSchema,
|
|
22
22
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
23
23
|
import { createModuleLogger } from "../scripts/lib/logger.mjs";
|
|
24
|
-
import { reloadBroker } from "./account-broker.mjs";
|
|
24
|
+
import { broker as brokerInstance, reloadBroker } from "./account-broker.mjs";
|
|
25
25
|
import { createAdaptiveEngine } from "./adaptive.mjs";
|
|
26
26
|
import { createAssignCallbackServer } from "./assign-callbacks.mjs";
|
|
27
27
|
import { DelegatorService } from "./delegator/index.mjs";
|
|
@@ -708,6 +708,17 @@ export async function startHub({
|
|
|
708
708
|
return writeJson(res, 200, getQosStatsPayload());
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
+
if (path === "/broker/snapshot" && req.method === "GET") {
|
|
712
|
+
const snap = brokerInstance?.snapshot() || [];
|
|
713
|
+
return writeJson(res, 200, { ok: true, accounts: snap, ts: Date.now() });
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (path === "/broker/dashboard" && req.method === "GET") {
|
|
717
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
718
|
+
res.end(renderBrokerDashboard());
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
|
|
711
722
|
if (path === "/broker/reload" && req.method === "POST") {
|
|
712
723
|
const result = reloadBroker();
|
|
713
724
|
if (!result.ok) {
|
|
@@ -1499,8 +1510,8 @@ export async function startHub({
|
|
|
1499
1510
|
httpServer.closeAllConnections();
|
|
1500
1511
|
await new Promise((resolveClose) => httpServer.close(resolveClose));
|
|
1501
1512
|
})().catch((error) => {
|
|
1502
|
-
|
|
1503
|
-
|
|
1513
|
+
hubLog.error({ err: String(error?.message || error) }, "hub.stop_error");
|
|
1514
|
+
// stopPromise를 null로 리셋하지 않음 — double-close 방지
|
|
1504
1515
|
});
|
|
1505
1516
|
|
|
1506
1517
|
return stopPromise;
|
|
@@ -1656,6 +1667,71 @@ function cleanupStaleSpawnSessions(log) {
|
|
|
1656
1667
|
return killed;
|
|
1657
1668
|
}
|
|
1658
1669
|
|
|
1670
|
+
// ── Broker Dashboard HTML ──────────────────────────────────────
|
|
1671
|
+
|
|
1672
|
+
function renderBrokerDashboard() {
|
|
1673
|
+
return `<!DOCTYPE html><html lang="ko"><head><meta charset="utf-8">
|
|
1674
|
+
<title>Account Broker Dashboard</title>
|
|
1675
|
+
<meta http-equiv="refresh" content="30">
|
|
1676
|
+
<style>
|
|
1677
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
1678
|
+
body{background:#0a0a0b;color:#e8e8ec;font:14px/1.6 'SF Mono',Consolas,monospace;padding:24px}
|
|
1679
|
+
h1{font-size:18px;color:#a8b1ff;margin-bottom:16px}
|
|
1680
|
+
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:12px}
|
|
1681
|
+
.card{background:#141416;border:1px solid rgba(255,255,255,0.06);border-radius:8px;padding:16px}
|
|
1682
|
+
.card.available{border-left:3px solid #34d399}
|
|
1683
|
+
.card.busy{border-left:3px solid #fbbf24}
|
|
1684
|
+
.card.cooldown{border-left:3px solid #f87171}
|
|
1685
|
+
.card.circuit-open{border-left:3px solid #ef4444;opacity:0.7}
|
|
1686
|
+
.provider{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:#888}
|
|
1687
|
+
.id{font-size:13px;color:#c8c8d0;margin:4px 0}
|
|
1688
|
+
.status{font-size:12px;font-weight:600;padding:2px 8px;border-radius:4px;display:inline-block}
|
|
1689
|
+
.status.available{background:#064e3b;color:#34d399}
|
|
1690
|
+
.status.busy{background:#451a03;color:#fbbf24}
|
|
1691
|
+
.status.cooldown{background:#450a0a;color:#f87171}
|
|
1692
|
+
.status.circuit-open{background:#450a0a;color:#ef4444}
|
|
1693
|
+
.meta{margin-top:8px;font-size:11px;color:#666;line-height:1.8}
|
|
1694
|
+
.meta b{color:#999}
|
|
1695
|
+
.timer{color:#f87171;font-weight:600}
|
|
1696
|
+
.refresh{color:#555;font-size:11px;margin-top:16px}
|
|
1697
|
+
</style></head><body>
|
|
1698
|
+
<h1>🔑 Account Broker Dashboard</h1>
|
|
1699
|
+
<div id="grid" class="grid"></div>
|
|
1700
|
+
<p class="refresh">auto-refresh: 10s | <a href="/broker/snapshot" style="color:#666">JSON API</a></p>
|
|
1701
|
+
<script>
|
|
1702
|
+
function fmt(ms){if(ms<=0)return'-';const s=Math.floor(ms/1000),m=Math.floor(s/60),h=Math.floor(m/60),d=Math.floor(h/24);if(d>0)return d+'d '+h%24+'h';if(h>0)return h+'h '+m%60+'m';if(m>0)return m+'m '+s%60+'s';return s+'s'}
|
|
1703
|
+
async function refresh(){
|
|
1704
|
+
try{
|
|
1705
|
+
const r=await fetch('/broker/snapshot');
|
|
1706
|
+
const d=await r.json();
|
|
1707
|
+
if(!d.ok)return;
|
|
1708
|
+
const now=d.ts;
|
|
1709
|
+
const grid=document.getElementById('grid');
|
|
1710
|
+
grid.innerHTML=d.accounts.map(a=>{
|
|
1711
|
+
const cd=a.cooldownUntil>now?a.cooldownUntil-now:0;
|
|
1712
|
+
const rm=a.remainingMs||0;
|
|
1713
|
+
let st='available',sl='Available';
|
|
1714
|
+
if(a.circuitState==='open'){st='circuit-open';sl='Circuit Open'}
|
|
1715
|
+
else if(cd>0){st='cooldown';sl='Cooldown'}
|
|
1716
|
+
else if(a.busy){st='busy';sl='Busy ('+fmt(rm)+')'}
|
|
1717
|
+
return '<div class="card '+st+'">'+
|
|
1718
|
+
'<div class="provider">'+a.provider+' / '+(a.tier||'unknown')+'</div>'+
|
|
1719
|
+
'<div class="id">'+a.id+'</div>'+
|
|
1720
|
+
'<span class="status '+st+'">'+sl+'</span>'+
|
|
1721
|
+
(cd>0?'<span class="timer" style="margin-left:8px">'+fmt(cd)+' remaining</span>':'')+
|
|
1722
|
+
'<div class="meta">'+
|
|
1723
|
+
'<b>Sessions:</b> '+a.totalSessions+
|
|
1724
|
+
(a.lastUsedAt?' | <b>Last:</b> '+new Date(a.lastUsedAt).toLocaleTimeString():'')+
|
|
1725
|
+
(a.failureTimestamps?.length?' | <b>Failures:</b> '+a.failureTimestamps.length:'')+
|
|
1726
|
+
(a.circuitState!=='closed'?' | <b>Circuit:</b> '+a.circuitState:'')+
|
|
1727
|
+
'</div></div>'
|
|
1728
|
+
}).join('');
|
|
1729
|
+
}catch(e){console.error('refresh failed',e)}
|
|
1730
|
+
}
|
|
1731
|
+
refresh();setInterval(refresh,10000);
|
|
1732
|
+
</script></body></html>`;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1659
1735
|
const selfRun = process.argv[1]?.replace(/\\/g, "/").endsWith("hub/server.mjs");
|
|
1660
1736
|
if (selfRun) {
|
|
1661
1737
|
const port = parseInt(process.env.TFX_HUB_PORT || "27888", 10);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "triflux",
|
|
3
|
-
"version": "10.9.
|
|
3
|
+
"version": "10.9.7",
|
|
4
4
|
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,69 +13,26 @@
|
|
|
13
13
|
"tfx-doctor-tui": "bin/tfx-doctor-tui.mjs",
|
|
14
14
|
"tfx-setup-tui": "bin/tfx-setup-tui.mjs"
|
|
15
15
|
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18.0.0"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@triflux/core": "10.0.1",
|
|
21
|
+
"@triflux/remote": "^10.0.0-alpha.1"
|
|
22
|
+
},
|
|
16
23
|
"files": [
|
|
17
24
|
"bin",
|
|
18
|
-
"tui",
|
|
19
|
-
"hub",
|
|
20
|
-
"config",
|
|
21
25
|
"skills",
|
|
22
|
-
"!skills/tfx-workspace",
|
|
23
|
-
"!**/failure-reports",
|
|
24
|
-
"scripts",
|
|
25
26
|
"hooks",
|
|
26
27
|
"hud",
|
|
28
|
+
"scripts",
|
|
29
|
+
"hub",
|
|
27
30
|
"mesh",
|
|
28
|
-
"
|
|
31
|
+
"references",
|
|
32
|
+
"CLAUDE.md",
|
|
29
33
|
"README.md",
|
|
30
|
-
"README.ko.md",
|
|
31
34
|
"LICENSE"
|
|
32
35
|
],
|
|
33
|
-
"workspaces": [
|
|
34
|
-
"packages/core",
|
|
35
|
-
"packages/remote",
|
|
36
|
-
"packages/triflux"
|
|
37
|
-
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"pack": "node scripts/pack.mjs all",
|
|
40
|
-
"pack:core": "node scripts/pack.mjs core",
|
|
41
|
-
"pack:remote": "node scripts/pack.mjs remote",
|
|
42
|
-
"setup": "node scripts/setup.mjs",
|
|
43
|
-
"preinstall": "node scripts/preinstall.mjs",
|
|
44
|
-
"postinstall": "node scripts/setup.mjs",
|
|
45
|
-
"lint": "biome check .",
|
|
46
|
-
"lint:fix": "biome check --fix .",
|
|
47
|
-
"health": "npm test && npm run lint",
|
|
48
|
-
"test": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
49
|
-
"test:unit": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/unit/**/*.test.mjs",
|
|
50
|
-
"test:integration": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/integration/**/*.test.mjs",
|
|
51
|
-
"test:route-smoke": "node scripts/test-lock.mjs --test scripts/test-tfx-route-no-claude-native.mjs",
|
|
52
|
-
"test:contract": "node scripts/test-lock.mjs --test --test-force-exit --test-concurrency=8 tests/contract/**/*.test.mjs",
|
|
53
|
-
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=60 --test --test-force-exit --test-concurrency=8 \"tests/**/*.test.mjs\"",
|
|
54
|
-
"gen:skill-docs": "node scripts/gen-skill-docs.mjs",
|
|
55
|
-
"gen:skill-manifest": "node scripts/gen-skill-manifest.mjs"
|
|
56
|
-
},
|
|
57
|
-
"engines": {
|
|
58
|
-
"node": ">=18.0.0"
|
|
59
|
-
},
|
|
60
|
-
"repository": {
|
|
61
|
-
"type": "git",
|
|
62
|
-
"url": "git+https://github.com/tellang/triflux.git"
|
|
63
|
-
},
|
|
64
|
-
"homepage": "https://github.com/tellang/triflux#readme",
|
|
65
|
-
"author": "tellang",
|
|
66
|
-
"license": "MIT",
|
|
67
|
-
"dependencies": {
|
|
68
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
69
|
-
"better-sqlite3": "^12.6.2",
|
|
70
|
-
"pino": "^10.3.1",
|
|
71
|
-
"pino-pretty": "^13.1.3",
|
|
72
|
-
"systray2": "^2.1.4",
|
|
73
|
-
"zod": "^4.0.0"
|
|
74
|
-
},
|
|
75
|
-
"devDependencies": {
|
|
76
|
-
"@biomejs/biome": "^2.0.0",
|
|
77
|
-
"knip": "^6.3.0"
|
|
78
|
-
},
|
|
79
36
|
"keywords": [
|
|
80
37
|
"claude-code",
|
|
81
38
|
"plugin",
|
|
@@ -86,5 +43,13 @@
|
|
|
86
43
|
"multi-model",
|
|
87
44
|
"triflux",
|
|
88
45
|
"tfx"
|
|
89
|
-
]
|
|
46
|
+
],
|
|
47
|
+
"author": "tellang",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"homepage": "https://github.com/tellang/triflux#readme",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/tellang/triflux.git",
|
|
53
|
+
"directory": "packages/triflux"
|
|
54
|
+
}
|
|
90
55
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hosts": {
|
|
3
|
+
"ultra4": {
|
|
4
|
+
"description": "Windows 데스크탑 (22코어/64GB RAM)",
|
|
5
|
+
"aliases": ["울트라", "울트라4", "데스크탑"],
|
|
6
|
+
"default_dir": "~/Desktop/Projects/cli/triflux",
|
|
7
|
+
"tailscale": {
|
|
8
|
+
"ip": "100.110.136.64",
|
|
9
|
+
"dns": "ultra-book-4-1",
|
|
10
|
+
"ssh_mode": "ssh-over-vpn"
|
|
11
|
+
},
|
|
12
|
+
"ssh_user": "SSAFY",
|
|
13
|
+
"os": "windows",
|
|
14
|
+
"specs": {
|
|
15
|
+
"cores": 22,
|
|
16
|
+
"ram_gb": 64,
|
|
17
|
+
"codex": "0.118.0",
|
|
18
|
+
"node": "24.14.0"
|
|
19
|
+
},
|
|
20
|
+
"capabilities": ["codex", "claude", "high-memory"]
|
|
21
|
+
},
|
|
22
|
+
"m2": {
|
|
23
|
+
"description": "MacBook Air M2 (8코어/16GB RAM)",
|
|
24
|
+
"aliases": ["맥", "맥북", "m2"],
|
|
25
|
+
"default_dir": "~/Desktop/Projects/triflux",
|
|
26
|
+
"tailscale": {
|
|
27
|
+
"ip": "100.104.61.126",
|
|
28
|
+
"dns": "tellang의-macbook-air",
|
|
29
|
+
"ssh_mode": "ssh-over-vpn"
|
|
30
|
+
},
|
|
31
|
+
"ssh_user": "tellang",
|
|
32
|
+
"os": "darwin",
|
|
33
|
+
"specs": {
|
|
34
|
+
"cores": 8,
|
|
35
|
+
"ram_gb": 16,
|
|
36
|
+
"claude": "2.1.89",
|
|
37
|
+
"codex": "0.114.0",
|
|
38
|
+
"gemini": "0.33.1",
|
|
39
|
+
"node": "25.9.0"
|
|
40
|
+
},
|
|
41
|
+
"capabilities": ["codex", "claude", "gemini"]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"default_host": "m2",
|
|
45
|
+
"triggers": ["원격에서", "다른 머신에서", "다른 컴퓨터에서"]
|
|
46
|
+
}
|
|
@@ -41,6 +41,16 @@ const MULTI_EXPIRE_MS = 30 * 60 * 1000; // 30분 자동 만료
|
|
|
41
41
|
const GATE_THRESHOLD = 2; // A: dispatch 전 허용할 Agent 호출 수
|
|
42
42
|
const NUDGE_THRESHOLD = 4; // B: dispatch 후 nudge 트리거 횟수
|
|
43
43
|
|
|
44
|
+
function isOwnerAlive(pid) {
|
|
45
|
+
if (!pid || typeof pid !== "number") return false;
|
|
46
|
+
try {
|
|
47
|
+
process.kill(pid, 0);
|
|
48
|
+
return true;
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
function readMultiState() {
|
|
45
55
|
try {
|
|
46
56
|
if (!existsSync(MULTI_STATE_FILE)) return null;
|
|
@@ -55,6 +65,15 @@ function readMultiState() {
|
|
|
55
65
|
}
|
|
56
66
|
return null;
|
|
57
67
|
}
|
|
68
|
+
// ownerPid 생존 체크 — 소유 세션이 죽었으면 stale (#62)
|
|
69
|
+
if (state.ownerPid && !isOwnerAlive(state.ownerPid)) {
|
|
70
|
+
try {
|
|
71
|
+
unlinkSync(MULTI_STATE_FILE);
|
|
72
|
+
} catch {
|
|
73
|
+
/* ignore */
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
58
77
|
return state;
|
|
59
78
|
} catch {
|
|
60
79
|
return null;
|
|
@@ -115,9 +115,15 @@ function cleanupOrphanPidFiles() {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function main() {
|
|
118
|
+
export function main() {
|
|
119
119
|
cleanupMultiState();
|
|
120
120
|
cleanupOrphanPidFiles();
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
const isDirectRun =
|
|
124
|
+
process.argv[1] &&
|
|
125
|
+
import.meta.url.endsWith(
|
|
126
|
+
process.argv[1].replace(/\\/g, "/").split("/").pop(),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
if (isDirectRun) main();
|
package/scripts/setup.mjs
CHANGED
|
@@ -159,6 +159,16 @@ const SYNC_MAP = [
|
|
|
159
159
|
dst: join(CLAUDE_DIR, "scripts", "hub", "workers", "factory.mjs"),
|
|
160
160
|
label: "hub/workers/factory.mjs",
|
|
161
161
|
},
|
|
162
|
+
{
|
|
163
|
+
src: join(PLUGIN_ROOT, "hub", "cli-adapter-base.mjs"),
|
|
164
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "cli-adapter-base.mjs"),
|
|
165
|
+
label: "hub/cli-adapter-base.mjs",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
src: join(PLUGIN_ROOT, "hub", "platform.mjs"),
|
|
169
|
+
dst: join(CLAUDE_DIR, "scripts", "hub", "platform.mjs"),
|
|
170
|
+
label: "hub/platform.mjs",
|
|
171
|
+
},
|
|
162
172
|
...scanHudFiles(PLUGIN_ROOT, CLAUDE_DIR),
|
|
163
173
|
{
|
|
164
174
|
src: join(PLUGIN_ROOT, "scripts", "notion-read.mjs"),
|
|
@@ -51,7 +51,6 @@ if (!createWorker) {
|
|
|
51
51
|
function parseArgs(argv) {
|
|
52
52
|
const args = {
|
|
53
53
|
allowedMcpServerNames: [],
|
|
54
|
-
extraArgs: [],
|
|
55
54
|
mcpConfig: [],
|
|
56
55
|
};
|
|
57
56
|
|
|
@@ -95,10 +94,6 @@ function parseArgs(argv) {
|
|
|
95
94
|
args.allowedMcpServerNames.push(next);
|
|
96
95
|
index += 1;
|
|
97
96
|
break;
|
|
98
|
-
case "--extra-arg":
|
|
99
|
-
args.extraArgs.push(next);
|
|
100
|
-
index += 1;
|
|
101
|
-
break;
|
|
102
97
|
case "--mcp-config":
|
|
103
98
|
args.mcpConfig.push(next);
|
|
104
99
|
index += 1;
|
|
@@ -207,7 +202,6 @@ const worker = createWorker(args.type, {
|
|
|
207
202
|
permissionMode: args.permissionMode,
|
|
208
203
|
allowDangerouslySkipPermissions: args.allowDangerouslySkipPermissions,
|
|
209
204
|
allowedMcpServerNames: args.allowedMcpServerNames,
|
|
210
|
-
extraArgs: args.extraArgs,
|
|
211
205
|
mcpConfig:
|
|
212
206
|
args.type === "claude" && args.mcpConfig.length === 0
|
|
213
207
|
? resolveDefaultMcpConfig(args.cwd || process.cwd())
|