triflux 9.8.0 → 9.8.2

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/bin/triflux.mjs CHANGED
@@ -2363,13 +2363,22 @@ async function cmdDoctor(options = {}) {
2363
2363
  e.hooks.some((h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator")),
2364
2364
  );
2365
2365
  if (!hasOrch) continue;
2366
- // orchestrator 아닌 엔트리 제거
2366
+ // 패턴 A: orchestrator 없는 별도 엔트리 제거
2367
2367
  const before = entries.length;
2368
2368
  fixedSettings.hooks[event] = entries.filter((e) =>
2369
2369
  Array.isArray(e?.hooks) &&
2370
2370
  e.hooks.some((h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator")),
2371
2371
  );
2372
2372
  removed += before - fixedSettings.hooks[event].length;
2373
+ // 패턴 B: orchestrator 엔트리 내부의 개별 훅 제거
2374
+ for (const entry of fixedSettings.hooks[event]) {
2375
+ if (!Array.isArray(entry.hooks) || entry.hooks.length <= 1) continue;
2376
+ const beforeInner = entry.hooks.length;
2377
+ entry.hooks = entry.hooks.filter(
2378
+ (h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator"),
2379
+ );
2380
+ removed += beforeInner - entry.hooks.length;
2381
+ }
2373
2382
  }
2374
2383
  if (removed > 0) {
2375
2384
  writeFileSync(settingsPath, JSON.stringify(fixedSettings, null, 2) + "\n", "utf8");
@@ -195,6 +195,9 @@ function ensureCaptureHelper() {
195
195
  " New-Item -ItemType Directory -Force -Path $parent | Out-Null",
196
196
  "}",
197
197
  "",
198
+ "# Force UTF-8 encoding — CP949 등 non-UTF-8 codepage에서 Gemini stdout 캡처 실패 방지",
199
+ "[Console]::InputEncoding = [System.Text.Encoding]::UTF8",
200
+ "",
198
201
  "$reader = [Console]::In",
199
202
  "while (($line = $reader.ReadLine()) -ne $null) {",
200
203
  " Add-Content -LiteralPath $Path -Value $line -Encoding utf8",
@@ -877,7 +880,9 @@ export function dispatchCommand(sessionName, paneNameOrTarget, commandText) {
877
880
 
878
881
  const token = randomToken(paneName);
879
882
  const safeCommand = wrapCliForBash(commandText);
880
- const wrapped = `try { ${safeCommand} } finally { $trifluxExit = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } else { 0 }; Write-Output "${COMPLETION_PREFIX}${token}:$trifluxExit" }`;
883
+ // CP949 non-UTF-8 codepage 환경에서 CLI stdout이 깨지는 문제 방지 (belt-and-suspenders)
884
+ const chcpPrefix = process.platform === "win32" ? "chcp 65001 > $null; " : "";
885
+ const wrapped = `${chcpPrefix}try { ${safeCommand} } finally { $trifluxExit = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } else { 0 }; Write-Output "${COMPLETION_PREFIX}${token}:$trifluxExit" }`;
881
886
 
882
887
  sendLiteralToPane(pane.paneId, wrapped, true);
883
888
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "9.8.0",
3
+ "version": "9.8.2",
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": {
@@ -189,8 +189,11 @@ async function main() {
189
189
  process.exit(0);
190
190
  }
191
191
 
192
- // codex/gemini 직접 CLI 호출 → deny
192
+ // codex/gemini 직접 CLI 호출 → deny (인라인 TFX_ALLOW_DIRECT_CLI=1 우회 허용)
193
193
  if (/\bcodex\b.*\bexec\b/.test(cmd) || /\bgemini\s+(-p|--prompt)\b/.test(cmd)) {
194
+ if (/\bTFX_ALLOW_DIRECT_CLI=1\b/.test(cmd)) {
195
+ nudge("[headless-guard] direct CLI mode (inline TFX_ALLOW_DIRECT_CLI=1)");
196
+ }
194
197
  deny(
195
198
  "[headless-guard] codex/gemini 직접 호출은 spawn-session에서 차단됩니다. " +
196
199
  `승인된 경로: ${HEADLESS_FALLBACK_COMMAND}. ` +
package/scripts/setup.mjs CHANGED
@@ -630,7 +630,10 @@ function ensureHooksInSettings({
630
630
  }
631
631
  if (!settings.hooks || typeof settings.hooks !== "object") settings.hooks = {};
632
632
 
633
- // ── 이중 실행 정리: orchestrator가 있는 이벤트에서 개별 훅 엔트리 제거 ──
633
+ // ── 이중 실행 정리: orchestrator가 있는 이벤트에서 개별 훅 제거 ──
634
+ // 두 가지 패턴 모두 처리:
635
+ // A) orchestrator 엔트리와 별도 개별 훅 엔트리가 공존 → 개별 엔트리 제거
636
+ // B) orchestrator와 개별 훅이 같은 엔트리의 hooks[] 안에 공존 → 개별 훅 제거
634
637
  let dedupRemoved = 0;
635
638
  for (const [event, entries] of Object.entries(settings.hooks)) {
636
639
  if (!Array.isArray(entries)) continue;
@@ -639,12 +642,24 @@ function ensureHooksInSettings({
639
642
  e.hooks.some((h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator")),
640
643
  );
641
644
  if (!hasOrch) continue;
645
+
646
+ // 패턴 A: orchestrator 없는 별도 엔트리 제거
642
647
  const before = entries.length;
643
648
  settings.hooks[event] = entries.filter((e) =>
644
649
  Array.isArray(e?.hooks) &&
645
650
  e.hooks.some((h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator")),
646
651
  );
647
652
  dedupRemoved += before - settings.hooks[event].length;
653
+
654
+ // 패턴 B: orchestrator가 있는 엔트리 내부에서 개별 훅 제거
655
+ for (const entry of settings.hooks[event]) {
656
+ if (!Array.isArray(entry.hooks) || entry.hooks.length <= 1) continue;
657
+ const beforeInner = entry.hooks.length;
658
+ entry.hooks = entry.hooks.filter(
659
+ (h) => typeof h?.command === "string" && h.command.includes("hook-orchestrator"),
660
+ );
661
+ dedupRemoved += beforeInner - entry.hooks.length;
662
+ }
648
663
  }
649
664
 
650
665
  const added = [];