triflux 10.9.28 → 10.9.29

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.
@@ -77,7 +77,7 @@ function persistState(stateMap) {
77
77
  }
78
78
  mkdirSync(AUTH_BASE_PATH, { recursive: true });
79
79
  writeFileSync(STATE_PERSIST_PATH, JSON.stringify({ ts: now, entries }));
80
- } catch { /* best-effort */ }
80
+ } catch (err) { try { console.error("[account-broker] persistState failed:", err.message); } catch {} }
81
81
  }
82
82
 
83
83
  function loadPersistedState() {
package/hub/server.mjs CHANGED
@@ -80,6 +80,7 @@ const AIMD_WINDOW_MS = 30 * 60 * 1000;
80
80
  const AIMD_INITIAL_BATCH_SIZE = 3;
81
81
  const AIMD_MIN_BATCH_SIZE = 1;
82
82
  const AIMD_MAX_BATCH_SIZE = 10;
83
+ const SYNAPSE_VALID_OPS = new Set(["checkout", "rebase", "cherry-pick", "reset", "stash-pop", "worktree-remove"]);
83
84
  const HUB_IDLE_TIMEOUT_DEFAULT_MS = 0; // 0 = 영구 실행 (idle shutdown 비활성). TFX_HUB_IDLE_TIMEOUT_MS 환경변수로 오버라이드 가능
84
85
  const HUB_IDLE_SWEEP_DEFAULT_MS = 60 * 1000;
85
86
  const STATIC_CONTENT_TYPES = Object.freeze({
@@ -839,14 +840,13 @@ export async function startHub({
839
840
  }
840
841
 
841
842
  if (path === "/synapse/preflight" && req.method === "POST") {
842
- const VALID_OPS = new Set(["checkout", "rebase", "cherry-pick", "reset", "stash-pop", "worktree-remove"]);
843
843
  try {
844
844
  const body = await parseBody(req);
845
845
  const { op, args = {}, sessionContext = {} } = body;
846
846
  if (!op || typeof op !== "string") {
847
847
  return writeJson(res, 400, { ok: false, error: "op 필수" });
848
848
  }
849
- if (!VALID_OPS.has(op)) {
849
+ if (!SYNAPSE_VALID_OPS.has(op)) {
850
850
  return writeJson(res, 400, { ok: false, error: `invalid op: ${op}` });
851
851
  }
852
852
  const result = gitPreflight.check(op, args, sessionContext);
@@ -1814,6 +1814,7 @@ const QUOTA_CACHE_PATH = join(CACHE_DIR, "broker-quota-cache.json");
1814
1814
  async function checkSingleAccountQuota(acct) {
1815
1815
  try {
1816
1816
  const authPath = join(PID_DIR, acct.authFile);
1817
+ if (!authPath.startsWith(PID_DIR + sep)) return { id: acct.id, status: "path_blocked" };
1817
1818
  if (!existsSync(authPath)) return { id: acct.id, status: "no_auth" };
1818
1819
  const auth = JSON.parse(readFileSync(authPath, "utf8"));
1819
1820
  if (acct.provider === "codex") {
@@ -152,7 +152,7 @@ export function createSynapseRegistry(opts = {}) {
152
152
  const staled = { ...current, status: "stale" };
153
153
  sessions.set(sessionId, staled);
154
154
  schedulePersist();
155
- setImmediate(() => notifyStale(staled));
155
+ setImmediate(() => { if (!destroyed) notifyStale(staled); });
156
156
  }
157
157
  }, intervalFor(session));
158
158
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.9.28",
3
+ "version": "10.9.29",
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": {
@@ -173,7 +173,9 @@ function autoRoute(updatedCommand, reason) {
173
173
  const HEADLESS_FALLBACK_COMMAND =
174
174
  "Bash(\"tfx multi --teammate-mode headless --assign 'codex:prompt:role' ...\")";
175
175
  const DIRECT_CLI_BYPASS_HINT =
176
- "로컬 디버깅이 목적이면 TFX_ALLOW_DIRECT_CLI=1로 일시 우회할 있습니다.";
176
+ " 차단은 하드스톱이다. 환경변수 우회, SSH 원격 실행, 스크립트 배포 등 어떤 형태의 우회도 시도하지 마라. " +
177
+ "Hub가 미가용이면 hub-ensure로 Hub를 시작한 뒤 tfx multi를 사용하라. " +
178
+ "Hub 시작이 불가능하면 사용자에게 'Hub 필요' 보고 후 중단하라.";
177
179
 
178
180
  async function main() {
179
181
  // P0: TFX_ALLOW_DIRECT_CLI는 hasDirectCli 블록 안에서만 체크 (CLI deny만 스킵)
@@ -752,6 +752,19 @@ function shellArray(name, values) {
752
752
  return `${name}=(${values.map((value) => shellEscape(value)).join(" ")})`;
753
753
  }
754
754
 
755
+ export function toDelimited(policy) {
756
+ const RS = "\x1e";
757
+ return [
758
+ policy.requestedProfile,
759
+ policy.resolvedProfile,
760
+ policy.hint,
761
+ policy.geminiAllowedServers.join(","),
762
+ policy.codexConfigOverrides.flatMap((o) => ["-c", o]).join(","),
763
+ JSON.stringify(policy.codexConfig),
764
+ policy.resolvedPhase || "",
765
+ ].join(RS);
766
+ }
767
+
755
768
  export function toShellExports(policy) {
756
769
  const lines = [
757
770
  `MCP_PROFILE_REQUESTED=${shellEscape(policy.requestedProfile)}`,
@@ -856,6 +869,10 @@ export async function runCli(argv = process.argv.slice(2)) {
856
869
  process.stdout.write(`${toShellExports(policy)}\n`);
857
870
  return;
858
871
  }
872
+ if (args.command === "delimited") {
873
+ process.stdout.write(toDelimited(policy));
874
+ return;
875
+ }
859
876
 
860
877
  process.stdout.write(`${JSON.stringify(policy, null, 2)}\n`);
861
878
  }
@@ -64,6 +64,7 @@ track_worker_pid() {
64
64
  }
65
65
 
66
66
  cleanup_workers() {
67
+ _codex_config_swap "restore" 2>/dev/null || true
67
68
  deregister_agent 2>/dev/null || true
68
69
  [[ ! -f "$_PID_TRACK" ]] && return
69
70
  while IFS= read -r pid; do
@@ -1203,7 +1204,7 @@ resolve_mcp_policy() {
1203
1204
  fi
1204
1205
 
1205
1206
  local -a cmd=(
1206
- "$NODE_BIN" "$filter_script" shell
1207
+ "$NODE_BIN" "$filter_script" delimited
1207
1208
  "--agent" "$AGENT_TYPE"
1208
1209
  "--profile" "$MCP_PROFILE"
1209
1210
  "--available" "$available_servers"
@@ -1213,13 +1214,18 @@ resolve_mcp_policy() {
1213
1214
  [[ -n "$TFX_SEARCH_TOOL" ]] && cmd+=("--search-tool" "$TFX_SEARCH_TOOL")
1214
1215
  [[ -n "$TFX_WORKER_INDEX" ]] && cmd+=("--worker-index" "$TFX_WORKER_INDEX")
1215
1216
 
1216
- local shell_exports
1217
- if ! shell_exports="$("${cmd[@]}")"; then
1217
+ local _raw
1218
+ if ! _raw="$("${cmd[@]}")"; then
1218
1219
  echo "[tfx-route] ERROR: MCP 정책 계산 실패" >&2
1219
1220
  return 1
1220
1221
  fi
1221
1222
 
1222
- eval "$shell_exports"
1223
+ local _gemini_servers _codex_flags _phase
1224
+ IFS=$'\x1e' read -r MCP_PROFILE_REQUESTED MCP_RESOLVED_PROFILE MCP_HINT \
1225
+ _gemini_servers _codex_flags CODEX_CONFIG_JSON _phase <<< "$_raw"
1226
+ IFS=',' read -r -a GEMINI_ALLOWED_SERVERS <<< "$_gemini_servers"
1227
+ IFS=',' read -r -a CODEX_CONFIG_FLAGS <<< "$_codex_flags"
1228
+ [[ -n "$_phase" ]] && MCP_PIPELINE_PHASE="$_phase"
1223
1229
  }
1224
1230
 
1225
1231
  get_claude_model() {