ccc-notifier 0.2.0 → 0.3.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.
@@ -7,12 +7,12 @@ import {
7
7
  formatTokens,
8
8
  formatUSD,
9
9
  modelDisplayName
10
- } from "./chunk-TBFKGFZX.js";
10
+ } from "./chunk-J5QAYTFE.js";
11
11
  import {
12
12
  paths,
13
13
  readConfig,
14
14
  readTurns
15
- } from "./chunk-IIYMGLV4.js";
15
+ } from "./chunk-ECADO26T.js";
16
16
 
17
17
  // src/dashboard.ts
18
18
  import { spawn, spawnSync } from "child_process";
@@ -170,7 +170,7 @@ function buildTurnEmbed(rec, map) {
170
170
  };
171
171
  }
172
172
  const ms = Date.parse(rec.ts);
173
- return {
173
+ const out = {
174
174
  t: Number.isFinite(ms) ? ms : 0,
175
175
  ts: fmtLocalDateTime(rec.ts),
176
176
  p: basename(rec.project) || rec.project || "(unknown)",
@@ -187,6 +187,8 @@ function buildTurnEmbed(rec, map) {
187
187
  tr: truncated,
188
188
  sa
189
189
  };
190
+ if (rec.source === "codex") out.sc = "codex";
191
+ return out;
190
192
  }
191
193
  function computeKpis(turns) {
192
194
  const now = /* @__PURE__ */ new Date();
@@ -331,6 +333,7 @@ section.card > .note{color:var(--muted); font-size:12px; margin:0 0 14px;}
331
333
  .turns .c-time,.turns .c-model{white-space:nowrap;}
332
334
  .turns .c-proj{max-width:160px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
333
335
  .turns .c-prompt{color:var(--ink2);}
336
+ .src-badge{display:inline-block; margin-left:6px; padding:1px 6px; border-radius:6px; font-size:11px; font-weight:600; line-height:1.4; background:var(--plane); color:var(--ink2); border:1px solid var(--border); vertical-align:middle;}
334
337
  .turn-row{cursor:pointer;}
335
338
  .turn-row:hover{background:color-mix(in srgb, var(--ink) 4%, transparent);}
336
339
  .turn-row:focus-visible{outline:2px solid var(--s1); outline-offset:-2px;}
@@ -418,17 +421,37 @@ var APP_JS = `<script>
418
421
  function ssGet(k){ try { return sessionStorage.getItem(k); } catch(e){ return null; } }
419
422
  function ssSet(k, v){ try { sessionStorage.setItem(k, v); } catch(e){} }
420
423
  var GRAN = ssGet('cccn-gran'); if(['day','week','month'].indexOf(GRAN) < 0) GRAN = 'day';
424
+ // \u30BD\u30FC\u30B9(Claude/Codex)\u30D5\u30A3\u30EB\u30BF\u3002\u30C1\u30C3\u30D7\u304C\u63CF\u753B\u3055\u308C\u3066\u3044\u308B(=Codex \u30EC\u30B3\u30FC\u30C9\u304C\u5B58\u5728\u3059\u308B)\u3068\u304D\u306E\u307F\u6709\u52B9\u3002
425
+ // \u65E2\u5B9A 'all'\u3002sessionStorage \u306B\u4FDD\u5B58\u3057\u81EA\u52D5\u30EA\u30ED\u30FC\u30C9(meta refresh)\u3092\u8DE8\u3044\u3067\u7DAD\u6301\u3059\u308B(cccn-gran \u3068\u540C\u3058\u4ED5\u7D44\u307F)\u3002
426
+ var HAS_CODEX = document.querySelectorAll('[data-src]').length > 0;
427
+ var SRC = ssGet('cccn-src'); if(['all','claude','codex'].indexOf(SRC) < 0) SRC = 'all';
428
+ // \u8FF0\u8A9E: claude \u2192 sc \u306A\u3057 / codex \u2192 sc==='codex' / all \u2192 \u5168\u4EF6\u3002Codex \u7121\u3057(\u30C1\u30C3\u30D7\u7121\u3057)\u306A\u3089\u5E38\u306B\u5168\u4EF6\u3002
429
+ function activeTurns(){
430
+ if(!HAS_CODEX || SRC === 'all') return turns;
431
+ if(SRC === 'codex') return turns.filter(function(tn){ return tn.sc === 'codex'; });
432
+ return turns.filter(function(tn){ return !tn.sc; });
433
+ }
421
434
  // \u65E2\u5B9A\u306F\u300C\u8D77\u52D5\u3057\u305F\u65E5\u300D\u3092\u521D\u671F\u9078\u629E\u3059\u308B(\u672A\u4FDD\u5B58\u6642)\u3002\u660E\u793A\u7684\u306A\u901A\u7B97\u306F '__all__'\u3001\u305D\u306E\u4ED6\u306F\u4FDD\u5B58\u30AD\u30FC\u3002
422
435
  var storedSel = ssGet('cccn-sel');
423
436
  var SEL = (storedSel === null) ? defaultSel(GRAN) : (storedSel === '__all__' ? null : storedSel);
424
437
  function setGran(g){ GRAN = g; ssSet('cccn-gran', g); }
425
438
  function setSel(v){ SEL = v; ssSet('cccn-sel', v === null ? '__all__' : v); }
439
+ // \u30BD\u30FC\u30B9\u5207\u66FF\u3002\u30D5\u30A3\u30EB\u30BF\u5F8C\u306B\u9078\u629E\u4E2D\u30D0\u30B1\u30C3\u30C8\u304C\u6D88\u3048\u305F\u3089\u901A\u7B97\u3078\u5012\u3059(\u7A7A\u8868\u793A\u306E\u8AA4\u89E3\u9632\u6B62\u30FBgran \u30C8\u30B0\u30EB\u3068\u540C\u3058\u6D41\u5100)\u3002
440
+ function setSrc(s){
441
+ SRC = s; ssSet('cccn-src', s);
442
+ if(SEL !== null){
443
+ var present = false, bk = buildBuckets(GRAN);
444
+ for(var i=0;i<bk.length;i++){ if(bk[i].key === SEL){ present = true; break; } }
445
+ if(!present) setSel(null);
446
+ }
447
+ }
426
448
  var lastRenderedGran = null; // \u76F4\u524D\u306B\u63CF\u753B\u3057\u305F\u7C92\u5EA6\u3002\u5909\u308F\u3063\u305F\u3068\u304D(=\u30D0\u30B1\u30C3\u30C8\u6570\u304C\u5909\u308F\u308B\u3068\u304D)\u3060\u3051\u53F3\u7AEF\u3078\u5BC4\u305B\u308B\u3002
427
449
 
428
450
  function buildBuckets(gran){
451
+ var src = activeTurns();
429
452
  var map = {}; var keys = [];
430
- for(var i=0;i<turns.length;i++){
431
- var tn = turns[i];
453
+ for(var i=0;i<src.length;i++){
454
+ var tn = src[i];
432
455
  var k = bucketKey(tn.t, gran);
433
456
  if(k === null) continue;
434
457
  var b = map[k];
@@ -452,8 +475,9 @@ var APP_JS = `<script>
452
475
  }
453
476
 
454
477
  function turnsInSelection(){
455
- if(SEL === null) return turns;
456
- return turns.filter(function(tn){ return bucketKey(tn.t, GRAN) === SEL; });
478
+ var src = activeTurns();
479
+ if(SEL === null) return src;
480
+ return src.filter(function(tn){ return bucketKey(tn.t, GRAN) === SEL; });
457
481
  }
458
482
 
459
483
  // ---- SVG \u30D8\u30EB\u30D1\u30FC ----
@@ -726,6 +750,12 @@ var APP_JS = `<script>
726
750
  sub.textContent = ' ' + mk + (mk === curMonth ? '(\u4ECA\u6708)' : '');
727
751
  h.appendChild(sub);
728
752
  budgetEl.appendChild(h);
753
+ // \u4E88\u7B97\u30AB\u30FC\u30C9\u306F\u5E38\u306B\u5168\u30BD\u30FC\u30B9\u5408\u7B97(\u30BD\u30FC\u30B9\u30D5\u30A3\u30EB\u30BF\u975E\u9023\u52D5)\u3002Codex \u304C\u3042\u308B\u6642\u3060\u3051\u305D\u306E\u65E8\u3092\u660E\u8A18\u3059\u308B\u3002
754
+ if(HAS_CODEX){
755
+ var srcNote = document.createElement('p'); srcNote.className = 'note';
756
+ srcNote.textContent = '\u5168\u30BD\u30FC\u30B9\u5408\u7B97 / all sources';
757
+ budgetEl.appendChild(srcNote);
758
+ }
729
759
 
730
760
  var bar = document.createElement('div'); bar.className = 'budget-bar';
731
761
  var fill = document.createElement('div'); fill.className = 'budget-fill lvl-' + level;
@@ -746,6 +776,55 @@ var APP_JS = `<script>
746
776
  budgetEl.appendChild(foot);
747
777
  }
748
778
 
779
+ // ---- KPI(\u4ECA\u65E5/\u4ECA\u9031/\u4ECA\u6708/\u901A\u7B97)+ \u30D2\u30FC\u30ED\u30FC(\u901A\u7B97\u30D0\u30CA\u30FC)----
780
+ // \u30BD\u30FC\u30B9\u30D5\u30A3\u30EB\u30BF\u9023\u52D5\u3002Codex \u7121\u3057\u306A\u3089\u30B5\u30FC\u30D0\u63CF\u753B\u306E\u307E\u307E(\u65E2\u5B58\u6319\u52D5\u30921px\u3082\u5909\u3048\u306A\u3044\u305F\u3081\u4F55\u3082\u3057\u306A\u3044)\u3002
781
+ // \u30AB\u30FC\u30C9\u306F .kpi \u5185\u306E .stat \u3092\u9806\u306B today / week / month / all \u3068\u3057\u3066\u66F4\u65B0\u3059\u308B(\u30B5\u30FC\u30D0\u63CF\u753B\u9806\u306B\u4E00\u81F4)\u3002
782
+ // \u30D2\u30FC\u30ED\u30FC\u306F\u30D5\u30A3\u30EB\u30BF\u5F8C\u306E\u5168\u671F\u9593\u5408\u8A08\u306B\u5DEE\u3057\u66FF\u3048\u3001SA \u5185\u8A33\u884C\u306F SA \u5408\u8A08\u30BC\u30ED\u306A\u3089\u884C\u3054\u3068\u96A0\u3059
783
+ // (Codex \u30EC\u30B3\u30FC\u30C9\u306F SA \u3092\u6301\u305F\u306A\u3044\u305F\u3081 Codex \u30D5\u30A3\u30EB\u30BF\u6642\u306F\u5FC5\u7136\u7684\u306B\u975E\u8868\u793A)\u3002\u884C\u306E\u6587\u8A00\u306F\u30B5\u30FC\u30D0\u63CF\u753B\u306E
784
+ // \u63A5\u982D\u8F9E($ \u306E\u624B\u524D\u307E\u3067)\u3092\u518D\u5229\u7528\u3057\u3066\u6570\u5024\u90E8\u5206\u3060\u3051\u7D44\u307F\u76F4\u3059(APP_JS \u306B\u8868\u793A\u6587\u8A00\u3092\u91CD\u8907\u3055\u305B\u306A\u3044)\u3002
785
+ var kpiCards = document.querySelectorAll('.kpi .stat');
786
+ var heroValueEl = document.querySelector('.hero .hero-value');
787
+ var heroMetaEls = document.querySelectorAll('.hero .hero-meta');
788
+ var heroSaEl = heroMetaEls.length > 1 ? heroMetaEls[1] : null; // SA \u884C(SA \u3042\u308A\u306E\u3068\u304D\u3060\u3051\u30B5\u30FC\u30D0\u304C\u63CF\u753B)
789
+ var heroSaPrefix = heroSaEl ? ((heroSaEl.textContent || '').split('$')[0]) : '';
790
+ function setKpiCard(card, tot){
791
+ if(!card) return;
792
+ var v = card.querySelector('.stat-value');
793
+ var m = card.querySelector('.stat-meta');
794
+ if(v) v.textContent = formatUSD(tot.usd);
795
+ if(m) m.textContent = formatJPY(tot.jpy) + ' \xB7 ' + tot.turns + ' \u30BF\u30FC\u30F3';
796
+ }
797
+ function renderKpis(){
798
+ if(!HAS_CODEX) return;
799
+ var src = activeTurns();
800
+ var now = new Date();
801
+ var y = now.getFullYear(), mo = now.getMonth(), d = now.getDate();
802
+ var weekCutoff = Date.now() - 7*86400000;
803
+ var today = {usd:0,jpy:0,turns:0}, week = {usd:0,jpy:0,turns:0}, month = {usd:0,jpy:0,turns:0}, all = {usd:0,jpy:0,turns:0};
804
+ var saUsd = 0, saJpy = 0;
805
+ for(var i=0;i<src.length;i++){
806
+ var tn = src[i], bs = tn.bs || {}, fx = tn.fx || 0, u = 0;
807
+ for(var s in bs){ if(bs.hasOwnProperty(s)) u += bs[s]; }
808
+ var j = u * fx;
809
+ if(tn.sa){ var sau = u - (tn.um || 0); saUsd += sau; saJpy += sau * fx; } // SA \u5206 = \u7DCF\u984D \u2212 \u30E1\u30A4\u30F3
810
+ all.usd += u; all.jpy += j; all.turns++;
811
+ var dt = new Date(tn.t);
812
+ if(isNaN(dt.getTime())) continue;
813
+ if(dt.getFullYear()===y && dt.getMonth()===mo && dt.getDate()===d){ today.usd+=u; today.jpy+=j; today.turns++; }
814
+ if(tn.t >= weekCutoff){ week.usd+=u; week.jpy+=j; week.turns++; }
815
+ if(dt.getFullYear()===y && dt.getMonth()===mo){ month.usd+=u; month.jpy+=j; month.turns++; }
816
+ }
817
+ if(kpiCards.length >= 4){
818
+ setKpiCard(kpiCards[0], today); setKpiCard(kpiCards[1], week); setKpiCard(kpiCards[2], month); setKpiCard(kpiCards[3], all);
819
+ }
820
+ if(heroValueEl) heroValueEl.textContent = formatUSD(all.usd);
821
+ if(heroMetaEls.length > 0) heroMetaEls[0].textContent = formatJPY(all.jpy) + ' \xB7 ' + all.turns + ' \u30BF\u30FC\u30F3';
822
+ if(heroSaEl){
823
+ heroSaEl.hidden = !(saUsd > 0);
824
+ if(saUsd > 0) heroSaEl.textContent = heroSaPrefix + formatUSD(saUsd) + '(' + formatJPY(saJpy) + ')';
825
+ }
826
+ }
827
+
749
828
  // ---- \u30BF\u30FC\u30F3\u5C65\u6B74 ----
750
829
  // \u5168\u671F\u9593\u3060\u3068\u6570\u5343\u884C\u306B\u306A\u308A\u30DA\u30FC\u30B8\u304C\u6975\u7AEF\u306B\u9577\u304F\u30FB\u91CD\u304F\u306A\u308B\u305F\u3081\u3001DOM \u63CF\u753B\u306F\u6700\u5927 HIST_CAP \u4EF6\u306B\u6291\u3048\u308B\u3002
751
830
  // \u691C\u7D22\u306F\u9078\u629E\u4E2D\u306E\u5168\u30BF\u30FC\u30F3\u3092\u5BFE\u8C61\u306B\u3057\u3001\u4E00\u81F4\u4EF6\u6570\u3092\u4EF6\u6570\u8868\u793A\u306B\u51FA\u3059(\u5148\u982D HIST_CAP \u4EF6\u3060\u3051\u63CF\u753B)\u3002
@@ -765,7 +844,14 @@ var APP_JS = `<script>
765
844
  function cell(text, cls){ var td = document.createElement('td'); if(cls) td.className = cls; td.textContent = text; tr.appendChild(td); return td; }
766
845
  cell(t.ts || '', 'c-time');
767
846
  cell(t.p || '', 'c-proj');
768
- cell(t.md || '', 'c-model');
847
+ var mcell = cell(t.md || '', 'c-model');
848
+ // Codex \u7531\u6765\u306F\u30E2\u30C7\u30EB\u540D\u306E\u96A3\u306B\u5C0F\u3055\u306A Codex \u30D0\u30C3\u30B8\u3002\u52D5\u7684\u5024\u306F\u5165\u308C\u305A\u9759\u7684\u30C6\u30AD\u30B9\u30C8\u3092 textContent \u3067\u633F\u5165\u3002
849
+ if(t.sc === 'codex'){
850
+ mcell.appendChild(document.createTextNode(' '));
851
+ var badge = document.createElement('span'); badge.className = 'src-badge';
852
+ badge.textContent = 'Codex';
853
+ mcell.appendChild(badge);
854
+ }
769
855
  cell(t.ti || '', 'c-num');
770
856
  cell(t.to || '', 'c-num');
771
857
  cell(formatUSD(t.um || 0), 'c-num');
@@ -832,6 +918,8 @@ var APP_JS = `<script>
832
918
  for(var i=0;i<gbtns.length;i++){ gbtns[i].classList.toggle('active', gbtns[i].getAttribute('data-gran') === GRAN); }
833
919
  var allBtn = document.getElementById('cccn-all');
834
920
  if(allBtn) allBtn.classList.toggle('active', SEL === null);
921
+ var sbtns = document.querySelectorAll('[data-src]');
922
+ for(var k=0;k<sbtns.length;k++){ sbtns[k].classList.toggle('active', sbtns[k].getAttribute('data-src') === SRC); }
835
923
  }
836
924
 
837
925
  // ---- \u5168\u4F53\u30EC\u30F3\u30C0\u30EA\u30F3\u30B0 ----
@@ -839,6 +927,7 @@ var APP_JS = `<script>
839
927
  var sub = turnsInSelection();
840
928
  renderChart();
841
929
  renderBudget();
930
+ renderKpis();
842
931
  renderByModel(sub);
843
932
  renderByProject(sub);
844
933
  renderHistory(sub);
@@ -853,6 +942,10 @@ var APP_JS = `<script>
853
942
  }
854
943
  var allBtn = document.getElementById('cccn-all');
855
944
  if(allBtn) allBtn.addEventListener('click', function(){ setSel(null); render(); });
945
+ var srcBtns = document.querySelectorAll('[data-src]');
946
+ for(var sbi=0;sbi<srcBtns.length;sbi++){
947
+ (function(btn){ btn.addEventListener('click', function(){ setSrc(btn.getAttribute('data-src')); render(); }); })(srcBtns[sbi]);
948
+ }
856
949
  if(search){
857
950
  var saved = ssGet('cccn-search');
858
951
  if(saved !== null) search.value = saved;
@@ -879,13 +972,13 @@ function renderLegend(slots) {
879
972
  const items = slots.map((m) => `<span class="legend-item"><span class="swatch k${m.slot}"></span>${esc(m.name)}</span>`).join("");
880
973
  return `<div class="legend">${items}</div>`;
881
974
  }
882
- function budgetCard(budgetUSD, month, fallbackRate) {
975
+ function budgetCard(budgetUSD, month, fallbackRate, hasCodex) {
883
976
  if (!(budgetUSD > 0)) return "";
884
977
  const pct = month.usd / budgetUSD * 100;
885
978
  const width = Math.max(0, Math.min(100, pct));
886
979
  const level = pct >= 100 ? "over" : pct >= 70 ? "warn" : "ok";
887
980
  const budgetJpy = budgetUSD * fallbackRate;
888
- return `<section class="card" id="cccn-budget"><h2>\u6708\u4E88\u7B97 / Monthly budget<span class="stat-sub"> \u4ECA\u6708(\u66A6\u6708)</span></h2><div class="budget-bar"><div class="budget-fill lvl-${level}" style="width:${width.toFixed(1)}%"></div></div><div class="budget-foot"><span>\u4ECA\u6708 <b>${esc(formatUSD(month.usd))}</b> / ${esc(formatUSD(budgetUSD))} <span class="muted">(${esc(formatJPY(month.jpy))} / ${esc(formatJPY(budgetJpy))})</span></span><span class="budget-pct lvl-${level}">${pct.toFixed(1)}% used</span></div></section>`;
981
+ return `<section class="card" id="cccn-budget"><h2>\u6708\u4E88\u7B97 / Monthly budget<span class="stat-sub"> \u4ECA\u6708(\u66A6\u6708)</span></h2>` + (hasCodex ? `<p class="note">\u5168\u30BD\u30FC\u30B9\u5408\u7B97 / all sources</p>` : "") + `<div class="budget-bar"><div class="budget-fill lvl-${level}" style="width:${width.toFixed(1)}%"></div></div><div class="budget-foot"><span>\u4ECA\u6708 <b>${esc(formatUSD(month.usd))}</b> / ${esc(formatUSD(budgetUSD))} <span class="muted">(${esc(formatJPY(month.jpy))} / ${esc(formatJPY(budgetJpy))})</span></span><span class="budget-pct lvl-${level}">${pct.toFixed(1)}% used</span></div></section>`;
889
982
  }
890
983
  function escapeJsonForScript(json) {
891
984
  return json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
@@ -901,6 +994,7 @@ function renderDashboard(turns, opts) {
901
994
  const turnsEmbed = turns.map((rec) => buildTurnEmbed(rec, map));
902
995
  const anyTruncated = turnsEmbed.some((t) => t.tr);
903
996
  const anySub = turns.some((r) => r.subagents);
997
+ const anyCodex = turns.some((r) => r.source === "codex");
904
998
  let subUsd = 0;
905
999
  let subJpy = 0;
906
1000
  for (const r of turns) {
@@ -929,7 +1023,8 @@ function renderDashboard(turns, opts) {
929
1023
  const dataJson = escapeJsonForScript(JSON.stringify(embed));
930
1024
  const reloadSec = Number.isFinite(opts.autoReloadSec) && opts.autoReloadSec > 0 ? Math.floor(opts.autoReloadSec) : 0;
931
1025
  const refreshMeta = reloadSec > 0 ? `<meta http-equiv="refresh" content="${reloadSec}">` : "";
932
- const autoUpdateFoot = reloadSec > 0 ? `<div class="foot">\u7D04 ${reloadSec} \u79D2\u3054\u3068\u306B\u81EA\u52D5\u66F4\u65B0(\u6700\u65B0\u5316\u306F Claude Code \u306E\u5FDC\u7B54\u5B8C\u4E86\u6642)</div>` : "";
1026
+ const updateTrigger = anyCodex ? "Claude Code / Codex \u306E\u5FDC\u7B54\u5B8C\u4E86\u6642" : "Claude Code \u306E\u5FDC\u7B54\u5B8C\u4E86\u6642";
1027
+ const autoUpdateFoot = reloadSec > 0 ? `<div class="foot">\u7D04 ${reloadSec} \u79D2\u3054\u3068\u306B\u81EA\u52D5\u66F4\u65B0(\u6700\u65B0\u5316\u306F ${updateTrigger})</div>` : "";
933
1028
  const head = `<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="color-scheme" content="light dark">` + refreshMeta + `<title>ccc-notifier \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9</title>` + STYLE + `</head><body><div class="wrap">`;
934
1029
  const foot = autoUpdateFoot + `<div class="foot">ccc-notifier v${esc(version)} \xB7 \u30C7\u30FC\u30BF\u306F\u30ED\u30FC\u30AB\u30EB\u306E\u307F / all data stays local</div></div><div id="cccn-tip" class="cccn-tip" hidden></div><script id="cccn-data" type="application/json">${dataJson}</script>` + APP_JS + `</body></html>`;
935
1030
  if (turns.length === 0) {
@@ -939,8 +1034,9 @@ function renderDashboard(turns, opts) {
939
1034
  }
940
1035
  const header = `<div class="head"><div><h1>ccc-notifier \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9</h1><div class="sub">${esc(period)}<span class="muted"> \xB7 ${esc(rangeText)}</span></div><div class="sub muted">\u751F\u6210 ${esc(generatedAt)} / generated</div></div><div class="hero"><div class="hero-label">\u901A\u7B97 / Total</div><div class="hero-value">${esc(formatUSD(kpi.all.usd))}</div><div class="hero-meta">${esc(formatJPY(kpi.all.jpy))} \xB7 ${kpi.all.turns} \u30BF\u30FC\u30F3</div>` + (anySub ? `<div class="hero-meta">\u3046\u3061\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8 ${esc(formatUSD(subUsd))}(${esc(formatJPY(subJpy))})</div>` : "") + `</div></div>`;
941
1036
  const kpiSection = `<div class="kpi">` + statCard("\u4ECA\u65E5", "Today", kpi.today) + statCard("\u4ECA\u9031", "\u76F4\u8FD17\u65E5", kpi.week) + statCard("\u4ECA\u6708", "\u66A6\u6708", kpi.month) + statCard("\u901A\u7B97", "\u5168\u671F\u9593", kpi.all) + `</div>`;
942
- const budgetSection = budgetCard(budgetUSD, kpi.month, cfg.fx.fallbackRate);
943
- const chartSection = `<section class="card"><h2>\u30B3\u30B9\u30C8\u63A8\u79FB / Cost over time</h2><p class="note">\u7C92\u5EA6\u3092 \u65E5 / \u9031 / \u6708 \u3067\u5207\u308A\u66FF\u3048\u3089\u308C\u307E\u3059(\u6A2A\u30B9\u30AF\u30ED\u30FC\u30EB\u3067\u904E\u53BB\u307E\u3067)\u3002\u68D2\u3092\u30AF\u30EA\u30C3\u30AF\u3059\u308B\u3068\u305D\u306E\u671F\u9593\u304C\u9078\u629E\u3055\u308C\u3001\u4E0B\u306E\u30E2\u30C7\u30EB\u5225\u30FB\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5225\u30FB\u5C65\u6B74\u304C\u9023\u52D5\u3057\u307E\u3059\u3002\u300C\u901A\u7B97\u300D\u3067\u5168\u671F\u9593\u306B\u623B\u308A\u307E\u3059\u3002\u30E2\u30C7\u30EB\u5225\u306B\u8272\u5206\u3051\u3002</p><div class="toolbar-row"><div class="seg-toggle"><button type="button" data-gran="day">\u65E5</button><button type="button" data-gran="week">\u9031</button><button type="button" data-gran="month">\u6708</button></div><button type="button" id="cccn-all" class="btn">\u901A\u7B97</button><span id="cccn-sel-label" class="sel-label"></span></div>` + renderLegend(map.slots) + `<div class="chart-scroll"><div id="cccn-chart"></div></div></section>`;
1037
+ const budgetSection = budgetCard(budgetUSD, kpi.month, cfg.fx.fallbackRate, anyCodex);
1038
+ const srcToggle = anyCodex ? `<div class="seg-toggle" id="cccn-src-toggle"><button type="button" data-src="all">\u5168\u4F53</button><button type="button" data-src="claude">Claude</button><button type="button" data-src="codex">Codex</button></div>` : "";
1039
+ const chartSection = `<section class="card"><h2>\u30B3\u30B9\u30C8\u63A8\u79FB / Cost over time</h2><p class="note">\u7C92\u5EA6\u3092 \u65E5 / \u9031 / \u6708 \u3067\u5207\u308A\u66FF\u3048\u3089\u308C\u307E\u3059(\u6A2A\u30B9\u30AF\u30ED\u30FC\u30EB\u3067\u904E\u53BB\u307E\u3067)\u3002\u68D2\u3092\u30AF\u30EA\u30C3\u30AF\u3059\u308B\u3068\u305D\u306E\u671F\u9593\u304C\u9078\u629E\u3055\u308C\u3001\u4E0B\u306E\u30E2\u30C7\u30EB\u5225\u30FB\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5225\u30FB\u5C65\u6B74\u304C\u9023\u52D5\u3057\u307E\u3059\u3002\u300C\u901A\u7B97\u300D\u3067\u5168\u671F\u9593\u306B\u623B\u308A\u307E\u3059\u3002\u30E2\u30C7\u30EB\u5225\u306B\u8272\u5206\u3051\u3002</p><div class="toolbar-row"><div class="seg-toggle"><button type="button" data-gran="day">\u65E5</button><button type="button" data-gran="week">\u9031</button><button type="button" data-gran="month">\u6708</button></div><button type="button" id="cccn-all" class="btn">\u901A\u7B97</button>` + srcToggle + `<span id="cccn-sel-label" class="sel-label"></span></div>` + renderLegend(map.slots) + `<div class="chart-scroll"><div id="cccn-chart"></div></div></section>`;
944
1040
  const breakdownSection = `<div class="grid2"><section class="card"><h2>\u30E2\u30C7\u30EB\u5225\u5185\u8A33 / By model</h2><p class="note">\u9078\u629E\u4E2D\u306E\u671F\u9593(\u65E2\u5B9A\u306F\u901A\u7B97)\u306E\u30B3\u30B9\u30C8\u964D\u9806\u3002\u8907\u6570\u30E2\u30C7\u30EB\u306E\u30BF\u30FC\u30F3\u306F\u5404\u30E2\u30C7\u30EB\u306B1\u305A\u3064\u8A08\u4E0A\u3002</p><div class="table-wrap" id="cccn-bymodel"></div></section><section class="card"><h2>\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5225 / By project</h2><p class="note">\u9078\u629E\u4E2D\u306E\u671F\u9593\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8(\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA basename)\u5358\u4F4D\u30FB\u30B3\u30B9\u30C8\u964D\u9806\u3002</p><div class="table-wrap" id="cccn-byproject"></div></section></div>`;
945
1041
  const capNote = "\u4EF6\u6570\u304C\u591A\u3044\u5834\u5408\u306F\u65B0\u3057\u3044\u9806\u306B\u5148\u982D200\u4EF6\u306E\u307F\u63CF\u753B\u3057\u307E\u3059(\u691C\u7D22\u3084\u3001\u4E0A\u306E\u30B0\u30E9\u30D5\u3067\u671F\u9593\u3092\u9078\u3076\u3068\u7D5E\u308A\u8FBC\u3081\u307E\u3059)\u3002";
946
1042
  const truncNote = anyTruncated ? `<p class="note">\u9078\u629E\u4E2D\u306E\u671F\u9593\u306E\u30BF\u30FC\u30F3\u3092\u65B0\u3057\u3044\u9806\u306B\u8868\u793A\u3057\u307E\u3059\u3002${capNote}\u30D7\u30ED\u30F3\u30D7\u30C8\u306F1\u4EF6\u3042\u305F\u308A\u6700\u5927 ${PROMPT_MAX.toLocaleString("en-US")} \u5B57\u307E\u3067(\u8D85\u904E\u5206\u306F\u300C${esc(PROMPT_TRUNC_MARK)}\u300D)\u3002\u884C\u30AF\u30EA\u30C3\u30AF\u3067\u5168\u6587\u5C55\u958B\u3002</p>` : `<p class="note">\u9078\u629E\u4E2D\u306E\u671F\u9593\u306E\u30BF\u30FC\u30F3\u3092\u65B0\u3057\u3044\u9806\u306B\u8868\u793A\u3057\u307E\u3059\u3002${capNote}\u884C\u30AF\u30EA\u30C3\u30AF\u3067\u30D7\u30ED\u30F3\u30D7\u30C8\u5168\u6587\u3092\u5C55\u958B\u3057\u307E\u3059\u3002</p>`;
package/dist/cli.js CHANGED
@@ -1,36 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  fmtMuteUntil
4
- } from "./chunk-KUJZYZSG.js";
4
+ } from "./chunk-4RZ6OGTD.js";
5
5
  import {
6
+ codexHooksFile,
6
7
  matchesMarker
7
- } from "./chunk-OXMHOVUL.js";
8
+ } from "./chunk-7KVOQ4UZ.js";
8
9
  import {
9
10
  notifyOS,
10
11
  notifySlack,
11
12
  selectNotifyBackend
12
- } from "./chunk-4JNZ7BHO.js";
13
+ } from "./chunk-NV5UOHJA.js";
13
14
  import {
14
15
  isWSL
15
16
  } from "./chunk-DGXUSPS4.js";
17
+ import {
18
+ codexHome,
19
+ detectCodex
20
+ } from "./chunk-HTYUYKFW.js";
16
21
  import {
17
22
  aggregateNewTurn,
18
23
  computeCost,
19
24
  getUsdJpy,
20
25
  loadPriceTable
21
- } from "./chunk-OEG3AVU6.js";
26
+ } from "./chunk-LHKBGA5K.js";
22
27
  import {
23
28
  formatJPY,
24
29
  formatTokens,
25
30
  formatUSD
26
- } from "./chunk-TBFKGFZX.js";
31
+ } from "./chunk-J5QAYTFE.js";
27
32
  import {
28
33
  isMuted,
29
34
  paths,
30
35
  readConfig,
31
36
  readMuteState,
32
37
  readTurns
33
- } from "./chunk-IIYMGLV4.js";
38
+ } from "./chunk-ECADO26T.js";
34
39
 
35
40
  // src/cli.ts
36
41
  import { realpathSync } from "fs";
@@ -156,6 +161,50 @@ async function checkHookRegistration() {
156
161
  }
157
162
  return true;
158
163
  }
164
+ async function checkCodex() {
165
+ if (!detectCodex()) {
166
+ log("ok", "Codex CLI \u306F\u672A\u691C\u51FA\u3067\u3059(\u672A\u4F7F\u7528\u306A\u3089\u554F\u984C\u3042\u308A\u307E\u305B\u3093)");
167
+ return true;
168
+ }
169
+ const hooksFile = codexHooksFile();
170
+ const matchedCommands = [];
171
+ if (existsSync(hooksFile)) {
172
+ try {
173
+ const raw = await readFile(hooksFile, "utf8");
174
+ const parsed = JSON.parse(raw);
175
+ if (isRecord(parsed)) {
176
+ const hooks = parsed.hooks;
177
+ const stopEntries = isRecord(hooks) ? hooks.Stop : void 0;
178
+ if (Array.isArray(stopEntries)) {
179
+ for (const entry of stopEntries) {
180
+ if (!isRecord(entry)) continue;
181
+ const innerHooks = entry.hooks;
182
+ if (!Array.isArray(innerHooks)) continue;
183
+ for (const h of innerHooks) {
184
+ if (isRecord(h) && typeof h.command === "string" && matchesMarker(h.command)) {
185
+ matchedCommands.push(h.command);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ } catch {
192
+ }
193
+ }
194
+ if (matchedCommands.length > 0) {
195
+ log("ok", `Codex \u306E Stop hook \u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u3059: ${matchedCommands.join(" / ")}`);
196
+ log("ok", "codex \u5074\u3067 hook \u3092\u627F\u8A8D\u6E08\u307F\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044(\u672A\u627F\u8A8D\u3060\u3068\u901A\u77E5\u3055\u308C\u307E\u305B\u3093)");
197
+ } else {
198
+ log("warn", "Codex \u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u304C hook \u304C\u672A\u767B\u9332\u3067\u3059\u3002init --codex \u3067\u767B\u9332\u3067\u304D\u307E\u3059");
199
+ }
200
+ const sessionsDir = join(codexHome(), "sessions");
201
+ if (existsSync(sessionsDir)) {
202
+ log("ok", `Codex \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u78BA\u8A8D\u3057\u307E\u3057\u305F: ${sessionsDir}`);
203
+ } else {
204
+ log("ok", `Codex \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093: ${sessionsDir}(\u30BB\u30C3\u30B7\u30E7\u30F3\u672A\u4F5C\u6210\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059)`);
205
+ }
206
+ return true;
207
+ }
159
208
  function readDirSafe(dir) {
160
209
  try {
161
210
  return readdirSync(dir, { withFileTypes: true });
@@ -339,6 +388,7 @@ async function checkRecentSessionTotal(latestTranscript) {
339
388
  async function runDoctor() {
340
389
  const results = [];
341
390
  results.push(await safeRun("settings.json", () => checkHookRegistration()));
391
+ results.push(await safeRun("codex", () => checkCodex()));
342
392
  let latestTranscript = null;
343
393
  results.push(
344
394
  await safeRun("projects", async () => {
@@ -631,19 +681,20 @@ async function main(argv) {
631
681
  switch (cmd) {
632
682
  case "track": {
633
683
  const text = await readStdin();
684
+ const codex = rest.includes("--codex");
634
685
  try {
635
- const trackMod = await import("./track-76Q5CZM5.js");
636
- await trackMod.runTrack(text);
686
+ const trackMod = await import("./track-PXFSGMGL.js");
687
+ await trackMod.runTrack(text, { codex });
637
688
  } catch {
638
689
  }
639
690
  return 0;
640
691
  }
641
692
  case "init": {
642
- const { runInit } = await import("./setup-IXPUR4CM.js");
693
+ const { runInit } = await import("./setup-PTO5N46B.js");
643
694
  return await runInit(rest);
644
695
  }
645
696
  case "uninstall": {
646
- const { runUninstall } = await import("./setup-IXPUR4CM.js");
697
+ const { runUninstall } = await import("./setup-PTO5N46B.js");
647
698
  return await runUninstall(rest);
648
699
  }
649
700
  case "doctor":
@@ -651,27 +702,27 @@ async function main(argv) {
651
702
  case "report":
652
703
  return await runReport(rest);
653
704
  case "dashboard": {
654
- const { runDashboard } = await import("./dashboard-LKK6NOBN.js");
705
+ const { runDashboard } = await import("./dashboard-TH3NOVT3.js");
655
706
  return await runDashboard(rest);
656
707
  }
657
708
  case "sweep": {
658
- const { runSweep } = await import("./sweep-XKKODGMA.js");
709
+ const { runSweep } = await import("./sweep-25EZAOGC.js");
659
710
  return await runSweep(rest);
660
711
  }
661
712
  case "history": {
662
- const { runHistory } = await import("./history-MWFHO5VR.js");
713
+ const { runHistory } = await import("./history-DLFYQJFM.js");
663
714
  return await runHistory(rest);
664
715
  }
665
716
  case "budget": {
666
- const { runBudget } = await import("./budget-5H2GQRXH.js");
717
+ const { runBudget } = await import("./budget-KKTFYAXH.js");
667
718
  return runBudget(rest);
668
719
  }
669
720
  case "mute": {
670
- const { runMute } = await import("./mute-2JW3NQXN.js");
721
+ const { runMute } = await import("./mute-EPLYH67P.js");
671
722
  return runMute(rest);
672
723
  }
673
724
  case "unmute": {
674
- const { runUnmute } = await import("./mute-2JW3NQXN.js");
725
+ const { runUnmute } = await import("./mute-EPLYH67P.js");
675
726
  return runUnmute();
676
727
  }
677
728
  case "--version":
@@ -3,10 +3,10 @@ import {
3
3
  browserOpenPlan,
4
4
  runDashboard,
5
5
  writeDashboardHtml
6
- } from "./chunk-64N5SGTT.js";
6
+ } from "./chunk-QX5KIRSU.js";
7
7
  import "./chunk-DGXUSPS4.js";
8
- import "./chunk-TBFKGFZX.js";
9
- import "./chunk-IIYMGLV4.js";
8
+ import "./chunk-J5QAYTFE.js";
9
+ import "./chunk-ECADO26T.js";
10
10
  export {
11
11
  browserOpenPlan,
12
12
  runDashboard,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  paths
4
- } from "./chunk-IIYMGLV4.js";
4
+ } from "./chunk-ECADO26T.js";
5
5
 
6
6
  // src/history.ts
7
7
  import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from "fs";
@@ -3,8 +3,8 @@ import {
3
3
  fmtMuteUntil,
4
4
  runMute,
5
5
  runUnmute
6
- } from "./chunk-KUJZYZSG.js";
7
- import "./chunk-IIYMGLV4.js";
6
+ } from "./chunk-4RZ6OGTD.js";
7
+ import "./chunk-ECADO26T.js";
8
8
  export {
9
9
  fmtMuteUntil,
10
10
  runMute,
@@ -3,11 +3,12 @@ import {
3
3
  matchesMarker,
4
4
  runInit,
5
5
  runUninstall
6
- } from "./chunk-OXMHOVUL.js";
7
- import "./chunk-4JNZ7BHO.js";
6
+ } from "./chunk-7KVOQ4UZ.js";
7
+ import "./chunk-NV5UOHJA.js";
8
8
  import "./chunk-DGXUSPS4.js";
9
- import "./chunk-TBFKGFZX.js";
10
- import "./chunk-IIYMGLV4.js";
9
+ import "./chunk-HTYUYKFW.js";
10
+ import "./chunk-J5QAYTFE.js";
11
+ import "./chunk-ECADO26T.js";
11
12
  export {
12
13
  matchesMarker,
13
14
  runInit,