triflux 2.5.1 → 3.0.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/bin/triflux.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // triflux CLI — setup, doctor, version
3
3
  import { copyFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, readdirSync, unlinkSync } from "fs";
4
4
  import { join, dirname } from "path";
@@ -145,7 +145,7 @@ function checkCliCrossShell(cmd, installHint) {
145
145
  return 1;
146
146
  }
147
147
  if (bashMissing) {
148
- warn("bash에서 미발견 — cli-route.sh 실행 불가");
148
+ warn("bash에서 미발견 — tfx-route.sh 실행 불가");
149
149
  info('→ ~/.bashrc에 추가: export PATH="$PATH:$APPDATA/npm"');
150
150
  return 1;
151
151
  }
@@ -158,9 +158,9 @@ function cmdSetup() {
158
158
  console.log(`\n${BOLD}triflux setup${RESET}\n`);
159
159
 
160
160
  syncFile(
161
- join(PKG_ROOT, "scripts", "cli-route.sh"),
162
- join(CLAUDE_DIR, "scripts", "cli-route.sh"),
163
- "cli-route.sh"
161
+ join(PKG_ROOT, "scripts", "tfx-route.sh"),
162
+ join(CLAUDE_DIR, "scripts", "tfx-route.sh"),
163
+ "tfx-route.sh"
164
164
  );
165
165
 
166
166
  syncFile(
@@ -175,6 +175,18 @@ function cmdSetup() {
175
175
  "notion-read.mjs"
176
176
  );
177
177
 
178
+ syncFile(
179
+ join(PKG_ROOT, "scripts", "tfx-route-post.mjs"),
180
+ join(CLAUDE_DIR, "scripts", "tfx-route-post.mjs"),
181
+ "tfx-route-post.mjs"
182
+ );
183
+
184
+ syncFile(
185
+ join(PKG_ROOT, "scripts", "tfx-batch-stats.mjs"),
186
+ join(CLAUDE_DIR, "scripts", "tfx-batch-stats.mjs"),
187
+ "tfx-batch-stats.mjs"
188
+ );
189
+
178
190
  // 스킬 동기화 (~/.claude/skills/{name}/SKILL.md)
179
191
  const skillsSrc = join(PKG_ROOT, "skills");
180
192
  const skillsDst = join(CLAUDE_DIR, "skills");
@@ -323,9 +335,9 @@ function cmdDoctor(options = {}) {
323
335
  if (fix) {
324
336
  section("Auto Fix");
325
337
  syncFile(
326
- join(PKG_ROOT, "scripts", "cli-route.sh"),
327
- join(CLAUDE_DIR, "scripts", "cli-route.sh"),
328
- "cli-route.sh"
338
+ join(PKG_ROOT, "scripts", "tfx-route.sh"),
339
+ join(CLAUDE_DIR, "scripts", "tfx-route.sh"),
340
+ "tfx-route.sh"
329
341
  );
330
342
  syncFile(
331
343
  join(PKG_ROOT, "hud", "hud-qos-status.mjs"),
@@ -375,9 +387,9 @@ function cmdDoctor(options = {}) {
375
387
 
376
388
  let issues = 0;
377
389
 
378
- // 1. cli-route.sh
379
- section("cli-route.sh");
380
- const routeSh = join(CLAUDE_DIR, "scripts", "cli-route.sh");
390
+ // 1. tfx-route.sh
391
+ section("tfx-route.sh");
392
+ const routeSh = join(CLAUDE_DIR, "scripts", "tfx-route.sh");
381
393
  if (existsSync(routeSh)) {
382
394
  const ver = getVersion(routeSh);
383
395
  ok(`설치됨 ${ver ? `${DIM}v${ver}${RESET}` : ""}`);
@@ -676,7 +688,7 @@ function cmdUpdate() {
676
688
  return;
677
689
  }
678
690
 
679
- // 3. setup 재실행 (cli-route.sh, HUD, 스킬 동기화)
691
+ // 3. setup 재실행 (tfx-route.sh, HUD, 스킬 동기화)
680
692
  if (updated) {
681
693
  console.log("");
682
694
  // 업데이트 후 새 버전 읽기
@@ -742,10 +754,10 @@ function cmdList() {
742
754
  }
743
755
 
744
756
  function cmdVersion() {
745
- const routeVer = getVersion(join(CLAUDE_DIR, "scripts", "cli-route.sh"));
757
+ const routeVer = getVersion(join(CLAUDE_DIR, "scripts", "tfx-route.sh"));
746
758
  const hudVer = getVersion(join(CLAUDE_DIR, "hud", "hud-qos-status.mjs"));
747
759
  console.log(`\n ${AMBER}${BOLD}⬡ triflux${RESET} ${WHITE_BRIGHT}v${PKG.version}${RESET}`);
748
- if (routeVer) console.log(` ${GRAY}cli-route${RESET} v${routeVer}`);
760
+ if (routeVer) console.log(` ${GRAY}tfx-route${RESET} v${routeVer}`);
749
761
  if (hudVer) console.log(` ${GRAY}hud${RESET} v${hudVer}`);
750
762
  console.log("");
751
763
  }
@@ -509,7 +509,7 @@ function normalizeTimeToken(value) {
509
509
  }
510
510
  const dayHour = text.match(/^(\d+)d(\d+)h$/);
511
511
  if (dayHour) {
512
- return `${Number(dayHour[1])}d${Number(dayHour[2])}h`;
512
+ return `${Number(dayHour[1])}d${String(Number(dayHour[2])).padStart(2, "0")}h`;
513
513
  }
514
514
  return text;
515
515
  }
@@ -958,7 +958,9 @@ function getGeminiEmail() {
958
958
  // ============================================================================
959
959
  function getCodexRateLimits() {
960
960
  const now = new Date();
961
- let todayHasFiles = false;
961
+ let syntheticBucket = null; // 오늘 token_count에서 합성 (행 활성화 + 토큰 데이터용)
962
+
963
+ // 2일간 스캔: 실제 rate_limits 우선, 합성 버킷은 폴백
962
964
  for (let dayOffset = 0; dayOffset <= 1; dayOffset++) {
963
965
  const d = new Date(now.getTime() - dayOffset * 86_400_000);
964
966
  const sessDir = join(
@@ -971,10 +973,7 @@ function getCodexRateLimits() {
971
973
  let files;
972
974
  try { files = readdirSync(sessDir).filter((f) => f.endsWith(".jsonl")).sort().reverse(); }
973
975
  catch { continue; }
974
- if (dayOffset === 0 && files.length > 0) todayHasFiles = true;
975
976
 
976
- // 당일 모든 세션 파일을 스캔해 limit_id별 가장 최신 버킷을 병합
977
- // (파일 목록은 이름 역순 정렬 → 최신 세션 우선)
978
977
  const mergedBuckets = {};
979
978
  for (const file of files) {
980
979
  try {
@@ -985,7 +984,7 @@ function getCodexRateLimits() {
985
984
  const evt = JSON.parse(line);
986
985
  const rl = evt?.payload?.rate_limits;
987
986
  if (rl?.limit_id && !mergedBuckets[rl.limit_id]) {
988
- // limit_id별로 발견(=해당 세션의 가장 최신 이벤트)만 기록
987
+ // 실제 rate_limits: limit_id별 최신 이벤트만 기록
989
988
  mergedBuckets[rl.limit_id] = {
990
989
  limitId: rl.limit_id, limitName: rl.limit_name,
991
990
  primary: rl.primary, secondary: rl.secondary,
@@ -994,18 +993,33 @@ function getCodexRateLimits() {
994
993
  contextWindow: evt.payload?.info?.model_context_window,
995
994
  timestamp: evt.timestamp,
996
995
  };
996
+ } else if (dayOffset === 0 && !rl && evt?.payload?.info?.total_token_usage && !syntheticBucket) {
997
+ // 오늘 token_count: 합성 버킷 (rate_limits가 null일 때 행 활성화용)
998
+ syntheticBucket = {
999
+ limitId: "codex", limitName: "codex-session",
1000
+ primary: null, secondary: null,
1001
+ credits: null,
1002
+ tokens: evt.payload.info.total_token_usage,
1003
+ contextWindow: evt.payload.info.model_context_window,
1004
+ timestamp: evt.timestamp,
1005
+ };
997
1006
  }
998
1007
  } catch { /* 라인 파싱 실패 무시 */ }
999
1008
  if (Object.keys(mergedBuckets).length >= CODEX_MIN_BUCKETS) break;
1000
1009
  }
1001
1010
  } catch { /* 파일 읽기 실패 무시 */ }
1002
1011
  }
1003
- if (Object.keys(mergedBuckets).length > 0) return mergedBuckets;
1004
-
1005
- // 오늘 세션 파일이 존재하지만 rate_limits가 없으면 어제 stale 데이터로 폴백하지 않음
1006
- if (todayHasFiles && dayOffset === 0) return null;
1012
+ // 실제 rate_limits 발견 오늘 토큰 데이터 병합 후 즉시 반환
1013
+ if (Object.keys(mergedBuckets).length > 0) {
1014
+ if (syntheticBucket) {
1015
+ const main = mergedBuckets.codex || mergedBuckets[Object.keys(mergedBuckets)[0]];
1016
+ if (main && !main.tokens) main.tokens = syntheticBucket.tokens;
1017
+ }
1018
+ return mergedBuckets;
1019
+ }
1007
1020
  }
1008
- return null;
1021
+ // 실제 rate_limits 없음 → 합성 버킷이라도 반환 (행 활성화)
1022
+ return syntheticBucket ? { codex: syntheticBucket } : null;
1009
1023
  }
1010
1024
 
1011
1025
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "2.5.1",
3
+ "version": "3.0.0",
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": {