@clauderecallhq/cli 0.0.1 → 0.12.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.
Files changed (279) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +543 -3
  3. package/README.public.md +523 -0
  4. package/dist/cli.js +362 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/activate.js +69 -0
  7. package/dist/commands/activate.js.map +1 -0
  8. package/dist/commands/audit-secrets.js +103 -0
  9. package/dist/commands/audit-secrets.js.map +1 -0
  10. package/dist/commands/blame.js +35 -0
  11. package/dist/commands/blame.js.map +1 -0
  12. package/dist/commands/config-verification.js +18 -0
  13. package/dist/commands/config-verification.js.map +1 -0
  14. package/dist/commands/context.js +144 -0
  15. package/dist/commands/context.js.map +1 -0
  16. package/dist/commands/correlate.js +70 -0
  17. package/dist/commands/correlate.js.map +1 -0
  18. package/dist/commands/digest.js +78 -0
  19. package/dist/commands/digest.js.map +1 -0
  20. package/dist/commands/health.js +62 -0
  21. package/dist/commands/health.js.map +1 -0
  22. package/dist/commands/index.js +247 -0
  23. package/dist/commands/index.js.map +1 -0
  24. package/dist/commands/install-extension.js +138 -0
  25. package/dist/commands/install-extension.js.map +1 -0
  26. package/dist/commands/installs.js +128 -0
  27. package/dist/commands/installs.js.map +1 -0
  28. package/dist/commands/license.js +39 -0
  29. package/dist/commands/license.js.map +1 -0
  30. package/dist/commands/list.js +47 -0
  31. package/dist/commands/list.js.map +1 -0
  32. package/dist/commands/mcp.js +29 -0
  33. package/dist/commands/mcp.js.map +1 -0
  34. package/dist/commands/open.js +28 -0
  35. package/dist/commands/open.js.map +1 -0
  36. package/dist/commands/paste.js +154 -0
  37. package/dist/commands/paste.js.map +1 -0
  38. package/dist/commands/projects.js +36 -0
  39. package/dist/commands/projects.js.map +1 -0
  40. package/dist/commands/search.js +67 -0
  41. package/dist/commands/search.js.map +1 -0
  42. package/dist/commands/semantic.js +173 -0
  43. package/dist/commands/semantic.js.map +1 -0
  44. package/dist/commands/show.js +121 -0
  45. package/dist/commands/show.js.map +1 -0
  46. package/dist/commands/start.js +47 -0
  47. package/dist/commands/start.js.map +1 -0
  48. package/dist/commands/stats.js +133 -0
  49. package/dist/commands/stats.js.map +1 -0
  50. package/dist/commands/status.js +45 -0
  51. package/dist/commands/status.js.map +1 -0
  52. package/dist/commands/stop.js +29 -0
  53. package/dist/commands/stop.js.map +1 -0
  54. package/dist/commands/thread.js +396 -0
  55. package/dist/commands/thread.js.map +1 -0
  56. package/dist/context/formatter.js +103 -0
  57. package/dist/context/formatter.js.map +1 -0
  58. package/dist/daemon/auto-tag-config.js +103 -0
  59. package/dist/daemon/auto-tag-config.js.map +1 -0
  60. package/dist/daemon/auto-tag-config.test.js +72 -0
  61. package/dist/daemon/auto-tag-config.test.js.map +1 -0
  62. package/dist/daemon/auto-title-config.js +70 -0
  63. package/dist/daemon/auto-title-config.js.map +1 -0
  64. package/dist/daemon/bulk-title-jobs.js +170 -0
  65. package/dist/daemon/bulk-title-jobs.js.map +1 -0
  66. package/dist/daemon/correlator.js +320 -0
  67. package/dist/daemon/correlator.js.map +1 -0
  68. package/dist/daemon/discover.js +316 -0
  69. package/dist/daemon/discover.js.map +1 -0
  70. package/dist/daemon/editor-detection.js +186 -0
  71. package/dist/daemon/editor-detection.js.map +1 -0
  72. package/dist/daemon/entrypoint.js +55 -0
  73. package/dist/daemon/entrypoint.js.map +1 -0
  74. package/dist/daemon/git-correlator.js +256 -0
  75. package/dist/daemon/git-correlator.js.map +1 -0
  76. package/dist/daemon/mcp-installer.js +108 -0
  77. package/dist/daemon/mcp-installer.js.map +1 -0
  78. package/dist/daemon/onboarding-state.js +140 -0
  79. package/dist/daemon/onboarding-state.js.map +1 -0
  80. package/dist/daemon/pidfile.js +57 -0
  81. package/dist/daemon/pidfile.js.map +1 -0
  82. package/dist/daemon/ports.js +48 -0
  83. package/dist/daemon/ports.js.map +1 -0
  84. package/dist/daemon/scanProgressRegistry.js +62 -0
  85. package/dist/daemon/scanProgressRegistry.js.map +1 -0
  86. package/dist/daemon/server.js +2010 -0
  87. package/dist/daemon/server.js.map +1 -0
  88. package/dist/daemon/tag-scanner/anthropic-client.js +40 -0
  89. package/dist/daemon/tag-scanner/anthropic-client.js.map +1 -0
  90. package/dist/daemon/tag-scanner/autopilot.js +131 -0
  91. package/dist/daemon/tag-scanner/autopilot.js.map +1 -0
  92. package/dist/daemon/tag-scanner/claude-cli-driver.js +250 -0
  93. package/dist/daemon/tag-scanner/claude-cli-driver.js.map +1 -0
  94. package/dist/daemon/tag-scanner/orchestrator.js +88 -0
  95. package/dist/daemon/tag-scanner/orchestrator.js.map +1 -0
  96. package/dist/daemon/tag-scanner/prompt.js +46 -0
  97. package/dist/daemon/tag-scanner/prompt.js.map +1 -0
  98. package/dist/daemon/tag-scanner/prompt.test.js +48 -0
  99. package/dist/daemon/tag-scanner/prompt.test.js.map +1 -0
  100. package/dist/daemon/tag-scanner/scan-state.js +49 -0
  101. package/dist/daemon/tag-scanner/scan-state.js.map +1 -0
  102. package/dist/daemon/tag-scanner/session-fetcher.js +82 -0
  103. package/dist/daemon/tag-scanner/session-fetcher.js.map +1 -0
  104. package/dist/daemon/tag-scanner/session-fetcher.test.js +34 -0
  105. package/dist/daemon/tag-scanner/session-fetcher.test.js.map +1 -0
  106. package/dist/daemon/tag-scanner/validator.js +50 -0
  107. package/dist/daemon/tag-scanner/validator.js.map +1 -0
  108. package/dist/daemon/tag-scanner/validator.test.js +41 -0
  109. package/dist/daemon/tag-scanner/validator.test.js.map +1 -0
  110. package/dist/daemon/terminal-registry.js +443 -0
  111. package/dist/daemon/terminal-registry.js.map +1 -0
  112. package/dist/daemon/ui.js +64 -0
  113. package/dist/daemon/ui.js.map +1 -0
  114. package/dist/daemon/watcher.js +256 -0
  115. package/dist/daemon/watcher.js.map +1 -0
  116. package/dist/db/client.js +22 -0
  117. package/dist/db/client.js.map +1 -0
  118. package/dist/db/schema.js +496 -0
  119. package/dist/db/schema.js.map +1 -0
  120. package/dist/license/api-base.js +13 -0
  121. package/dist/license/api-base.js.map +1 -0
  122. package/dist/license/manager.js +43 -0
  123. package/dist/license/manager.js.map +1 -0
  124. package/dist/license/public-key.js +19 -0
  125. package/dist/license/public-key.js.map +1 -0
  126. package/dist/license/storage.js +27 -0
  127. package/dist/license/storage.js.map +1 -0
  128. package/dist/license/verify.js +23 -0
  129. package/dist/license/verify.js.map +1 -0
  130. package/dist/mcp/audit.js +126 -0
  131. package/dist/mcp/audit.js.map +1 -0
  132. package/dist/mcp/prompts.js +180 -0
  133. package/dist/mcp/prompts.js.map +1 -0
  134. package/dist/mcp/server.js +502 -0
  135. package/dist/mcp/server.js.map +1 -0
  136. package/dist/mcp/thread-tools.js +363 -0
  137. package/dist/mcp/thread-tools.js.map +1 -0
  138. package/dist/mcp/write-tools.js +239 -0
  139. package/dist/mcp/write-tools.js.map +1 -0
  140. package/dist/parser/jsonl.js +150 -0
  141. package/dist/parser/jsonl.js.map +1 -0
  142. package/dist/semantic/chunker.js +47 -0
  143. package/dist/semantic/chunker.js.map +1 -0
  144. package/dist/semantic/config.js +74 -0
  145. package/dist/semantic/config.js.map +1 -0
  146. package/dist/semantic/embedder.js +54 -0
  147. package/dist/semantic/embedder.js.map +1 -0
  148. package/dist/semantic/fusion.js +38 -0
  149. package/dist/semantic/fusion.js.map +1 -0
  150. package/dist/semantic/model-download.js +69 -0
  151. package/dist/semantic/model-download.js.map +1 -0
  152. package/dist/semantic/pipeline.js +375 -0
  153. package/dist/semantic/pipeline.js.map +1 -0
  154. package/dist/semantic/query.js +42 -0
  155. package/dist/semantic/query.js.map +1 -0
  156. package/dist/semantic/worker.js +78 -0
  157. package/dist/semantic/worker.js.map +1 -0
  158. package/dist/stats/backfill.js +151 -0
  159. package/dist/stats/backfill.js.map +1 -0
  160. package/dist/stats/health.js +102 -0
  161. package/dist/stats/health.js.map +1 -0
  162. package/dist/stats/query.js +385 -0
  163. package/dist/stats/query.js.map +1 -0
  164. package/dist/utils/aliases.js +107 -0
  165. package/dist/utils/aliases.js.map +1 -0
  166. package/dist/utils/autoCollections.js +635 -0
  167. package/dist/utils/autoCollections.js.map +1 -0
  168. package/dist/utils/autoTitle.js +348 -0
  169. package/dist/utils/autoTitle.js.map +1 -0
  170. package/dist/utils/collections.js +446 -0
  171. package/dist/utils/collections.js.map +1 -0
  172. package/dist/utils/format.js +46 -0
  173. package/dist/utils/format.js.map +1 -0
  174. package/dist/utils/notes.js +270 -0
  175. package/dist/utils/notes.js.map +1 -0
  176. package/dist/utils/paths.js +50 -0
  177. package/dist/utils/paths.js.map +1 -0
  178. package/dist/utils/pricing.js +257 -0
  179. package/dist/utils/pricing.js.map +1 -0
  180. package/dist/utils/secret-scanner.js +166 -0
  181. package/dist/utils/secret-scanner.js.map +1 -0
  182. package/dist/utils/sessionLabel.js +64 -0
  183. package/dist/utils/sessionLabel.js.map +1 -0
  184. package/dist/utils/tags.js +97 -0
  185. package/dist/utils/tags.js.map +1 -0
  186. package/dist/utils/thread-context.js +129 -0
  187. package/dist/utils/thread-context.js.map +1 -0
  188. package/dist/utils/threadFilter.js +18 -0
  189. package/dist/utils/threadFilter.js.map +1 -0
  190. package/dist/utils/threads-titler.js +298 -0
  191. package/dist/utils/threads-titler.js.map +1 -0
  192. package/dist/utils/threads.js +383 -0
  193. package/dist/utils/threads.js.map +1 -0
  194. package/dist/utils/usage.js +76 -0
  195. package/dist/utils/usage.js.map +1 -0
  196. package/dist/verification/compute.js +88 -0
  197. package/dist/verification/compute.js.map +1 -0
  198. package/dist/verification/config.js +34 -0
  199. package/dist/verification/config.js.map +1 -0
  200. package/dist/web/assets/index-CIr6J4Fw.js +1201 -0
  201. package/dist/web/assets/index-Ctc8g9Jw.css +1 -0
  202. package/dist/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
  203. package/dist/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
  204. package/dist/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
  205. package/dist/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
  206. package/dist/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
  207. package/dist/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
  208. package/dist/web/assets/inter-cyrillic-700-normal-CjBOestx.woff2 +0 -0
  209. package/dist/web/assets/inter-cyrillic-700-normal-DrXBdSj3.woff +0 -0
  210. package/dist/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
  211. package/dist/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
  212. package/dist/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
  213. package/dist/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
  214. package/dist/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
  215. package/dist/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
  216. package/dist/web/assets/inter-cyrillic-ext-700-normal-BjwYoWNd.woff2 +0 -0
  217. package/dist/web/assets/inter-cyrillic-ext-700-normal-LO58E6JB.woff +0 -0
  218. package/dist/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
  219. package/dist/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
  220. package/dist/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
  221. package/dist/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
  222. package/dist/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
  223. package/dist/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
  224. package/dist/web/assets/inter-greek-700-normal-BUv2fZ6O.woff +0 -0
  225. package/dist/web/assets/inter-greek-700-normal-C3JjAnD8.woff2 +0 -0
  226. package/dist/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
  227. package/dist/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
  228. package/dist/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
  229. package/dist/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
  230. package/dist/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
  231. package/dist/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
  232. package/dist/web/assets/inter-greek-ext-700-normal-BoQ6DsYi.woff +0 -0
  233. package/dist/web/assets/inter-greek-ext-700-normal-qfdV9bQt.woff2 +0 -0
  234. package/dist/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  235. package/dist/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  236. package/dist/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  237. package/dist/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  238. package/dist/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
  239. package/dist/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  240. package/dist/web/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
  241. package/dist/web/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  242. package/dist/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  243. package/dist/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  244. package/dist/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
  245. package/dist/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
  246. package/dist/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
  247. package/dist/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
  248. package/dist/web/assets/inter-latin-ext-700-normal-Ca8adRJv.woff2 +0 -0
  249. package/dist/web/assets/inter-latin-ext-700-normal-TidjK2hL.woff +0 -0
  250. package/dist/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  251. package/dist/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  252. package/dist/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
  253. package/dist/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
  254. package/dist/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
  255. package/dist/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
  256. package/dist/web/assets/inter-vietnamese-700-normal-BZaoP0fm.woff +0 -0
  257. package/dist/web/assets/inter-vietnamese-700-normal-DlLaEgI2.woff2 +0 -0
  258. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  259. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  260. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  261. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  262. package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  263. package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  264. package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  265. package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  266. package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  267. package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  268. package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  269. package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  270. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  271. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  272. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  273. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  274. package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  275. package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  276. package/dist/web/favicon.svg +9 -0
  277. package/dist/web/index.html +15 -0
  278. package/package.json +80 -9
  279. package/bin/cli.js +0 -12
@@ -0,0 +1,151 @@
1
+ import { getDb } from '../db/client.js';
2
+ import { extractUsage } from '../parser/jsonl.js';
3
+ import { recomputeSessionRollup } from '../utils/usage.js';
4
+ const DEFAULT_CHUNK_SIZE = 500;
5
+ // Process-local set of message uuids whose raw_json is valid but carries
6
+ // no usage block (aborted assistant turns, pre-v0.10 Claude Code output).
7
+ // Without this the backfill endpoint would rescan them every call even
8
+ // though there is nothing to recover. Persisting to disk would need a
9
+ // schema migration for ~60–100 rows on a well-used install; an in-memory
10
+ // set is enough — on daemon restart we re-scan once, cheap.
11
+ const unparsable = new Set();
12
+ let lastRun = null;
13
+ let running = false;
14
+ /** Count of messages we've already scanned and confirmed have no usage data. */
15
+ export function getUnrecoverableCount() {
16
+ return unparsable.size;
17
+ }
18
+ function selectPendingSql() {
19
+ return `
20
+ SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
21
+ FROM messages m
22
+ LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
23
+ WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
24
+ AND m.uuid NOT IN (SELECT value FROM json_each(?))
25
+ LIMIT ?
26
+ `;
27
+ }
28
+ function runChunked(db, options) {
29
+ const totalLimit = options.limit ?? Number.MAX_SAFE_INTEGER;
30
+ const chunkSize = Math.max(1, options.chunkSize ?? DEFAULT_CHUNK_SIZE);
31
+ const selectStmt = db.prepare(selectPendingSql());
32
+ const insertStmt = db.prepare(`
33
+ INSERT INTO message_usage (
34
+ message_uuid, session_id, model,
35
+ input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
36
+ timestamp
37
+ ) VALUES (
38
+ @uuid, @session_id, @model,
39
+ @input, @output, @cc, @cr, @ts
40
+ )
41
+ ON CONFLICT(message_uuid) DO NOTHING
42
+ `);
43
+ let scanned = 0;
44
+ let inserted = 0;
45
+ const allTouched = new Set();
46
+ while (scanned < totalLimit) {
47
+ const take = Math.min(chunkSize, totalLimit - scanned);
48
+ const skipList = JSON.stringify([...unparsable]);
49
+ const rows = selectStmt.all(skipList, take);
50
+ if (rows.length === 0)
51
+ break;
52
+ const chunkTouched = new Set();
53
+ const chunkTxn = db.transaction(() => {
54
+ for (const r of rows) {
55
+ let parsed;
56
+ try {
57
+ parsed = JSON.parse(r.raw_json);
58
+ }
59
+ catch {
60
+ unparsable.add(r.uuid);
61
+ continue;
62
+ }
63
+ const usage = extractUsage(parsed.message);
64
+ if (!usage) {
65
+ unparsable.add(r.uuid);
66
+ continue;
67
+ }
68
+ insertStmt.run({
69
+ uuid: r.uuid,
70
+ session_id: r.session_id,
71
+ model: parsed.message?.model ?? null,
72
+ input: usage.inputTokens,
73
+ output: usage.outputTokens,
74
+ cc: usage.cacheCreateTokens,
75
+ cr: usage.cacheReadTokens,
76
+ ts: r.timestamp,
77
+ });
78
+ inserted += 1;
79
+ chunkTouched.add(r.session_id);
80
+ }
81
+ for (const sid of chunkTouched) {
82
+ recomputeSessionRollup(db, sid);
83
+ allTouched.add(sid);
84
+ }
85
+ });
86
+ chunkTxn();
87
+ scanned += rows.length;
88
+ options.onProgress?.({
89
+ scanned,
90
+ inserted,
91
+ sessionsTouched: allTouched.size,
92
+ done: rows.length < take,
93
+ });
94
+ if (rows.length < take)
95
+ break;
96
+ }
97
+ return {
98
+ scanned,
99
+ inserted,
100
+ sessionsTouched: allTouched.size,
101
+ done: true,
102
+ };
103
+ }
104
+ /**
105
+ * Run a synchronous backfill pass in bounded chunks. Returns when either
106
+ * `limit` is reached or there are no more pending messages.
107
+ */
108
+ export function runBackfill(options = {}) {
109
+ return runChunked(getDb(), options);
110
+ }
111
+ /**
112
+ * Kick the backfill in the background. Safe to call multiple times — a
113
+ * simple in-process gate prevents overlapping passes from stepping on
114
+ * each other.
115
+ */
116
+ export function startBackgroundBackfill(opts = {}) {
117
+ if (running)
118
+ return false;
119
+ running = true;
120
+ queueMicrotask(() => {
121
+ try {
122
+ const result = runChunked(getDb(), opts);
123
+ lastRun = {
124
+ scanned: result.scanned,
125
+ inserted: result.inserted,
126
+ sessionsTouched: result.sessionsTouched,
127
+ finishedAt: new Date().toISOString(),
128
+ };
129
+ }
130
+ catch (err) {
131
+ console.error('[stats.backfill] failed:', err);
132
+ }
133
+ finally {
134
+ running = false;
135
+ }
136
+ });
137
+ return true;
138
+ }
139
+ export function isBackfillRunning() {
140
+ return running;
141
+ }
142
+ export function getLastBackfillRun() {
143
+ return lastRun;
144
+ }
145
+ /** Test helper — clears the in-memory unparsable cache. */
146
+ export function __resetBackfillState() {
147
+ unparsable.clear();
148
+ lastRun = null;
149
+ running = false;
150
+ }
151
+ //# sourceMappingURL=backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill.js","sourceRoot":"","sources":["../../src/stats/backfill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { getDb } from '../db/client.js';
2
+ function clamp01(n) {
3
+ return Math.max(0, Math.min(1, n));
4
+ }
5
+ export function computeHealthScore(projectId) {
6
+ const db = getDb();
7
+ const project = db
8
+ .prepare('SELECT id, name FROM projects WHERE id = ?')
9
+ .get(projectId);
10
+ if (!project)
11
+ return null;
12
+ const sessionStats = db
13
+ .prepare(`SELECT COUNT(*) AS cnt,
14
+ MAX(started_at) AS latest
15
+ FROM sessions WHERE project_id = ?`)
16
+ .get(projectId);
17
+ const sessionCount = sessionStats.cnt;
18
+ if (sessionCount === 0) {
19
+ return {
20
+ projectId,
21
+ projectName: project.name,
22
+ score: 0,
23
+ breakdown: {
24
+ sessionCount: { raw: 0, score: 0, weight: 0.2 },
25
+ recency: { daysSinceLastSession: Infinity, score: 0, weight: 0.25 },
26
+ fragmentation: { avgMessages: 0, score: 0, weight: 0.15 },
27
+ searchCoverage: { ratio: 0, score: 0, weight: 0.2 },
28
+ tagCoverage: { ratio: 0, score: 0, weight: 0.2 },
29
+ },
30
+ };
31
+ }
32
+ const sessionCountScore = clamp01(sessionCount / 10);
33
+ const daysSince = sessionStats.latest
34
+ ? (Date.now() - new Date(sessionStats.latest).getTime()) / (1000 * 60 * 60 * 24)
35
+ : 90;
36
+ const recencyScore = clamp01(1 - daysSince / 90);
37
+ const fragRow = db
38
+ .prepare(`SELECT AVG(message_count) AS avg_msgs
39
+ FROM sessions WHERE project_id = ?`)
40
+ .get(projectId);
41
+ const avgMessages = fragRow.avg_msgs ?? 0;
42
+ const fragmentationScore = clamp01((avgMessages - 2) / 3);
43
+ const ftsRow = db
44
+ .prepare(`SELECT COUNT(*) AS total,
45
+ SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
46
+ FROM messages m
47
+ JOIN sessions s ON s.id = m.session_id
48
+ WHERE s.project_id = ?`)
49
+ .get(projectId);
50
+ const searchRatio = ftsRow.total > 0 ? ftsRow.covered / ftsRow.total : 0.5;
51
+ const searchScore = clamp01(searchRatio);
52
+ const tagRow = db
53
+ .prepare(`SELECT COUNT(DISTINCT s.id) AS total,
54
+ COUNT(DISTINCT st.session_id) AS tagged
55
+ FROM sessions s
56
+ LEFT JOIN session_tags st ON st.session_id = s.id
57
+ WHERE s.project_id = ?`)
58
+ .get(projectId);
59
+ const tagRatio = tagRow.total > 0 ? tagRow.tagged / tagRow.total : 0;
60
+ const tagScore = clamp01(tagRatio);
61
+ const score = Math.round((sessionCountScore * 0.2 +
62
+ recencyScore * 0.25 +
63
+ fragmentationScore * 0.15 +
64
+ searchScore * 0.2 +
65
+ tagScore * 0.2) *
66
+ 100);
67
+ return {
68
+ projectId,
69
+ projectName: project.name,
70
+ score,
71
+ breakdown: {
72
+ sessionCount: { raw: sessionCount, score: sessionCountScore, weight: 0.2 },
73
+ recency: { daysSinceLastSession: Math.round(daysSince), score: recencyScore, weight: 0.25 },
74
+ fragmentation: { avgMessages: Math.round(avgMessages * 10) / 10, score: fragmentationScore, weight: 0.15 },
75
+ searchCoverage: { ratio: Math.round(searchRatio * 100) / 100, score: searchScore, weight: 0.2 },
76
+ tagCoverage: { ratio: Math.round(tagRatio * 100) / 100, score: tagScore, weight: 0.2 },
77
+ },
78
+ };
79
+ }
80
+ export function computeHealthScoreByName(name) {
81
+ const db = getDb();
82
+ const row = db
83
+ .prepare('SELECT id FROM projects WHERE name = ?')
84
+ .get(name);
85
+ if (!row)
86
+ return null;
87
+ return computeHealthScore(row.id);
88
+ }
89
+ export function computeAllHealthScores() {
90
+ const db = getDb();
91
+ const projects = db
92
+ .prepare('SELECT id FROM projects ORDER BY name')
93
+ .all();
94
+ const scores = [];
95
+ for (const p of projects) {
96
+ const s = computeHealthScore(p.id);
97
+ if (s)
98
+ scores.push(s);
99
+ }
100
+ return scores;
101
+ }
102
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/stats/health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,385 @@
1
+ /**
2
+ * v0.10a — cost / token analytics query layer.
3
+ *
4
+ * Reads raw token counts from `message_usage` + per-session rollups from
5
+ * `sessions`, then derives dollar amounts via the pricing table at
6
+ * render time. Prices change; tokens don't.
7
+ */
8
+ import { getDb } from '../db/client.js';
9
+ import { getUnrecoverableCount } from './backfill.js';
10
+ import { estimateCost, formatDollars, formatTokens, priceFor, } from '../utils/pricing.js';
11
+ function aggregateByModel(rows) {
12
+ const agg = new Map();
13
+ for (const r of rows) {
14
+ const key = r.model ?? null;
15
+ const prev = agg.get(key) ?? {
16
+ inputTokens: 0,
17
+ outputTokens: 0,
18
+ cacheCreateTokens: 0,
19
+ cacheReadTokens: 0,
20
+ messageCount: 0,
21
+ };
22
+ prev.inputTokens += r.input_tokens;
23
+ prev.outputTokens += r.output_tokens;
24
+ prev.cacheCreateTokens += r.cache_create_tokens;
25
+ prev.cacheReadTokens += r.cache_read_tokens;
26
+ prev.messageCount += r.n;
27
+ agg.set(key, prev);
28
+ }
29
+ const out = [];
30
+ for (const [model, m] of agg.entries()) {
31
+ const cost = estimateCost({
32
+ inputTokens: m.inputTokens,
33
+ outputTokens: m.outputTokens,
34
+ cacheCreateTokens: m.cacheCreateTokens,
35
+ cacheReadTokens: m.cacheReadTokens,
36
+ }, model);
37
+ out.push({
38
+ model,
39
+ modelLabel: priceFor(model).label,
40
+ inputTokens: m.inputTokens,
41
+ outputTokens: m.outputTokens,
42
+ cacheCreateTokens: m.cacheCreateTokens,
43
+ cacheReadTokens: m.cacheReadTokens,
44
+ messageCount: m.messageCount,
45
+ cost,
46
+ });
47
+ }
48
+ return out.sort((a, b) => b.cost.cents - a.cost.cents);
49
+ }
50
+ function buildByModelTotals(rows) {
51
+ const byModel = {};
52
+ for (const r of rows) {
53
+ byModel[r.model ?? '__unknown__'] = {
54
+ inputTokens: r.inputTokens,
55
+ outputTokens: r.outputTokens,
56
+ cacheCreateTokens: r.cacheCreateTokens,
57
+ cacheReadTokens: r.cacheReadTokens,
58
+ };
59
+ }
60
+ return { byModel };
61
+ }
62
+ export function getSessionStats(sessionId) {
63
+ const db = getDb();
64
+ const s = db
65
+ .prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
66
+ s.message_count,
67
+ s.total_input_tokens, s.total_output_tokens,
68
+ s.total_cache_create_tokens, s.total_cache_read_tokens,
69
+ s.primary_model
70
+ FROM sessions s
71
+ LEFT JOIN projects p ON p.id = s.project_id
72
+ WHERE s.id = ?`)
73
+ .get(sessionId);
74
+ if (!s)
75
+ return null;
76
+ const perModel = db
77
+ .prepare(`SELECT model,
78
+ COALESCE(SUM(input_tokens), 0) AS input_tokens,
79
+ COALESCE(SUM(output_tokens), 0) AS output_tokens,
80
+ COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
81
+ COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,
82
+ COUNT(*) AS n
83
+ FROM message_usage
84
+ WHERE session_id = ?
85
+ GROUP BY model`)
86
+ .all(sessionId);
87
+ const byModel = aggregateByModel(perModel);
88
+ const input = s.total_input_tokens ?? 0;
89
+ const output = s.total_output_tokens ?? 0;
90
+ const cc = s.total_cache_create_tokens ?? 0;
91
+ const cr = s.total_cache_read_tokens ?? 0;
92
+ const cost = estimateCost({
93
+ inputTokens: input,
94
+ outputTokens: output,
95
+ cacheCreateTokens: cc,
96
+ cacheReadTokens: cr,
97
+ ...buildByModelTotals(byModel),
98
+ }, s.primary_model);
99
+ return {
100
+ sessionId: s.id,
101
+ project: s.project,
102
+ startedAt: s.started_at,
103
+ endedAt: s.ended_at,
104
+ messageCount: s.message_count,
105
+ primaryModel: s.primary_model,
106
+ primaryModelLabel: priceFor(s.primary_model).label,
107
+ inputTokens: input,
108
+ outputTokens: output,
109
+ cacheCreateTokens: cc,
110
+ cacheReadTokens: cr,
111
+ totalTokens: cost.totalTokens,
112
+ cost,
113
+ byModel,
114
+ display: {
115
+ dollars: formatDollars(cost.cents),
116
+ tokens: formatTokens(cost.totalTokens),
117
+ model: priceFor(s.primary_model).label,
118
+ },
119
+ };
120
+ }
121
+ export function getProjectStats(projectName) {
122
+ const db = getDb();
123
+ const p = db
124
+ .prepare(`SELECT id, name FROM projects WHERE name = ?`)
125
+ .get(projectName);
126
+ if (!p)
127
+ return null;
128
+ const perModel = db
129
+ .prepare(`SELECT mu.model,
130
+ COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
131
+ COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
132
+ COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
133
+ COALESCE(SUM(mu.cache_read_tokens), 0) AS cache_read_tokens,
134
+ COUNT(*) AS n
135
+ FROM message_usage mu
136
+ JOIN sessions s ON s.id = mu.session_id
137
+ WHERE s.project_id = ?
138
+ GROUP BY mu.model`)
139
+ .all(p.id);
140
+ const byModel = aggregateByModel(perModel);
141
+ const totalsRow = db
142
+ .prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
143
+ COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
144
+ COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
145
+ COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
146
+ COUNT(*) AS session_count
147
+ FROM sessions WHERE project_id = ?`)
148
+ .get(p.id);
149
+ const cost = estimateCost({
150
+ inputTokens: totalsRow.input_tokens,
151
+ outputTokens: totalsRow.output_tokens,
152
+ cacheCreateTokens: totalsRow.cache_create_tokens,
153
+ cacheReadTokens: totalsRow.cache_read_tokens,
154
+ ...buildByModelTotals(byModel),
155
+ }, null);
156
+ const topRaw = db
157
+ .prepare(`SELECT s.id, sa.alias, s.started_at,
158
+ s.total_input_tokens, s.total_output_tokens,
159
+ s.total_cache_create_tokens, s.total_cache_read_tokens,
160
+ s.primary_model
161
+ FROM sessions s
162
+ LEFT JOIN session_aliases sa ON sa.session_id = s.id
163
+ WHERE s.project_id = ?
164
+ ORDER BY (COALESCE(s.total_input_tokens,0)
165
+ + COALESCE(s.total_output_tokens,0)
166
+ + COALESCE(s.total_cache_create_tokens,0)
167
+ + COALESCE(s.total_cache_read_tokens,0)) DESC
168
+ LIMIT 10`)
169
+ .all(p.id);
170
+ const topSessions = topRaw.map((r) => {
171
+ const c = estimateCost({
172
+ inputTokens: r.total_input_tokens ?? 0,
173
+ outputTokens: r.total_output_tokens ?? 0,
174
+ cacheCreateTokens: r.total_cache_create_tokens ?? 0,
175
+ cacheReadTokens: r.total_cache_read_tokens ?? 0,
176
+ }, r.primary_model);
177
+ return {
178
+ sessionId: r.id,
179
+ alias: r.alias,
180
+ startedAt: r.started_at,
181
+ totalTokens: c.totalTokens,
182
+ cost: c,
183
+ };
184
+ });
185
+ return {
186
+ project: p.name,
187
+ sessionCount: totalsRow.session_count,
188
+ inputTokens: totalsRow.input_tokens,
189
+ outputTokens: totalsRow.output_tokens,
190
+ cacheCreateTokens: totalsRow.cache_create_tokens,
191
+ cacheReadTokens: totalsRow.cache_read_tokens,
192
+ totalTokens: cost.totalTokens,
193
+ cost,
194
+ byModel,
195
+ topSessions,
196
+ display: {
197
+ dollars: formatDollars(cost.cents),
198
+ tokens: formatTokens(cost.totalTokens),
199
+ },
200
+ };
201
+ }
202
+ export function getOverviewStats(range = 'all') {
203
+ const db = getDb();
204
+ const sinceIso = (() => {
205
+ if (range === '7d')
206
+ return new Date(Date.now() - 7 * 86400_000).toISOString();
207
+ if (range === '30d')
208
+ return new Date(Date.now() - 30 * 86400_000).toISOString();
209
+ return null;
210
+ })();
211
+ // Single methodology across every field: sum message_usage rows whose
212
+ // timestamp falls in the window. Backfilled rows (timestamp IS NULL) are
213
+ // attributed to the session's started_at — the best we have for them.
214
+ // For range='all' there is no filter, so this matches the session rollups
215
+ // exactly (message_usage is 1:1 with assistant messages on write).
216
+ const whereMu = sinceIso
217
+ ? `WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)`
218
+ : '';
219
+ const whereMsg = sinceIso
220
+ ? `WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)`
221
+ : '';
222
+ const params = sinceIso ? { since: sinceIso } : {};
223
+ // better-sqlite3 rejects a params object when the statement has no bindings.
224
+ const getRow = (sql) => sinceIso ? db.prepare(sql).get(params) : db.prepare(sql).get();
225
+ const allRows = (sql) => (sinceIso ? db.prepare(sql).all(params) : db.prepare(sql).all());
226
+ // byModel — one row per model, summed over the window.
227
+ const perModel = allRows(`SELECT mu.model,
228
+ COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
229
+ COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
230
+ COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
231
+ COALESCE(SUM(mu.cache_read_tokens), 0) AS cache_read_tokens,
232
+ COUNT(*) AS n
233
+ FROM message_usage mu
234
+ JOIN sessions s ON s.id = mu.session_id
235
+ ${whereMu}
236
+ GROUP BY mu.model`);
237
+ const byModel = aggregateByModel(perModel);
238
+ // Totals — sum across the same filtered message_usage set. Guarantees
239
+ // that input/output/cc/cr on the response match the sum of byModel[].
240
+ let inputTokens = 0;
241
+ let outputTokens = 0;
242
+ let cacheCreateTokens = 0;
243
+ let cacheReadTokens = 0;
244
+ for (const m of byModel) {
245
+ inputTokens += m.inputTokens;
246
+ outputTokens += m.outputTokens;
247
+ cacheCreateTokens += m.cacheCreateTokens;
248
+ cacheReadTokens += m.cacheReadTokens;
249
+ }
250
+ const cost = estimateCost({
251
+ inputTokens,
252
+ outputTokens,
253
+ cacheCreateTokens,
254
+ cacheReadTokens,
255
+ ...buildByModelTotals(byModel),
256
+ }, null);
257
+ // Session counters — use the same window. `totalSessions` counts any
258
+ // session with at least one message in range; `sessionsWithUsage` counts
259
+ // sessions with a priced message_usage row in range. For range='all'
260
+ // both degenerate to the full sessions table (and rows with no activity
261
+ // at all will simply not appear — which is correct).
262
+ const sessionsCounts = sinceIso
263
+ ? getRow(`SELECT
264
+ (SELECT COUNT(DISTINCT m.session_id)
265
+ FROM messages m
266
+ JOIN sessions s2 ON s2.id = m.session_id
267
+ ${whereMsg}) AS total_sessions,
268
+ (SELECT COUNT(DISTINCT mu.session_id)
269
+ FROM message_usage mu
270
+ JOIN sessions s ON s.id = mu.session_id
271
+ ${whereMu}) AS sessions_with_usage`)
272
+ : db
273
+ .prepare(`SELECT
274
+ (SELECT COUNT(*) FROM sessions) AS total_sessions,
275
+ (SELECT COUNT(*) FROM sessions
276
+ WHERE (COALESCE(total_input_tokens,0)
277
+ +COALESCE(total_output_tokens,0)
278
+ +COALESCE(total_cache_create_tokens,0)
279
+ +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`)
280
+ .get();
281
+ // Daily bucketing in *local* time (the daemon and browser share a
282
+ // machine, so the user's local TZ is what they expect to see). The
283
+ // 'localtime' modifier shifts the UTC ISO timestamp into the daemon's
284
+ // configured TZ before slicing the YYYY-MM-DD prefix. Without this, a
285
+ // user in PT working at 6pm sees their evening activity bucketed into
286
+ // tomorrow's UTC bar — confusing and the source of "stats look stale"
287
+ // bug reports.
288
+ const dailyRows = allRows(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
289
+ mu.model,
290
+ COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
291
+ COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
292
+ COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
293
+ COALESCE(SUM(mu.cache_read_tokens), 0) AS cache_read_tokens
294
+ FROM message_usage mu
295
+ JOIN sessions s ON s.id = mu.session_id
296
+ ${whereMu}
297
+ GROUP BY day, mu.model
298
+ ORDER BY day ASC`);
299
+ const dailyMap = new Map();
300
+ for (const r of dailyRows) {
301
+ if (!r.day)
302
+ continue;
303
+ const c = estimateCost({
304
+ inputTokens: r.input_tokens,
305
+ outputTokens: r.output_tokens,
306
+ cacheCreateTokens: r.cache_create_tokens,
307
+ cacheReadTokens: r.cache_read_tokens,
308
+ }, r.model);
309
+ const prev = dailyMap.get(r.day) ?? { tokens: 0, cents: 0 };
310
+ prev.tokens += c.totalTokens;
311
+ prev.cents += c.cents;
312
+ dailyMap.set(r.day, prev);
313
+ }
314
+ const daily = [...dailyMap.entries()]
315
+ .map(([day, v]) => ({ day, tokens: v.tokens, cents: v.cents }))
316
+ .sort((a, b) => a.day.localeCompare(b.day));
317
+ // Top sessions — aggregate in-window usage per session so a long-running
318
+ // session whose started_at is outside the window can still appear if its
319
+ // recent activity makes the top 10.
320
+ const topRaw = allRows(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
321
+ COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
322
+ COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
323
+ COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
324
+ COALESCE(SUM(mu.cache_read_tokens), 0) AS cache_read_tokens,
325
+ s.primary_model
326
+ FROM message_usage mu
327
+ JOIN sessions s ON s.id = mu.session_id
328
+ LEFT JOIN projects p ON p.id = s.project_id
329
+ LEFT JOIN session_aliases sa ON sa.session_id = s.id
330
+ ${whereMu}
331
+ GROUP BY s.id
332
+ ORDER BY (COALESCE(SUM(mu.input_tokens),0)
333
+ + COALESCE(SUM(mu.output_tokens),0)
334
+ + COALESCE(SUM(mu.cache_create_tokens),0)
335
+ + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
336
+ LIMIT 10`);
337
+ const topSessions = topRaw.map((r) => {
338
+ const c = estimateCost({
339
+ inputTokens: r.input_tokens,
340
+ outputTokens: r.output_tokens,
341
+ cacheCreateTokens: r.cache_create_tokens,
342
+ cacheReadTokens: r.cache_read_tokens,
343
+ }, r.primary_model);
344
+ return {
345
+ sessionId: r.id,
346
+ project: r.project,
347
+ alias: r.alias,
348
+ startedAt: r.started_at,
349
+ totalTokens: c.totalTokens,
350
+ cost: c,
351
+ };
352
+ });
353
+ // Backfill coverage is a global invariant — not range-scoped — so it
354
+ // never shifts depending on the filter the user picked.
355
+ const coverage = db
356
+ .prepare(`SELECT
357
+ (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
358
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`)
359
+ .get();
360
+ return {
361
+ range,
362
+ totalSessions: sessionsCounts.total_sessions,
363
+ sessionsWithUsage: sessionsCounts.sessions_with_usage,
364
+ inputTokens,
365
+ outputTokens,
366
+ cacheCreateTokens,
367
+ cacheReadTokens,
368
+ totalTokens: cost.totalTokens,
369
+ cost,
370
+ daily,
371
+ byModel,
372
+ topSessions,
373
+ backfill: {
374
+ assistantMessages: coverage.assistant_messages,
375
+ messagesWithUsage: coverage.messages_with_usage,
376
+ pending: Math.max(0, coverage.assistant_messages - coverage.messages_with_usage),
377
+ unrecoverable: Math.min(getUnrecoverableCount(), Math.max(0, coverage.assistant_messages - coverage.messages_with_usage)),
378
+ },
379
+ display: {
380
+ dollars: formatDollars(cost.cents),
381
+ tokens: formatTokens(cost.totalTokens),
382
+ },
383
+ };
384
+ }
385
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/stats/query.ts"],"names":[],"mappings":"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"}