ccgauge 1.0.2 → 1.0.3

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.
Files changed (64) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +26 -26
  3. package/.next/standalone/.next/app-path-routes-manifest.json +7 -7
  4. package/.next/standalone/.next/build-manifest.json +2 -2
  5. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/api/blocks/route.js +1 -1
  7. package/.next/standalone/.next/server/app/api/blocks/route.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/api/blocks/route_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/api/export/usage/route.js +1 -1
  10. package/.next/standalone/.next/server/app/api/export/usage/route.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/api/export/usage/route_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
  14. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/api/scan/route.js +1 -1
  17. package/.next/standalone/.next/server/app/api/scan/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/scan/route_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
  20. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/api/usage/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/usage/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/usage/route_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/models/page.js +2 -2
  26. package/.next/standalone/.next/server/app/models/page.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/models/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/page.js +2 -2
  29. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/projects/[id]/page.js +2 -2
  32. package/.next/standalone/.next/server/app/projects/[id]/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/projects/[id]/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  35. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/sessions/[id]/page.js +2 -2
  38. package/.next/standalone/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/sessions/page.js +1 -1
  41. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  44. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/usage/page.js +2 -2
  47. package/.next/standalone/.next/server/app/usage/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app-paths-manifest.json +7 -7
  50. package/.next/standalone/.next/server/chunks/971.js +1 -1
  51. package/.next/standalone/.next/server/chunks/98.js +1 -0
  52. package/.next/standalone/.next/server/functions-config-manifest.json +2 -2
  53. package/.next/standalone/.next/server/pages/500.html +1 -1
  54. package/.next/standalone/package.json +1 -1
  55. package/CHANGELOG.md +148 -0
  56. package/README.md +19 -6
  57. package/README.zh-CN.md +19 -6
  58. package/bin/cli.mjs +37 -17
  59. package/dist/mcp/server.mjs +40 -23699
  60. package/dist/report/index.mjs +38 -18
  61. package/package.json +1 -1
  62. package/.next/standalone/.next/server/chunks/155.js +0 -1
  63. /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → alqi5oQtTQUdpxp2x0yAt}/_buildManifest.js +0 -0
  64. /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → alqi5oQtTQUdpxp2x0yAt}/_ssgManifest.js +0 -0
@@ -812,7 +812,6 @@ function isProviderId(v) {
812
812
  // lib/data-loader/indexer.ts
813
813
  import { promises as fs3, watch as fsWatch } from "node:fs";
814
814
  import path4 from "node:path";
815
- import os4 from "node:os";
816
815
 
817
816
  // lib/dedup.ts
818
817
  function dedupKey(r) {
@@ -946,6 +945,15 @@ function linkSidechainParents({
946
945
  return stats;
947
946
  }
948
947
 
948
+ // lib/sanitize.ts
949
+ import { homedir } from "node:os";
950
+ function sanitizeForUser(s) {
951
+ const home = homedir();
952
+ if (!home) return s;
953
+ const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
954
+ return s.replace(new RegExp(escaped, "g"), "~");
955
+ }
956
+
949
957
  // lib/data-loader/indexer.ts
950
958
  var RECONCILE_DEBOUNCE_MS = 200;
951
959
  var SNAPSHOT_REBUILD_DEBOUNCE_MS = 100;
@@ -1112,6 +1120,10 @@ var FileIndexer = class {
1112
1120
  }
1113
1121
  setupPolling() {
1114
1122
  if (this.pollTimer) clearInterval(this.pollTimer);
1123
+ const isMcp = this.cacheName === "mcp";
1124
+ const envOpt = process.env.CCGAUGE_POLL_FALLBACK;
1125
+ const enable = envOpt === "1" ? true : envOpt === "0" ? false : !isMcp;
1126
+ if (!enable) return;
1115
1127
  this.pollTimer = setInterval(() => {
1116
1128
  this.pollOnce().catch((err) => this.recordError(`poll: ${err.message}`));
1117
1129
  }, POLL_INTERVAL_MS);
@@ -1394,12 +1406,6 @@ var FileIndexer = class {
1394
1406
  this.fileDebouncers.clear();
1395
1407
  }
1396
1408
  };
1397
- function sanitizeForUser(s) {
1398
- const home = os4.homedir();
1399
- if (!home) return s;
1400
- const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1401
- return s.replace(new RegExp(escaped, "g"), "~");
1402
- }
1403
1409
  async function dirExists(p) {
1404
1410
  try {
1405
1411
  const s = await fs3.stat(p);
@@ -1541,18 +1547,28 @@ function bucketKey(ts, gran) {
1541
1547
  }
1542
1548
  return { key: `${yyyy}-${mm}`, label: `${yyyy}-${mm}` };
1543
1549
  }
1544
- function withinRange(rec, opts) {
1545
- if (rec.source !== opts.source) return false;
1546
- if (opts.from && rec.timestamp < opts.from.toISOString()) return false;
1547
- if (opts.to && rec.timestamp > opts.to.toISOString()) return false;
1548
- if (opts.models && opts.models.length && !opts.models.includes(rec.model)) return false;
1549
- if (opts.projects && opts.projects.length && !opts.projects.includes(rec.cwd)) return false;
1550
+ function prepareOpts(opts) {
1551
+ return {
1552
+ source: opts.source,
1553
+ fromIso: opts.from?.toISOString(),
1554
+ toIso: opts.to?.toISOString(),
1555
+ models: opts.models && opts.models.length ? new Set(opts.models) : void 0,
1556
+ projects: opts.projects && opts.projects.length ? new Set(opts.projects) : void 0
1557
+ };
1558
+ }
1559
+ function withinRangePrepared(rec, p) {
1560
+ if (rec.source !== p.source) return false;
1561
+ if (p.fromIso && rec.timestamp < p.fromIso) return false;
1562
+ if (p.toIso && rec.timestamp > p.toIso) return false;
1563
+ if (p.models && !p.models.has(rec.model)) return false;
1564
+ if (p.projects && !p.projects.has(rec.cwd)) return false;
1550
1565
  return true;
1551
1566
  }
1552
1567
  function aggregateByTime(records, gran, opts) {
1553
1568
  const buckets = /* @__PURE__ */ new Map();
1569
+ const prepared = prepareOpts(opts);
1554
1570
  for (const rec of records) {
1555
- if (!withinRange(rec, opts)) continue;
1571
+ if (!withinRangePrepared(rec, prepared)) continue;
1556
1572
  const { key, label } = bucketKey(rec.timestamp, gran);
1557
1573
  let b = buckets.get(key);
1558
1574
  if (!b) {
@@ -1596,8 +1612,9 @@ function pushRecord(b, rec) {
1596
1612
  }
1597
1613
  function aggregateByModel(records, opts) {
1598
1614
  const map = /* @__PURE__ */ new Map();
1615
+ const prepared = prepareOpts(opts);
1599
1616
  for (const rec of records) {
1600
- if (!withinRange(rec, opts)) continue;
1617
+ if (!withinRangePrepared(rec, prepared)) continue;
1601
1618
  let s = map.get(rec.model);
1602
1619
  if (!s) {
1603
1620
  const { pricing, matchType } = getProvider(rec.source).resolvePricing(rec.model);
@@ -1632,8 +1649,9 @@ function aggregateByModel(records, opts) {
1632
1649
  function aggregateByProject(records, opts) {
1633
1650
  const map = /* @__PURE__ */ new Map();
1634
1651
  const sessionsByProject = /* @__PURE__ */ new Map();
1652
+ const prepared = prepareOpts(opts);
1635
1653
  for (const rec of records) {
1636
- if (!withinRange(rec, opts)) continue;
1654
+ if (!withinRangePrepared(rec, prepared)) continue;
1637
1655
  const cwd = rec.cwd || "(unknown)";
1638
1656
  let s = map.get(cwd);
1639
1657
  if (!s) {
@@ -1679,8 +1697,9 @@ function aggregateByProject(records, opts) {
1679
1697
  }
1680
1698
  function aggregateBySession(records, userRecords, opts) {
1681
1699
  const map = /* @__PURE__ */ new Map();
1700
+ const prepared = prepareOpts(opts);
1682
1701
  for (const rec of records) {
1683
- if (!withinRange(rec, opts)) continue;
1702
+ if (!withinRangePrepared(rec, prepared)) continue;
1684
1703
  const sid = rec.sessionId || rec.uuid;
1685
1704
  let s = map.get(sid);
1686
1705
  if (!s) {
@@ -1748,8 +1767,9 @@ function aggregateTotals(records, opts) {
1748
1767
  let cost = 0;
1749
1768
  let saved = 0;
1750
1769
  let requests = 0;
1770
+ const prepared = prepareOpts(opts);
1751
1771
  for (const rec of records) {
1752
- if (!withinRange(rec, opts)) continue;
1772
+ if (!withinRangePrepared(rec, prepared)) continue;
1753
1773
  const c = costOfRecord(rec);
1754
1774
  inputTokens += rec.usage.input_tokens;
1755
1775
  outputTokens += rec.usage.output_tokens;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgauge",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Local web dashboard for Claude Code and OpenAI Codex CLI token usage and cost",
5
5
  "keywords": [
6
6
  "claude",
@@ -1 +0,0 @@
1
- "use strict";exports.id=155,exports.ids=[155],exports.modules={20155:(a,b,c)=>{c.d(b,{So:()=>s,mo:()=>u,TF:()=>t});var d=c(60704),e=c(73024),f=c(76760),g=c.n(f),h=c(48161),i=c.n(h);let j="default";function k(a){let b=a===j?"index-v2.json":`index-${a}-v2.json`;return g().join(process.env.CCGAUGE_STATE_DIR?process.env.CCGAUGE_STATE_DIR:g().join(i().homedir(),".ccgauge"),"cache",b)}async function l(a=j){let b=k(a);try{let a=await e.promises.readFile(b,"utf8"),c=JSON.parse(a);if(2!==c.schemaVersion||!Array.isArray(c.files))return null;return c}catch{return null}}async function m(a,b=j){let c=k(b),d=g().dirname(c);await e.promises.mkdir(d,{recursive:!0});let f={schemaVersion:2,savedAt:a.savedAt,files:a.files},h=`${c}.tmp-${process.pid}`;await e.promises.writeFile(h,JSON.stringify(f)),await e.promises.rename(h,c)}let n=/\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/subagents\/agent-[^/]+\.jsonl$/i;class o{constructor(a=j){this.files=new Map,this.snapshot=null,this.watchers=new Map,this.pollTimer=null,this.snapshotRebuildTimer=null,this.persistTimer=null,this.fileDebouncers=new Map,this.initPromise=null,this.isIndexing=!1,this.lastIndexedAt=null,this.indexDurationMs=null,this.existingDirs=[],this.dirToProvider=new Map,this.errors=[],this.loadedFromDisk=!1,this.lastWorkStart=null,this.rescanPromise=null,this.cacheName=a}init(){return this.initPromise||(this.initPromise=this.doInit()),this.initPromise}async doInit(){let a=Date.now();this.isIndexing=!0,this.lastWorkStart=a;try{await this.detectProviderDirs();let b=await l(this.cacheName);this.loadedFromDisk=null!==b;let c=new Map;if(b)for(let a of b.files)c.set(a.filePath,a);await this.fullScan(c),this.rebuildSnapshotNow(),this.indexDurationMs=Date.now()-a,this.lastIndexedAt=new Date().toISOString(),this.syncWatchersToDirs(),this.setupPolling(),this.schedulePersist()}finally{this.isIndexing=!1}}async detectProviderDirs(){let a=new Map,b=[];for(let c of(0,d.R0)())for(let d of c.getDirs())await p(d)&&!a.has(d)&&(a.set(d,c),b.push(d));let c=[],e=[];for(let b of a.keys())this.dirToProvider.has(b)||c.push(b);for(let b of this.dirToProvider.keys())a.has(b)||e.push(b);return this.dirToProvider=a,this.existingDirs=b,{added:c,removed:e}}async fullScan(a){let b=[];for(let[a,c]of this.dirToProvider)for(let d of(await q(a,c)))b.push({file:d,provider:c});let c=new Set(b.map(a=>a.file));for(let d of(await Promise.all(b.map(async({file:b,provider:c})=>{try{let d=await e.promises.stat(b),f=a.get(b);if(f&&f.source===c.id&&f.parserVersion===c.parserVersion&&f.mtimeMs===d.mtimeMs&&f.size===d.size)return void this.files.set(b,{source:c.id,parserVersion:c.parserVersion,mtimeMs:d.mtimeMs,size:d.size,assistantRecords:f.assistantRecords,userRecords:f.userRecords,parentLinks:f.parentLinks});let g=await c.parseFile(b);this.files.set(b,{source:c.id,parserVersion:c.parserVersion,mtimeMs:d.mtimeMs,size:d.size,assistantRecords:g.assistant,userRecords:g.user,parentLinks:g.parentLinks})}catch(a){this.recordError(`parse ${b}: ${a.message}`)}})),Array.from(this.files.keys())))c.has(d)||this.files.delete(d)}syncWatchersToDirs(){for(let[a,b]of this.watchers)if(!this.dirToProvider.has(a)){try{b.close()}catch{}this.watchers.delete(a)}for(let[a,b]of this.dirToProvider)if(!this.watchers.has(a))try{let c=(0,e.watch)(a,{recursive:!0},(c,d)=>{if(!d||"string"!=typeof d||!d.endsWith(".jsonl"))return;let e=g().join(a,d);this.scheduleFileReconcile(e,b)});c.on("error",b=>{this.recordError(`watcher ${a}: ${b.message}`)}),this.watchers.set(a,c)}catch(b){this.recordError(`watch ${a}: ${b.message}`)}}setupPolling(){this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=setInterval(()=>{this.pollOnce().catch(a=>this.recordError(`poll: ${a.message}`))},3e4),this.pollTimer.unref?.()}async pollOnce(){let a=Date.now(),b=await this.detectProviderDirs(),c=b.added.length>0||b.removed.length>0;c&&this.syncWatchersToDirs();let d=[];for(let[a,b]of this.dirToProvider)for(let c of(await q(a,b)))d.push({file:c,provider:b});let f=new Set(d.map(a=>a.file));for(let a of(await Promise.all(d.map(async({file:a,provider:b})=>{try{let d=await e.promises.stat(a),f=this.files.get(a);if(f&&f.mtimeMs===d.mtimeMs&&f.size===d.size)return;let g=await b.parseFile(a);this.files.set(a,{source:b.id,parserVersion:b.parserVersion,mtimeMs:d.mtimeMs,size:d.size,assistantRecords:g.assistant,userRecords:g.user,parentLinks:g.parentLinks}),c=!0}catch(b){this.recordError(`poll-parse ${a}: ${b.message}`)}})),Array.from(this.files.keys())))f.has(a)||(this.files.delete(a),c=!0);c&&(this.lastWorkStart=a,this.scheduleSnapshotRebuild(),this.schedulePersist())}scheduleFileReconcile(a,b){let c=this.fileDebouncers.get(a);c&&clearTimeout(c);let d=setTimeout(()=>{this.fileDebouncers.delete(a),this.reconcileFile(a,b).catch(b=>this.recordError(`reconcile ${a}: ${b.message}`))},200);d.unref?.(),this.fileDebouncers.set(a,d)}async reconcileFile(a,b){let c=Date.now(),d=null;try{d=await e.promises.stat(a)}catch{}if(!d||!d.isFile()){this.files.has(a)&&(this.files.delete(a),this.lastWorkStart=c,this.scheduleSnapshotRebuild(),this.schedulePersist());return}let f=this.files.get(a);if(!f||f.mtimeMs!==d.mtimeMs||f.size!==d.size)try{let e=await b.parseFile(a);this.files.set(a,{source:b.id,parserVersion:b.parserVersion,mtimeMs:d.mtimeMs,size:d.size,assistantRecords:e.assistant,userRecords:e.user,parentLinks:e.parentLinks}),this.lastWorkStart=c,this.scheduleSnapshotRebuild(),this.schedulePersist()}catch(b){this.recordError(`parse ${a}: ${b.message}`)}}scheduleSnapshotRebuild(){this.snapshotRebuildTimer&&clearTimeout(this.snapshotRebuildTimer),this.snapshotRebuildTimer=setTimeout(()=>{this.snapshotRebuildTimer=null,this.rebuildSnapshotNow()},100),this.snapshotRebuildTimer.unref?.()}rebuildSnapshotNow(){let a=Date.now(),b=this.lastWorkStart??a,c=[],d=[],e={},f=0,g={claude:{source:"claude",filesScanned:0,recordsParsed:0,assistantRecords:0,scannedDirs:[]},codex:{source:"codex",filesScanned:0,recordsParsed:0,assistantRecords:0,scannedDirs:[]}};for(let[a,b]of this.dirToProvider)g[b.id].scannedDirs.push(a);for(let a of this.files.values()){for(let[b,f]of(c.push(...a.assistantRecords),d.push(...a.userRecords),a.parentLinks))e[b]=f;f+=a.assistantRecords.length+a.userRecords.length,g[a.source].filesScanned+=1,g[a.source].recordsParsed+=a.assistantRecords.length+a.userRecords.length}let h=(function(a){let b=new Map;for(let c of a){let a=function(a){let b=`${a.source}:`;return a.messageId&&a.requestId?`${b}${a.messageId}::${a.requestId}`:a.messageId?`${b}mid:${a.messageId}`:a.requestId?`${b}req:${a.requestId}`:`${b}uuid:${a.uuid}`}(c),d=b.get(a);if(!d){b.set(a,c);continue}c.timestamp<d.timestamp&&b.set(a,c)}return Array.from(b.values())})(c).sort((a,b)=>a.timestamp.localeCompare(b.timestamp)),i=(function(a){let b=new Set;return a.filter(a=>{let c=a.uuid;return!c||!b.has(c)&&(b.add(c),!0)})})(d).sort((a,b)=>a.timestamp.localeCompare(b.timestamp));for(let a of(!function({assistantRecords:a,userRecords:b,parentMap:c}){let d=new Map;for(let b of a){if(b.isSidechain||!b.sessionId)continue;let a=d.get(b.sessionId);a||(a=[],d.set(b.sessionId,a)),a.push(b)}for(let a of d.values())a.sort((a,b)=>a.timestamp<b.timestamp?-1:+(a.timestamp>b.timestamp));let e=new Map;for(let a of b){if(!a.isSidechain)continue;let b=e.get(a.filePath);(!b||a.timestamp<b.timestamp)&&e.set(a.filePath,a)}let f={subagentFiles:0,relinked:0,orphans:0,alreadyLinked:0};for(let[a,b]of e){let e,g=function(a){let b=n.exec(a);return b?b[1]:null}(a);if(!g)continue;if(f.subagentFiles+=1,null!=c[b.uuid]){f.alreadyLinked+=1;continue}let h=d.get(g);if(!h||0===h.length){f.orphans+=1;continue}let i=b.timestamp;for(let a=h.length-1;a>=0;a-=1)if(h[a].timestamp<=i){e=h[a];break}e||(e=h[0]),c[b.uuid]=e.uuid,f.relinked+=1}}({assistantRecords:h,userRecords:i,parentMap:e}),h))g[a.source].assistantRecords+=1;let j={filesScanned:this.files.size,recordsParsed:f,assistantRecords:h.length,durationMs:Date.now()-b,scannedDirs:this.existingDirs};this.snapshot={records:h,userRecords:i,parentMap:e,stats:j,bySource:Object.values(g)},this.lastWorkStart=null}getSnapshot(){if(!this.snapshot)throw Error("Indexer not initialized — call init() first.");return this.snapshot}async forceRescan(){if(!this.initPromise)return await this.init(),this.snapshot;if(this.rescanPromise)return this.rescanPromise;this.rescanPromise=this.runRescan();try{return await this.rescanPromise}finally{this.rescanPromise=null}}async runRescan(){let a=Date.now();this.isIndexing=!0,this.lastWorkStart=a;try{return this.files.clear(),await this.detectProviderDirs(),await this.fullScan(new Map),this.rebuildSnapshotNow(),this.indexDurationMs=Date.now()-a,this.lastIndexedAt=new Date().toISOString(),this.syncWatchersToDirs(),this.schedulePersist(),this.snapshot}finally{this.isIndexing=!1}}getStatus(){let a=this.snapshot;return{initialized:null!==a,isIndexing:this.isIndexing,lastIndexedAt:this.lastIndexedAt,indexDurationMs:this.indexDurationMs,filesIndexed:this.files.size,recordsIndexed:a?.stats.assistantRecords??0,bySource:a?.bySource??[],watchers:this.watchers.size,errors:this.errors.slice(-20),pendingReconciles:this.fileDebouncers.size,loadedFromDisk:this.loadedFromDisk}}schedulePersist(){this.persistTimer&&clearTimeout(this.persistTimer),this.persistTimer=setTimeout(()=>{this.persistTimer=null;let a=[];for(let[b,c]of this.files)a.push({filePath:b,source:c.source,parserVersion:c.parserVersion,mtimeMs:c.mtimeMs,size:c.size,assistantRecords:c.assistantRecords,userRecords:c.userRecords,parentLinks:c.parentLinks});m({savedAt:new Date().toISOString(),files:a},this.cacheName).catch(a=>this.recordError(`persist: ${a.message}`))},2e3),this.persistTimer.unref?.()}recordError(a){let b=`${new Date().toISOString()} ${function(a){let b=i().homedir();if(!b)return a;let c=b.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return a.replace(RegExp(c,"g"),"~")}(a)}`;this.errors.push(b),this.errors.length>40&&this.errors.splice(0,this.errors.length-20),process.env.CCGAUGE_DEBUG&&console.error(`[ccgauge:indexer] ${new Date().toISOString()} ${a}`)}disposeWatchers(){for(let a of this.watchers.values())try{a.close()}catch{}this.watchers.clear()}dispose(){for(let a of(this.disposeWatchers(),this.pollTimer&&clearInterval(this.pollTimer),this.snapshotRebuildTimer&&clearTimeout(this.snapshotRebuildTimer),this.persistTimer&&clearTimeout(this.persistTimer),this.fileDebouncers.values()))clearTimeout(a);this.fileDebouncers.clear()}}async function p(a){try{return(await e.promises.stat(a)).isDirectory()}catch{return!1}}async function q(a,b){let c=[];async function d(a,f){let h;if(!(f>8)){try{h=await e.promises.readdir(a,{withFileTypes:!0})}catch{return}for(let e of h){let h=g().join(a,e.name);if(e.isDirectory()){if(b.shouldSkipDir(e.name))continue;await d(h,f+1)}else e.isFile()&&e.name.endsWith(".jsonl")&&c.push(h)}}}return await d(a,0),c}let r=function(a=j){let b=(globalThis.__ccgaugeIndexers||(globalThis.__ccgaugeIndexers=new Map),globalThis.__ccgaugeIndexers),c=b.get(a);return c||(c=new o(a),b.set(a,c)),c}();async function s(a={}){return a.force?r.forceRescan():(await r.init(),r.getSnapshot())}function t(){return(0,d.R0)().map(a=>({source:a.id,dirs:a.getDirs()}))}function u(){return r.getStatus()}}};