@kaitranntt/ccs 6.7.0 → 6.7.1-dev.1

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 (623) hide show
  1. package/README.md +45 -23
  2. package/dist/api/index.d.ts +7 -0
  3. package/dist/api/index.d.ts.map +1 -0
  4. package/dist/api/index.js +24 -0
  5. package/dist/api/index.js.map +1 -0
  6. package/dist/api/services/index.d.ts +10 -0
  7. package/dist/api/services/index.d.ts.map +1 -0
  8. package/dist/api/services/index.js +26 -0
  9. package/dist/api/services/index.js.map +1 -0
  10. package/dist/api/services/profile-reader.d.ts +28 -0
  11. package/dist/api/services/profile-reader.d.ts.map +1 -0
  12. package/dist/api/services/profile-reader.js +148 -0
  13. package/dist/api/services/profile-reader.js.map +1 -0
  14. package/dist/api/services/profile-types.d.ts +42 -0
  15. package/dist/api/services/profile-types.d.ts.map +1 -0
  16. package/dist/api/services/profile-types.js +8 -0
  17. package/dist/api/services/profile-types.js.map +1 -0
  18. package/dist/api/services/profile-writer.d.ts +6 -0
  19. package/dist/api/services/profile-writer.d.ts.map +1 -0
  20. package/dist/api/services/profile-writer.js +176 -0
  21. package/dist/api/services/profile-writer.js.map +1 -0
  22. package/dist/api/services/validation-service.d.ts +26 -0
  23. package/dist/api/services/validation-service.d.ts.map +1 -0
  24. package/dist/api/services/validation-service.js +77 -0
  25. package/dist/api/services/validation-service.js.map +1 -0
  26. package/dist/auth/auth-commands.d.ts +16 -22
  27. package/dist/auth/auth-commands.d.ts.map +1 -1
  28. package/dist/auth/auth-commands.js +29 -467
  29. package/dist/auth/auth-commands.js.map +1 -1
  30. package/dist/auth/commands/create-command.d.ts +11 -0
  31. package/dist/auth/commands/create-command.d.ts.map +1 -0
  32. package/dist/auth/commands/create-command.js +137 -0
  33. package/dist/auth/commands/create-command.js.map +1 -0
  34. package/dist/auth/commands/default-command.d.ts +15 -0
  35. package/dist/auth/commands/default-command.d.ts.map +1 -0
  36. package/dist/auth/commands/default-command.js +71 -0
  37. package/dist/auth/commands/default-command.js.map +1 -0
  38. package/dist/auth/commands/index.d.ts +12 -0
  39. package/dist/auth/commands/index.d.ts.map +1 -0
  40. package/dist/auth/commands/index.js +25 -0
  41. package/dist/auth/commands/index.js.map +1 -0
  42. package/dist/auth/commands/list-command.d.ts +11 -0
  43. package/dist/auth/commands/list-command.d.ts.map +1 -0
  44. package/dist/auth/commands/list-command.js +124 -0
  45. package/dist/auth/commands/list-command.js.map +1 -0
  46. package/dist/auth/commands/remove-command.d.ts +11 -0
  47. package/dist/auth/commands/remove-command.d.ts.map +1 -0
  48. package/dist/auth/commands/remove-command.js +102 -0
  49. package/dist/auth/commands/remove-command.js.map +1 -0
  50. package/dist/auth/commands/show-command.d.ts +11 -0
  51. package/dist/auth/commands/show-command.d.ts.map +1 -0
  52. package/dist/auth/commands/show-command.js +103 -0
  53. package/dist/auth/commands/show-command.js.map +1 -0
  54. package/dist/auth/commands/types.d.ts +50 -0
  55. package/dist/auth/commands/types.d.ts.map +1 -0
  56. package/dist/auth/commands/types.js +26 -0
  57. package/dist/auth/commands/types.js.map +1 -0
  58. package/dist/auth/index.d.ts +8 -0
  59. package/dist/auth/index.d.ts.map +1 -0
  60. package/dist/auth/index.js +16 -0
  61. package/dist/auth/index.js.map +1 -0
  62. package/dist/auth/profile-detector.d.ts +0 -5
  63. package/dist/auth/profile-detector.d.ts.map +1 -1
  64. package/dist/auth/profile-detector.js +1 -9
  65. package/dist/auth/profile-detector.js.map +1 -1
  66. package/dist/auth/profile-registry.d.ts +0 -4
  67. package/dist/auth/profile-registry.d.ts.map +1 -1
  68. package/dist/auth/profile-registry.js +3 -10
  69. package/dist/auth/profile-registry.js.map +1 -1
  70. package/dist/ccs.js +21 -4
  71. package/dist/ccs.js.map +1 -1
  72. package/dist/cliproxy/auth/auth-types.d.ts +85 -0
  73. package/dist/cliproxy/auth/auth-types.d.ts.map +1 -0
  74. package/dist/cliproxy/auth/auth-types.js +100 -0
  75. package/dist/cliproxy/auth/auth-types.js.map +1 -0
  76. package/dist/cliproxy/auth/environment-detector.d.ts +30 -0
  77. package/dist/cliproxy/auth/environment-detector.d.ts.map +1 -0
  78. package/dist/cliproxy/auth/environment-detector.js +124 -0
  79. package/dist/cliproxy/auth/environment-detector.js.map +1 -0
  80. package/dist/cliproxy/auth/index.d.ts +11 -0
  81. package/dist/cliproxy/auth/index.d.ts.map +1 -0
  82. package/dist/cliproxy/auth/index.js +35 -0
  83. package/dist/cliproxy/auth/index.js.map +1 -0
  84. package/dist/cliproxy/auth/oauth-handler.d.ts +29 -0
  85. package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -0
  86. package/dist/cliproxy/auth/oauth-handler.js +205 -0
  87. package/dist/cliproxy/auth/oauth-handler.js.map +1 -0
  88. package/dist/cliproxy/auth/oauth-process.d.ts +27 -0
  89. package/dist/cliproxy/auth/oauth-process.d.ts.map +1 -0
  90. package/dist/cliproxy/auth/oauth-process.js +209 -0
  91. package/dist/cliproxy/auth/oauth-process.js.map +1 -0
  92. package/dist/cliproxy/auth/token-manager.d.ts +53 -0
  93. package/dist/cliproxy/auth/token-manager.d.ts.map +1 -0
  94. package/dist/cliproxy/auth/token-manager.js +250 -0
  95. package/dist/cliproxy/auth/token-manager.js.map +1 -0
  96. package/dist/cliproxy/auth-handler.d.ts +13 -103
  97. package/dist/cliproxy/auth-handler.d.ts.map +1 -1
  98. package/dist/cliproxy/auth-handler.js +35 -766
  99. package/dist/cliproxy/auth-handler.js.map +1 -1
  100. package/dist/cliproxy/binary/downloader.d.ts +29 -0
  101. package/dist/cliproxy/binary/downloader.d.ts.map +1 -0
  102. package/dist/cliproxy/binary/downloader.js +218 -0
  103. package/dist/cliproxy/binary/downloader.js.map +1 -0
  104. package/dist/cliproxy/binary/extractor.d.ts +12 -0
  105. package/dist/cliproxy/binary/extractor.d.ts.map +1 -0
  106. package/dist/cliproxy/binary/extractor.js +27 -0
  107. package/dist/cliproxy/binary/extractor.js.map +1 -0
  108. package/dist/cliproxy/binary/index.d.ts +13 -0
  109. package/dist/cliproxy/binary/index.d.ts.map +1 -0
  110. package/dist/cliproxy/binary/index.js +53 -0
  111. package/dist/cliproxy/binary/index.js.map +1 -0
  112. package/dist/cliproxy/binary/installer.d.ts +24 -0
  113. package/dist/cliproxy/binary/installer.d.ts.map +1 -0
  114. package/dist/cliproxy/binary/installer.js +117 -0
  115. package/dist/cliproxy/binary/installer.js.map +1 -0
  116. package/dist/cliproxy/binary/lifecycle.d.ts +11 -0
  117. package/dist/cliproxy/binary/lifecycle.d.ts.map +1 -0
  118. package/dist/cliproxy/binary/lifecycle.js +106 -0
  119. package/dist/cliproxy/binary/lifecycle.js.map +1 -0
  120. package/dist/cliproxy/binary/tar-extractor.d.ts +9 -0
  121. package/dist/cliproxy/binary/tar-extractor.d.ts.map +1 -0
  122. package/dist/cliproxy/binary/tar-extractor.js +118 -0
  123. package/dist/cliproxy/binary/tar-extractor.js.map +1 -0
  124. package/dist/cliproxy/binary/types.d.ts +24 -0
  125. package/dist/cliproxy/binary/types.d.ts.map +1 -0
  126. package/dist/cliproxy/binary/types.js +14 -0
  127. package/dist/cliproxy/binary/types.js.map +1 -0
  128. package/dist/cliproxy/binary/updater.d.ts +7 -0
  129. package/dist/cliproxy/binary/updater.d.ts.map +1 -0
  130. package/dist/cliproxy/binary/updater.js +18 -0
  131. package/dist/cliproxy/binary/updater.js.map +1 -0
  132. package/dist/cliproxy/binary/verifier.d.ts +18 -0
  133. package/dist/cliproxy/binary/verifier.d.ts.map +1 -0
  134. package/dist/cliproxy/binary/verifier.js +82 -0
  135. package/dist/cliproxy/binary/verifier.js.map +1 -0
  136. package/dist/cliproxy/binary/version-cache.d.ts +46 -0
  137. package/dist/cliproxy/binary/version-cache.d.ts.map +1 -0
  138. package/dist/cliproxy/binary/version-cache.js +171 -0
  139. package/dist/cliproxy/binary/version-cache.js.map +1 -0
  140. package/dist/cliproxy/binary/version-checker.d.ts +19 -0
  141. package/dist/cliproxy/binary/version-checker.d.ts.map +1 -0
  142. package/dist/cliproxy/binary/version-checker.js +77 -0
  143. package/dist/cliproxy/binary/version-checker.js.map +1 -0
  144. package/dist/cliproxy/binary/zip-extractor.d.ts +9 -0
  145. package/dist/cliproxy/binary/zip-extractor.d.ts.map +1 -0
  146. package/dist/cliproxy/binary/zip-extractor.js +110 -0
  147. package/dist/cliproxy/binary/zip-extractor.js.map +1 -0
  148. package/dist/cliproxy/binary-manager.d.ts +16 -170
  149. package/dist/cliproxy/binary-manager.d.ts.map +1 -1
  150. package/dist/cliproxy/binary-manager.js +38 -865
  151. package/dist/cliproxy/binary-manager.js.map +1 -1
  152. package/dist/cliproxy/services/binary-service.d.ts +65 -0
  153. package/dist/cliproxy/services/binary-service.d.ts.map +1 -0
  154. package/dist/cliproxy/services/binary-service.js +140 -0
  155. package/dist/cliproxy/services/binary-service.js.map +1 -0
  156. package/dist/cliproxy/services/index.d.ts +8 -0
  157. package/dist/cliproxy/services/index.d.ts.map +1 -0
  158. package/dist/cliproxy/services/index.js +31 -0
  159. package/dist/cliproxy/services/index.js.map +1 -0
  160. package/dist/cliproxy/services/proxy-lifecycle-service.d.ts +38 -0
  161. package/dist/cliproxy/services/proxy-lifecycle-service.d.ts.map +1 -0
  162. package/dist/cliproxy/services/proxy-lifecycle-service.js +41 -0
  163. package/dist/cliproxy/services/proxy-lifecycle-service.js.map +1 -0
  164. package/dist/cliproxy/services/variant-config-adapter.d.ts +38 -0
  165. package/dist/cliproxy/services/variant-config-adapter.d.ts.map +1 -0
  166. package/dist/cliproxy/services/variant-config-adapter.js +172 -0
  167. package/dist/cliproxy/services/variant-config-adapter.js.map +1 -0
  168. package/dist/cliproxy/services/variant-service.d.ts +37 -0
  169. package/dist/cliproxy/services/variant-service.d.ts.map +1 -0
  170. package/dist/cliproxy/services/variant-service.js +128 -0
  171. package/dist/cliproxy/services/variant-service.js.map +1 -0
  172. package/dist/cliproxy/services/variant-settings.d.ts +32 -0
  173. package/dist/cliproxy/services/variant-settings.d.ts.map +1 -0
  174. package/dist/cliproxy/services/variant-settings.js +128 -0
  175. package/dist/cliproxy/services/variant-settings.js.map +1 -0
  176. package/dist/commands/api-command.d.ts +3 -6
  177. package/dist/commands/api-command.d.ts.map +1 -1
  178. package/dist/commands/api-command.js +84 -445
  179. package/dist/commands/api-command.js.map +1 -1
  180. package/dist/commands/cliproxy-command.d.ts +0 -3
  181. package/dist/commands/cliproxy-command.d.ts.map +1 -1
  182. package/dist/commands/cliproxy-command.js +182 -621
  183. package/dist/commands/cliproxy-command.js.map +1 -1
  184. package/dist/commands/index.d.ts +17 -0
  185. package/dist/commands/index.d.ts.map +1 -0
  186. package/dist/commands/index.js +33 -0
  187. package/dist/commands/index.js.map +1 -0
  188. package/dist/config/unified-config-loader.d.ts +7 -0
  189. package/dist/config/unified-config-loader.d.ts.map +1 -1
  190. package/dist/config/unified-config-loader.js +11 -1
  191. package/dist/config/unified-config-loader.js.map +1 -1
  192. package/dist/delegation/executor/index.d.ts +7 -0
  193. package/dist/delegation/executor/index.d.ts.map +1 -0
  194. package/dist/delegation/executor/index.js +28 -0
  195. package/dist/delegation/executor/index.js.map +1 -0
  196. package/dist/delegation/executor/result-aggregator.d.ts +30 -0
  197. package/dist/delegation/executor/result-aggregator.d.ts.map +1 -0
  198. package/dist/delegation/executor/result-aggregator.js +66 -0
  199. package/dist/delegation/executor/result-aggregator.js.map +1 -0
  200. package/dist/delegation/executor/stream-parser.d.ts +28 -0
  201. package/dist/delegation/executor/stream-parser.d.ts.map +1 -0
  202. package/dist/delegation/executor/stream-parser.js +140 -0
  203. package/dist/delegation/executor/stream-parser.js.map +1 -0
  204. package/dist/delegation/executor/types.d.ts +121 -0
  205. package/dist/delegation/executor/types.d.ts.map +1 -0
  206. package/dist/delegation/executor/types.js +6 -0
  207. package/dist/delegation/executor/types.js.map +1 -0
  208. package/dist/delegation/headless-executor.d.ts +13 -91
  209. package/dist/delegation/headless-executor.d.ts.map +1 -1
  210. package/dist/delegation/headless-executor.js +84 -291
  211. package/dist/delegation/headless-executor.js.map +1 -1
  212. package/dist/delegation/index.d.ts +11 -0
  213. package/dist/delegation/index.d.ts.map +1 -0
  214. package/dist/delegation/index.js +33 -0
  215. package/dist/delegation/index.js.map +1 -0
  216. package/dist/delegation/result-formatter.d.ts +1 -30
  217. package/dist/delegation/result-formatter.d.ts.map +1 -1
  218. package/dist/delegation/result-formatter.js.map +1 -1
  219. package/dist/errors/cleanup-registry.d.ts +64 -0
  220. package/dist/errors/cleanup-registry.d.ts.map +1 -0
  221. package/dist/errors/cleanup-registry.js +141 -0
  222. package/dist/errors/cleanup-registry.js.map +1 -0
  223. package/dist/errors/error-handler.d.ts +45 -0
  224. package/dist/errors/error-handler.d.ts.map +1 -0
  225. package/dist/errors/error-handler.js +150 -0
  226. package/dist/errors/error-handler.js.map +1 -0
  227. package/dist/errors/error-types.d.ts +102 -0
  228. package/dist/errors/error-types.d.ts.map +1 -0
  229. package/dist/errors/error-types.js +158 -0
  230. package/dist/errors/error-types.js.map +1 -0
  231. package/dist/errors/exit-codes.d.ts +49 -0
  232. package/dist/errors/exit-codes.d.ts.map +1 -0
  233. package/dist/errors/exit-codes.js +72 -0
  234. package/dist/errors/exit-codes.js.map +1 -0
  235. package/dist/errors/index.d.ts +29 -0
  236. package/dist/errors/index.d.ts.map +1 -0
  237. package/dist/errors/index.js +62 -0
  238. package/dist/errors/index.js.map +1 -0
  239. package/dist/glmt/glmt-transformer.d.ts +29 -248
  240. package/dist/glmt/glmt-transformer.d.ts.map +1 -1
  241. package/dist/glmt/glmt-transformer.js +63 -718
  242. package/dist/glmt/glmt-transformer.js.map +1 -1
  243. package/dist/glmt/index.d.ts +14 -0
  244. package/dist/glmt/index.d.ts.map +1 -0
  245. package/dist/glmt/index.js +41 -0
  246. package/dist/glmt/index.js.map +1 -0
  247. package/dist/glmt/pipeline/content-transformer.d.ts +39 -0
  248. package/dist/glmt/pipeline/content-transformer.d.ts.map +1 -0
  249. package/dist/glmt/pipeline/content-transformer.js +162 -0
  250. package/dist/glmt/pipeline/content-transformer.js.map +1 -0
  251. package/dist/glmt/pipeline/index.d.ts +12 -0
  252. package/dist/glmt/pipeline/index.d.ts.map +1 -0
  253. package/dist/glmt/pipeline/index.js +20 -0
  254. package/dist/glmt/pipeline/index.js.map +1 -0
  255. package/dist/glmt/pipeline/request-transformer.d.ts +31 -0
  256. package/dist/glmt/pipeline/request-transformer.d.ts.map +1 -0
  257. package/dist/glmt/pipeline/request-transformer.js +116 -0
  258. package/dist/glmt/pipeline/request-transformer.js.map +1 -0
  259. package/dist/glmt/pipeline/response-builder.d.ts +52 -0
  260. package/dist/glmt/pipeline/response-builder.d.ts.map +1 -0
  261. package/dist/glmt/pipeline/response-builder.js +205 -0
  262. package/dist/glmt/pipeline/response-builder.js.map +1 -0
  263. package/dist/glmt/pipeline/stream-parser.d.ts +59 -0
  264. package/dist/glmt/pipeline/stream-parser.d.ts.map +1 -0
  265. package/dist/glmt/pipeline/stream-parser.js +241 -0
  266. package/dist/glmt/pipeline/stream-parser.js.map +1 -0
  267. package/dist/glmt/pipeline/tool-call-handler.d.ts +22 -0
  268. package/dist/glmt/pipeline/tool-call-handler.d.ts.map +1 -0
  269. package/dist/glmt/pipeline/tool-call-handler.js +87 -0
  270. package/dist/glmt/pipeline/tool-call-handler.js.map +1 -0
  271. package/dist/glmt/pipeline/types.d.ts +159 -0
  272. package/dist/glmt/pipeline/types.d.ts.map +1 -0
  273. package/dist/glmt/pipeline/types.js +6 -0
  274. package/dist/glmt/pipeline/types.js.map +1 -0
  275. package/dist/management/checks/cliproxy-check.d.ts +37 -0
  276. package/dist/management/checks/cliproxy-check.d.ts.map +1 -0
  277. package/dist/management/checks/cliproxy-check.js +187 -0
  278. package/dist/management/checks/cliproxy-check.js.map +1 -0
  279. package/dist/management/checks/config-check.d.ts +27 -0
  280. package/dist/management/checks/config-check.d.ts.map +1 -0
  281. package/dist/management/checks/config-check.js +158 -0
  282. package/dist/management/checks/config-check.js.map +1 -0
  283. package/dist/management/checks/env-check.d.ts +17 -0
  284. package/dist/management/checks/env-check.d.ts.map +1 -0
  285. package/dist/management/checks/env-check.js +71 -0
  286. package/dist/management/checks/env-check.js.map +1 -0
  287. package/dist/management/checks/index.d.ts +12 -0
  288. package/dist/management/checks/index.d.ts.map +1 -0
  289. package/dist/management/checks/index.js +48 -0
  290. package/dist/management/checks/index.js.map +1 -0
  291. package/dist/management/checks/oauth-check.d.ts +16 -0
  292. package/dist/management/checks/oauth-check.d.ts.map +1 -0
  293. package/dist/management/checks/oauth-check.js +68 -0
  294. package/dist/management/checks/oauth-check.js.map +1 -0
  295. package/dist/management/checks/profile-check.d.ts +36 -0
  296. package/dist/management/checks/profile-check.d.ts.map +1 -0
  297. package/dist/management/checks/profile-check.js +165 -0
  298. package/dist/management/checks/profile-check.js.map +1 -0
  299. package/dist/management/checks/symlink-check.d.ts +30 -0
  300. package/dist/management/checks/symlink-check.d.ts.map +1 -0
  301. package/dist/management/checks/symlink-check.js +204 -0
  302. package/dist/management/checks/symlink-check.js.map +1 -0
  303. package/dist/management/checks/system-check.d.ts +25 -0
  304. package/dist/management/checks/system-check.d.ts.map +1 -0
  305. package/dist/management/checks/system-check.js +136 -0
  306. package/dist/management/checks/system-check.js.map +1 -0
  307. package/dist/management/checks/types.d.ts +64 -0
  308. package/dist/management/checks/types.d.ts.map +1 -0
  309. package/dist/management/checks/types.js +63 -0
  310. package/dist/management/checks/types.js.map +1 -0
  311. package/dist/management/doctor.d.ts +3 -92
  312. package/dist/management/doctor.d.ts.map +1 -1
  313. package/dist/management/doctor.js +15 -831
  314. package/dist/management/doctor.js.map +1 -1
  315. package/dist/management/index.d.ts +15 -0
  316. package/dist/management/index.d.ts.map +1 -0
  317. package/dist/management/index.js +56 -0
  318. package/dist/management/index.js.map +1 -0
  319. package/dist/management/repair/auto-repair.d.ts +13 -0
  320. package/dist/management/repair/auto-repair.d.ts.map +1 -0
  321. package/dist/management/repair/auto-repair.js +170 -0
  322. package/dist/management/repair/auto-repair.js.map +1 -0
  323. package/dist/management/repair/index.d.ts +5 -0
  324. package/dist/management/repair/index.d.ts.map +1 -0
  325. package/dist/management/repair/index.js +9 -0
  326. package/dist/management/repair/index.js.map +1 -0
  327. package/dist/types/delegation.d.ts +1 -13
  328. package/dist/types/delegation.d.ts.map +1 -1
  329. package/dist/types/utils.d.ts +27 -0
  330. package/dist/types/utils.d.ts.map +1 -1
  331. package/dist/ui/assets/{accounts-p1_nf0Jy.js → accounts-BqDNXZu4.js} +1 -1
  332. package/dist/ui/assets/analytics-DQMyOsqI.js +1 -0
  333. package/dist/ui/assets/api-Bzyq4XP7.js +1 -0
  334. package/dist/ui/assets/{card-CCDc-Mx9.js → card-CIN0KDsX.js} +1 -1
  335. package/dist/ui/assets/cliproxy-5iKdPrEG.js +3 -0
  336. package/dist/ui/assets/cliproxy-control-panel-CJ2CfYod.js +1 -0
  337. package/dist/ui/assets/{code-editor-Br9x-r-E.js → code-editor-CDmNFLQ6.js} +1 -1
  338. package/dist/ui/assets/confirm-dialog-CaiDQikc.js +1 -0
  339. package/dist/ui/assets/copilot-nS6iAyk2.js +4 -0
  340. package/dist/ui/assets/form-utils-DP6ILe7Z.js +20 -0
  341. package/dist/ui/assets/health-DYOQfC7u.js +1 -0
  342. package/dist/ui/assets/{icons-BOsxPbiD.js → icons-ZmwVoUeR.js} +1 -1
  343. package/dist/ui/assets/index-C6Dah-xh.js +46 -0
  344. package/dist/ui/assets/index-It66SkKf.css +1 -0
  345. package/dist/ui/assets/{radix-ui-DFHQr9A5.js → radix-ui-CV3R9pD6.js} +3 -3
  346. package/dist/ui/assets/settings-DABC9b5G.js +1 -0
  347. package/dist/ui/assets/{shared-BfYhSN4-.js → shared-BwU4OKQc.js} +1 -1
  348. package/dist/ui/assets/{switch-CMk95lwf.js → switch-BQ6sBBUv.js} +1 -1
  349. package/dist/ui/assets/{tanstack-C4gT2P7V.js → tanstack-Df9bCj5R.js} +1 -1
  350. package/dist/ui/index.html +6 -6
  351. package/dist/utils/claude-spawner.d.ts +54 -0
  352. package/dist/utils/claude-spawner.d.ts.map +1 -0
  353. package/dist/utils/claude-spawner.js +118 -0
  354. package/dist/utils/claude-spawner.js.map +1 -0
  355. package/dist/utils/delegation-validator.d.ts +8 -4
  356. package/dist/utils/delegation-validator.d.ts.map +1 -1
  357. package/dist/utils/delegation-validator.js.map +1 -1
  358. package/dist/utils/helpers.d.ts +0 -11
  359. package/dist/utils/helpers.d.ts.map +1 -1
  360. package/dist/utils/helpers.js +1 -57
  361. package/dist/utils/helpers.js.map +1 -1
  362. package/dist/utils/index.d.ts +15 -0
  363. package/dist/utils/index.d.ts.map +1 -0
  364. package/dist/utils/index.js +50 -0
  365. package/dist/utils/index.js.map +1 -0
  366. package/dist/utils/time.d.ts +10 -0
  367. package/dist/utils/time.d.ts.map +1 -0
  368. package/dist/utils/time.js +27 -0
  369. package/dist/utils/time.js.map +1 -0
  370. package/dist/utils/ui/boxes.d.ts +24 -0
  371. package/dist/utils/ui/boxes.d.ts.map +1 -0
  372. package/dist/utils/ui/boxes.js +108 -0
  373. package/dist/utils/ui/boxes.js.map +1 -0
  374. package/dist/utils/ui/colors.d.ts +25 -0
  375. package/dist/utils/ui/colors.d.ts.map +1 -0
  376. package/dist/utils/ui/colors.js +70 -0
  377. package/dist/utils/ui/colors.js.map +1 -0
  378. package/dist/utils/ui/index.d.ts +51 -0
  379. package/dist/utils/ui/index.d.ts.map +1 -0
  380. package/dist/utils/ui/index.js +96 -0
  381. package/dist/utils/ui/index.js.map +1 -0
  382. package/dist/utils/ui/indicators.d.ts +23 -0
  383. package/dist/utils/ui/indicators.d.ts.map +1 -0
  384. package/dist/utils/ui/indicators.js +39 -0
  385. package/dist/utils/ui/indicators.js.map +1 -0
  386. package/dist/utils/ui/init.d.ts +30 -0
  387. package/dist/utils/ui/init.d.ts.map +1 -0
  388. package/dist/utils/ui/init.js +102 -0
  389. package/dist/utils/ui/init.js.map +1 -0
  390. package/dist/utils/ui/spinner.d.ts +13 -0
  391. package/dist/utils/ui/spinner.d.ts.map +1 -0
  392. package/dist/utils/ui/spinner.js +89 -0
  393. package/dist/utils/ui/spinner.js.map +1 -0
  394. package/dist/utils/ui/tables.d.ts +12 -0
  395. package/dist/utils/ui/tables.d.ts.map +1 -0
  396. package/dist/utils/ui/tables.js +69 -0
  397. package/dist/utils/ui/tables.js.map +1 -0
  398. package/dist/utils/ui/tasks.d.ts +26 -0
  399. package/dist/utils/ui/tasks.d.ts.map +1 -0
  400. package/dist/utils/ui/tasks.js +102 -0
  401. package/dist/utils/ui/tasks.js.map +1 -0
  402. package/dist/utils/ui/text.d.ts +24 -0
  403. package/dist/utils/ui/text.d.ts.map +1 -0
  404. package/dist/utils/ui/text.js +60 -0
  405. package/dist/utils/ui/text.js.map +1 -0
  406. package/dist/utils/ui/types.d.ts +36 -0
  407. package/dist/utils/ui/types.d.ts.map +1 -0
  408. package/dist/utils/ui/types.js +33 -0
  409. package/dist/utils/ui/types.js.map +1 -0
  410. package/dist/utils/ui.d.ts +3 -138
  411. package/dist/utils/ui.d.ts.map +1 -1
  412. package/dist/utils/ui.js +40 -567
  413. package/dist/utils/ui.js.map +1 -1
  414. package/dist/utils/websearch/gemini-cli.d.ts +36 -0
  415. package/dist/utils/websearch/gemini-cli.d.ts.map +1 -0
  416. package/dist/utils/websearch/gemini-cli.js +132 -0
  417. package/dist/utils/websearch/gemini-cli.js.map +1 -0
  418. package/dist/utils/websearch/grok-cli.d.ts +26 -0
  419. package/dist/utils/websearch/grok-cli.d.ts.map +1 -0
  420. package/dist/utils/websearch/grok-cli.js +81 -0
  421. package/dist/utils/websearch/grok-cli.js.map +1 -0
  422. package/dist/utils/websearch/hook-config.d.ts +21 -0
  423. package/dist/utils/websearch/hook-config.d.ts.map +1 -0
  424. package/dist/utils/websearch/hook-config.js +183 -0
  425. package/dist/utils/websearch/hook-config.js.map +1 -0
  426. package/dist/utils/websearch/hook-env.d.ts +16 -0
  427. package/dist/utils/websearch/hook-env.d.ts.map +1 -0
  428. package/dist/utils/websearch/hook-env.js +62 -0
  429. package/dist/utils/websearch/hook-env.js.map +1 -0
  430. package/dist/utils/websearch/hook-installer.d.ts +27 -0
  431. package/dist/utils/websearch/hook-installer.d.ts.map +1 -0
  432. package/dist/utils/websearch/hook-installer.js +141 -0
  433. package/dist/utils/websearch/hook-installer.js.map +1 -0
  434. package/dist/utils/websearch/index.d.ts +15 -0
  435. package/dist/utils/websearch/index.d.ts.map +1 -0
  436. package/dist/utils/websearch/index.js +44 -0
  437. package/dist/utils/websearch/index.js.map +1 -0
  438. package/dist/utils/websearch/opencode-cli.d.ts +26 -0
  439. package/dist/utils/websearch/opencode-cli.d.ts.map +1 -0
  440. package/dist/utils/websearch/opencode-cli.js +81 -0
  441. package/dist/utils/websearch/opencode-cli.js.map +1 -0
  442. package/dist/utils/websearch/status.d.ts +35 -0
  443. package/dist/utils/websearch/status.d.ts.map +1 -0
  444. package/dist/utils/websearch/status.js +185 -0
  445. package/dist/utils/websearch/status.js.map +1 -0
  446. package/dist/utils/websearch/types.d.ts +85 -0
  447. package/dist/utils/websearch/types.d.ts.map +1 -0
  448. package/dist/utils/websearch/types.js +10 -0
  449. package/dist/utils/websearch/types.js.map +1 -0
  450. package/dist/utils/websearch-manager.d.ts +7 -186
  451. package/dist/utils/websearch-manager.d.ts.map +1 -1
  452. package/dist/utils/websearch-manager.js +37 -710
  453. package/dist/utils/websearch-manager.js.map +1 -1
  454. package/dist/web-server/data-aggregator.d.ts +3 -51
  455. package/dist/web-server/data-aggregator.d.ts.map +1 -1
  456. package/dist/web-server/data-aggregator.js +18 -386
  457. package/dist/web-server/data-aggregator.js.map +1 -1
  458. package/dist/web-server/health/cliproxy-checks.d.ts +23 -0
  459. package/dist/web-server/health/cliproxy-checks.d.ts.map +1 -0
  460. package/dist/web-server/health/cliproxy-checks.js +145 -0
  461. package/dist/web-server/health/cliproxy-checks.js.map +1 -0
  462. package/dist/web-server/health/config-checks.d.ts +19 -0
  463. package/dist/web-server/health/config-checks.d.ts.map +1 -0
  464. package/dist/web-server/health/config-checks.js +174 -0
  465. package/dist/web-server/health/config-checks.js.map +1 -0
  466. package/dist/web-server/health/environment-checks.d.ts +11 -0
  467. package/dist/web-server/health/environment-checks.d.ts.map +1 -0
  468. package/dist/web-server/health/environment-checks.js +40 -0
  469. package/dist/web-server/health/environment-checks.js.map +1 -0
  470. package/dist/web-server/health/index.d.ts +13 -0
  471. package/dist/web-server/health/index.d.ts.map +1 -0
  472. package/dist/web-server/health/index.js +41 -0
  473. package/dist/web-server/health/index.js.map +1 -0
  474. package/dist/web-server/health/oauth-checks.d.ts +11 -0
  475. package/dist/web-server/health/oauth-checks.d.ts.map +1 -0
  476. package/dist/web-server/health/oauth-checks.js +34 -0
  477. package/dist/web-server/health/oauth-checks.js.map +1 -0
  478. package/dist/web-server/health/profile-checks.d.ts +19 -0
  479. package/dist/web-server/health/profile-checks.d.ts.map +1 -0
  480. package/dist/web-server/health/profile-checks.js +152 -0
  481. package/dist/web-server/health/profile-checks.js.map +1 -0
  482. package/dist/web-server/health/symlink-checks.d.ts +15 -0
  483. package/dist/web-server/health/symlink-checks.d.ts.map +1 -0
  484. package/dist/web-server/health/symlink-checks.js +173 -0
  485. package/dist/web-server/health/symlink-checks.js.map +1 -0
  486. package/dist/web-server/health/system-checks.d.ts +19 -0
  487. package/dist/web-server/health/system-checks.d.ts.map +1 -0
  488. package/dist/web-server/health/system-checks.js +127 -0
  489. package/dist/web-server/health/system-checks.js.map +1 -0
  490. package/dist/web-server/health/types.d.ts +34 -0
  491. package/dist/web-server/health/types.d.ts.map +1 -0
  492. package/dist/web-server/health/types.js +8 -0
  493. package/dist/web-server/health/types.js.map +1 -0
  494. package/dist/web-server/health/websearch-checks.d.ts +11 -0
  495. package/dist/web-server/health/websearch-checks.d.ts.map +1 -0
  496. package/dist/web-server/health/websearch-checks.js +53 -0
  497. package/dist/web-server/health/websearch-checks.js.map +1 -0
  498. package/dist/web-server/health-service.d.ts +4 -30
  499. package/dist/web-server/health-service.d.ts.map +1 -1
  500. package/dist/web-server/health-service.js +28 -695
  501. package/dist/web-server/health-service.js.map +1 -1
  502. package/dist/web-server/index.d.ts.map +1 -1
  503. package/dist/web-server/index.js +2 -5
  504. package/dist/web-server/index.js.map +1 -1
  505. package/dist/web-server/routes/cliproxy-auth-routes.d.ts +6 -0
  506. package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -0
  507. package/dist/web-server/routes/cliproxy-auth-routes.js +202 -0
  508. package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -0
  509. package/dist/web-server/routes/cliproxy-stats-routes.d.ts +6 -0
  510. package/dist/web-server/routes/cliproxy-stats-routes.d.ts.map +1 -0
  511. package/dist/web-server/routes/cliproxy-stats-routes.js +253 -0
  512. package/dist/web-server/routes/cliproxy-stats-routes.js.map +1 -0
  513. package/dist/web-server/routes/config-routes.d.ts +6 -0
  514. package/dist/web-server/routes/config-routes.d.ts.map +1 -0
  515. package/dist/web-server/routes/config-routes.js +145 -0
  516. package/dist/web-server/routes/config-routes.js.map +1 -0
  517. package/dist/web-server/routes/copilot-routes.d.ts +6 -0
  518. package/dist/web-server/routes/copilot-routes.d.ts.map +1 -0
  519. package/dist/web-server/routes/copilot-routes.js +195 -0
  520. package/dist/web-server/routes/copilot-routes.js.map +1 -0
  521. package/dist/web-server/routes/copilot-settings-routes.d.ts +6 -0
  522. package/dist/web-server/routes/copilot-settings-routes.d.ts.map +1 -0
  523. package/dist/web-server/routes/copilot-settings-routes.js +122 -0
  524. package/dist/web-server/routes/copilot-settings-routes.js.map +1 -0
  525. package/dist/web-server/routes/health-routes.d.ts +6 -0
  526. package/dist/web-server/routes/health-routes.d.ts.map +1 -0
  527. package/dist/web-server/routes/health-routes.js +30 -0
  528. package/dist/web-server/routes/health-routes.js.map +1 -0
  529. package/dist/web-server/routes/index.d.ts +8 -0
  530. package/dist/web-server/routes/index.d.ts.map +1 -0
  531. package/dist/web-server/routes/index.js +54 -0
  532. package/dist/web-server/routes/index.js.map +1 -0
  533. package/dist/web-server/routes/misc-routes.d.ts +6 -0
  534. package/dist/web-server/routes/misc-routes.d.ts.map +1 -0
  535. package/dist/web-server/routes/misc-routes.js +219 -0
  536. package/dist/web-server/routes/misc-routes.js.map +1 -0
  537. package/dist/web-server/routes/profile-routes.d.ts +6 -0
  538. package/dist/web-server/routes/profile-routes.d.ts.map +1 -0
  539. package/dist/web-server/routes/profile-routes.js +164 -0
  540. package/dist/web-server/routes/profile-routes.js.map +1 -0
  541. package/dist/web-server/routes/provider-routes.d.ts +6 -0
  542. package/dist/web-server/routes/provider-routes.d.ts.map +1 -0
  543. package/dist/web-server/routes/provider-routes.js +146 -0
  544. package/dist/web-server/routes/provider-routes.js.map +1 -0
  545. package/dist/web-server/routes/route-helpers.d.ts +55 -0
  546. package/dist/web-server/routes/route-helpers.d.ts.map +1 -0
  547. package/dist/web-server/routes/route-helpers.js +201 -0
  548. package/dist/web-server/routes/route-helpers.js.map +1 -0
  549. package/dist/web-server/routes/settings-routes.d.ts +6 -0
  550. package/dist/web-server/routes/settings-routes.d.ts.map +1 -0
  551. package/dist/web-server/routes/settings-routes.js +207 -0
  552. package/dist/web-server/routes/settings-routes.js.map +1 -0
  553. package/dist/web-server/routes/variant-routes.d.ts +6 -0
  554. package/dist/web-server/routes/variant-routes.d.ts.map +1 -0
  555. package/dist/web-server/routes/variant-routes.js +158 -0
  556. package/dist/web-server/routes/variant-routes.js.map +1 -0
  557. package/dist/web-server/routes/websearch-routes.d.ts +6 -0
  558. package/dist/web-server/routes/websearch-routes.d.ts.map +1 -0
  559. package/dist/web-server/routes/websearch-routes.js +130 -0
  560. package/dist/web-server/routes/websearch-routes.js.map +1 -0
  561. package/dist/web-server/services/index.d.ts +7 -0
  562. package/dist/web-server/services/index.d.ts.map +1 -0
  563. package/dist/web-server/services/index.js +21 -0
  564. package/dist/web-server/services/index.js.map +1 -0
  565. package/dist/web-server/services/usage-aggregator.d.ts +7 -0
  566. package/dist/web-server/services/usage-aggregator.d.ts.map +1 -0
  567. package/dist/web-server/services/usage-aggregator.js +23 -0
  568. package/dist/web-server/services/usage-aggregator.js.map +1 -0
  569. package/dist/web-server/usage/aggregator.d.ts +54 -0
  570. package/dist/web-server/usage/aggregator.d.ts.map +1 -0
  571. package/dist/web-server/usage/aggregator.js +470 -0
  572. package/dist/web-server/usage/aggregator.js.map +1 -0
  573. package/dist/web-server/usage/data-aggregator.d.ts +55 -0
  574. package/dist/web-server/usage/data-aggregator.d.ts.map +1 -0
  575. package/dist/web-server/usage/data-aggregator.js +391 -0
  576. package/dist/web-server/usage/data-aggregator.js.map +1 -0
  577. package/dist/web-server/usage/disk-cache.d.ts +46 -0
  578. package/dist/web-server/usage/disk-cache.d.ts.map +1 -0
  579. package/dist/web-server/usage/disk-cache.js +162 -0
  580. package/dist/web-server/usage/disk-cache.js.map +1 -0
  581. package/dist/web-server/usage/handlers.d.ts +50 -0
  582. package/dist/web-server/usage/handlers.d.ts.map +1 -0
  583. package/dist/web-server/usage/handlers.js +524 -0
  584. package/dist/web-server/usage/handlers.js.map +1 -0
  585. package/dist/web-server/usage/index.d.ts +12 -0
  586. package/dist/web-server/usage/index.d.ts.map +1 -0
  587. package/dist/web-server/usage/index.js +54 -0
  588. package/dist/web-server/usage/index.js.map +1 -0
  589. package/dist/web-server/usage/routes.d.ts +11 -0
  590. package/dist/web-server/usage/routes.d.ts.map +1 -0
  591. package/dist/web-server/usage/routes.js +37 -0
  592. package/dist/web-server/usage/routes.js.map +1 -0
  593. package/dist/web-server/usage/types.d.ts +119 -0
  594. package/dist/web-server/usage/types.d.ts.map +1 -0
  595. package/dist/web-server/usage/types.js +9 -0
  596. package/dist/web-server/usage/types.js.map +1 -0
  597. package/dist/web-server/usage-disk-cache.d.ts +3 -42
  598. package/dist/web-server/usage-disk-cache.d.ts.map +1 -1
  599. package/dist/web-server/usage-disk-cache.js +5 -144
  600. package/dist/web-server/usage-disk-cache.js.map +1 -1
  601. package/dist/web-server/usage-routes.d.ts +3 -31
  602. package/dist/web-server/usage-routes.d.ts.map +1 -1
  603. package/dist/web-server/usage-routes.js +8 -1139
  604. package/dist/web-server/usage-routes.js.map +1 -1
  605. package/dist/web-server/usage-types.d.ts +3 -115
  606. package/dist/web-server/usage-types.d.ts.map +1 -1
  607. package/dist/web-server/usage-types.js +17 -3
  608. package/dist/web-server/usage-types.js.map +1 -1
  609. package/package.json +1 -1
  610. package/dist/ui/assets/analytics-D1k2nYcF.js +0 -1
  611. package/dist/ui/assets/api-D9VVmZ1j.js +0 -1
  612. package/dist/ui/assets/cliproxy-CTt_Nsft.js +0 -1
  613. package/dist/ui/assets/cliproxy-control-panel-DKMAi4_4.js +0 -1
  614. package/dist/ui/assets/copilot-C6F8PT_L.js +0 -4
  615. package/dist/ui/assets/form-utils-BuXDJb0w.js +0 -20
  616. package/dist/ui/assets/health-DiAvJaSn.js +0 -1
  617. package/dist/ui/assets/index-Brq6EBKZ.css +0 -1
  618. package/dist/ui/assets/index-ByU8ZhED.js +0 -48
  619. package/dist/ui/assets/settings-CEomZLGl.js +0 -1
  620. package/dist/web-server/routes.d.ts +0 -7
  621. package/dist/web-server/routes.d.ts.map +0 -1
  622. package/dist/web-server/routes.js +0 -1891
  623. package/dist/web-server/routes.js.map +0 -1
@@ -1,1145 +1,14 @@
1
1
  "use strict";
2
2
  /**
3
- * Usage Analytics API Routes
3
+ * Usage Analytics API Routes - Re-export from modularized location
4
4
  *
5
- * Provides REST endpoints for Claude Code usage analytics.
6
- * Supports daily, monthly, and session-based usage data aggregation.
7
- *
8
- * Performance optimizations:
9
- * - Persistent disk cache to avoid re-parsing JSONL files on startup
10
- * - TTL-based in-memory caching for fast repeated requests
11
- * - Request coalescing to prevent duplicate concurrent requests
12
- * - Non-blocking prewarm with instant stale data serving
5
+ * @deprecated Import from './usage/routes' instead
13
6
  */
14
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- var desc = Object.getOwnPropertyDescriptor(m, k);
17
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
- desc = { enumerable: true, get: function() { return m[k]; } };
19
- }
20
- Object.defineProperty(o, k2, desc);
21
- }) : (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- o[k2] = m[k];
24
- }));
25
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
- Object.defineProperty(o, "default", { enumerable: true, value: v });
27
- }) : function(o, v) {
28
- o["default"] = v;
29
- });
30
- var __importStar = (this && this.__importStar) || function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
7
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.prewarmUsageCache = exports.clearUsageCache = exports.getLastFetchTimestamp = exports.usageRoutes = void 0;
39
- const express_1 = require("express");
40
- const fs = __importStar(require("fs"));
41
- const path = __importStar(require("path"));
42
- const os = __importStar(require("os"));
43
- const data_aggregator_1 = require("./data-aggregator");
44
- const model_pricing_1 = require("./model-pricing");
45
- const usage_disk_cache_1 = require("./usage-disk-cache");
46
- const ui_1 = require("../utils/ui");
47
- // ============================================================================
48
- // Multi-Instance Support - Aggregate usage from CCS profiles
49
- // ============================================================================
50
- /** Path to CCS instances directory */
51
- const CCS_INSTANCES_DIR = path.join(os.homedir(), '.ccs', 'instances');
52
- /**
53
- * Get list of CCS instance paths that have usage data
54
- * Only returns instances with existing projects/ directory
55
- */
56
- function getInstancePaths() {
57
- if (!fs.existsSync(CCS_INSTANCES_DIR)) {
58
- return [];
59
- }
60
- try {
61
- const entries = fs.readdirSync(CCS_INSTANCES_DIR, { withFileTypes: true });
62
- return entries
63
- .filter((entry) => entry.isDirectory())
64
- .map((entry) => path.join(CCS_INSTANCES_DIR, entry.name))
65
- .filter((instancePath) => {
66
- // Only include instances that have a projects directory
67
- const projectsPath = path.join(instancePath, 'projects');
68
- return fs.existsSync(projectsPath);
69
- });
70
- }
71
- catch {
72
- console.error((0, ui_1.fail)('Failed to read CCS instances directory'));
73
- return [];
74
- }
75
- }
76
- /**
77
- * Load usage data from a specific instance
78
- * Uses custom JSONL parser with instance's projects directory
79
- */
80
- async function loadInstanceData(instancePath) {
81
- try {
82
- const projectsDir = path.join(instancePath, 'projects');
83
- const result = await (0, data_aggregator_1.loadAllUsageData)({ projectsDir });
84
- return result;
85
- }
86
- catch (_err) {
87
- // Instance may have no usage data - that's OK
88
- const instanceName = path.basename(instancePath);
89
- console.log((0, ui_1.info)(`No usage data in instance: ${instanceName}`));
90
- return { daily: [], hourly: [], monthly: [], session: [] };
91
- }
92
- }
93
- /**
94
- * Merge daily usage data from multiple sources
95
- * Combines entries with same date by aggregating tokens
96
- */
97
- function mergeDailyData(sources) {
98
- const dateMap = new Map();
99
- for (const source of sources) {
100
- for (const day of source) {
101
- const existing = dateMap.get(day.date);
102
- if (existing) {
103
- // Aggregate tokens for same date
104
- existing.inputTokens += day.inputTokens;
105
- existing.outputTokens += day.outputTokens;
106
- existing.cacheCreationTokens += day.cacheCreationTokens;
107
- existing.cacheReadTokens += day.cacheReadTokens;
108
- existing.totalCost += day.totalCost;
109
- // Merge unique models
110
- const modelSet = new Set([...existing.modelsUsed, ...day.modelsUsed]);
111
- existing.modelsUsed = Array.from(modelSet);
112
- // Merge model breakdowns by aggregating same modelName
113
- for (const breakdown of day.modelBreakdowns) {
114
- const existingBreakdown = existing.modelBreakdowns.find((b) => b.modelName === breakdown.modelName);
115
- if (existingBreakdown) {
116
- existingBreakdown.inputTokens += breakdown.inputTokens;
117
- existingBreakdown.outputTokens += breakdown.outputTokens;
118
- existingBreakdown.cacheCreationTokens += breakdown.cacheCreationTokens;
119
- existingBreakdown.cacheReadTokens += breakdown.cacheReadTokens;
120
- existingBreakdown.cost += breakdown.cost;
121
- }
122
- else {
123
- existing.modelBreakdowns.push({ ...breakdown });
124
- }
125
- }
126
- }
127
- else {
128
- // Clone to avoid mutating original
129
- dateMap.set(day.date, {
130
- ...day,
131
- modelsUsed: [...day.modelsUsed],
132
- modelBreakdowns: day.modelBreakdowns.map((b) => ({ ...b })),
133
- });
134
- }
135
- }
136
- }
137
- return Array.from(dateMap.values()).sort((a, b) => a.date.localeCompare(b.date));
138
- }
139
- /**
140
- * Merge monthly usage data from multiple sources
141
- */
142
- function mergeMonthlyData(sources) {
143
- const monthMap = new Map();
144
- for (const source of sources) {
145
- for (const month of source) {
146
- const existing = monthMap.get(month.month);
147
- if (existing) {
148
- existing.inputTokens += month.inputTokens;
149
- existing.outputTokens += month.outputTokens;
150
- existing.cacheCreationTokens += month.cacheCreationTokens;
151
- existing.cacheReadTokens += month.cacheReadTokens;
152
- existing.totalCost += month.totalCost;
153
- const modelSet = new Set([...existing.modelsUsed, ...month.modelsUsed]);
154
- existing.modelsUsed = Array.from(modelSet);
155
- }
156
- else {
157
- monthMap.set(month.month, { ...month, modelsUsed: [...month.modelsUsed] });
158
- }
159
- }
160
- }
161
- return Array.from(monthMap.values()).sort((a, b) => a.month.localeCompare(b.month));
162
- }
163
- /**
164
- * Merge hourly usage data from multiple sources
165
- * Combines entries with same hour by aggregating tokens
166
- */
167
- function mergeHourlyData(sources) {
168
- const hourMap = new Map();
169
- for (const source of sources) {
170
- for (const hour of source) {
171
- const existing = hourMap.get(hour.hour);
172
- if (existing) {
173
- existing.inputTokens += hour.inputTokens;
174
- existing.outputTokens += hour.outputTokens;
175
- existing.cacheCreationTokens += hour.cacheCreationTokens;
176
- existing.cacheReadTokens += hour.cacheReadTokens;
177
- existing.totalCost += hour.totalCost;
178
- const modelSet = new Set([...existing.modelsUsed, ...hour.modelsUsed]);
179
- existing.modelsUsed = Array.from(modelSet);
180
- // Merge model breakdowns
181
- for (const breakdown of hour.modelBreakdowns) {
182
- const existingBreakdown = existing.modelBreakdowns.find((b) => b.modelName === breakdown.modelName);
183
- if (existingBreakdown) {
184
- existingBreakdown.inputTokens += breakdown.inputTokens;
185
- existingBreakdown.outputTokens += breakdown.outputTokens;
186
- existingBreakdown.cacheCreationTokens += breakdown.cacheCreationTokens;
187
- existingBreakdown.cacheReadTokens += breakdown.cacheReadTokens;
188
- existingBreakdown.cost += breakdown.cost;
189
- }
190
- else {
191
- existing.modelBreakdowns.push({ ...breakdown });
192
- }
193
- }
194
- }
195
- else {
196
- hourMap.set(hour.hour, {
197
- ...hour,
198
- modelsUsed: [...hour.modelsUsed],
199
- modelBreakdowns: hour.modelBreakdowns.map((b) => ({ ...b })),
200
- });
201
- }
202
- }
203
- }
204
- return Array.from(hourMap.values()).sort((a, b) => a.hour.localeCompare(b.hour));
205
- }
206
- /**
207
- * Merge session data from multiple sources
208
- * Deduplicates by sessionId (same session shouldn't appear in multiple instances)
209
- */
210
- function mergeSessionData(sources) {
211
- const sessionMap = new Map();
212
- for (const source of sources) {
213
- for (const session of source) {
214
- // Use sessionId as unique key - later entries overwrite earlier ones
215
- sessionMap.set(session.sessionId, session);
216
- }
217
- }
218
- return Array.from(sessionMap.values()).sort((a, b) => new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime());
219
- }
220
- exports.usageRoutes = (0, express_1.Router)();
221
- // Constants for validation
222
- const MAX_LIMIT = 1000;
223
- const DEFAULT_LIMIT = 50;
224
- const DATE_REGEX = /^\d{8}$/; // YYYYMMDD format
225
- // Cache TTLs (milliseconds)
226
- const CACHE_TTL = {
227
- daily: 60 * 1000, // 1 minute - changes frequently
228
- monthly: 5 * 60 * 1000, // 5 minutes - aggregated data
229
- session: 60 * 1000, // 1 minute - user may refresh
230
- };
231
- /// Stale-while-revalidate: max age for stale data (7 days)
232
- // We always show cached data to avoid blocking UI, refresh happens in background
233
- const STALE_TTL = 7 * 24 * 60 * 60 * 1000;
234
- // Track when data was last fetched (for UI indicator)
235
- let lastFetchTimestamp = null;
236
- /** Get timestamp of last successful data fetch */
237
- function getLastFetchTimestamp() {
238
- return lastFetchTimestamp;
239
- }
240
- exports.getLastFetchTimestamp = getLastFetchTimestamp;
241
- // In-memory cache
242
- const cache = new Map();
243
- // Pending requests for coalescing (prevents duplicate concurrent calls)
244
- const pendingRequests = new Map();
245
- // Track if disk cache has been loaded into memory
246
- let diskCacheInitialized = false;
247
- /**
248
- * Persist cache to disk when we have enough data to be useful.
249
- * Writes immediately with whatever data is available (empty arrays for missing).
250
- * This ensures disk cache is created after first Analytics page visit.
251
- */
252
- function persistCacheIfComplete() {
253
- const daily = cache.get('daily');
254
- const hourly = cache.get('hourly');
255
- const monthly = cache.get('monthly');
256
- const session = cache.get('session');
257
- // Write if we have at least daily data (the most essential)
258
- if (daily) {
259
- (0, usage_disk_cache_1.writeDiskCache)(daily.data, hourly?.data ?? [], monthly?.data ?? [], session?.data ?? []);
260
- }
261
- }
262
- /**
263
- * Get cached data or fetch from loader with TTL
264
- * Also coalesces concurrent requests to prevent duplicate library calls
265
- * Implements stale-while-revalidate pattern for instant responses
266
- */
267
- async function getCachedData(key, ttl, loader) {
268
- // Ensure disk cache is loaded on first request
269
- ensureDiskCacheLoaded();
270
- const cached = cache.get(key);
271
- const now = Date.now();
272
- // Fresh cache - return immediately
273
- if (cached && now - cached.timestamp < ttl) {
274
- return cached.data;
275
- }
276
- // Stale cache - return immediately, refresh in background (SWR pattern)
277
- if (cached && now - cached.timestamp < STALE_TTL) {
278
- // Fire and forget background refresh if not already pending
279
- if (!pendingRequests.has(key)) {
280
- const promise = loader()
281
- .then((data) => {
282
- cache.set(key, { data, timestamp: Date.now() });
283
- lastFetchTimestamp = Date.now();
284
- // Persist to disk if all data types are cached
285
- persistCacheIfComplete();
286
- })
287
- .catch((err) => {
288
- console.error((0, ui_1.fail)(`Background refresh failed for ${key}: ${err}`));
289
- })
290
- .finally(() => {
291
- pendingRequests.delete(key);
292
- });
293
- pendingRequests.set(key, promise);
294
- }
295
- return cached.data;
296
- }
297
- // No usable cache - check if request is already pending (coalesce)
298
- const pending = pendingRequests.get(key);
299
- if (pending) {
300
- return pending;
301
- }
302
- // Create new request
303
- const promise = loader()
304
- .then((data) => {
305
- cache.set(key, { data, timestamp: Date.now() });
306
- lastFetchTimestamp = Date.now();
307
- // Persist to disk if all data types are cached
308
- persistCacheIfComplete();
309
- return data;
310
- })
311
- .finally(() => {
312
- pendingRequests.delete(key);
313
- });
314
- pendingRequests.set(key, promise);
315
- return promise;
316
- }
317
- /** Cached loader for daily usage data */
318
- async function getCachedDailyData() {
319
- return getCachedData('daily', CACHE_TTL.daily, async () => {
320
- return await (0, data_aggregator_1.loadDailyUsageData)();
321
- });
322
- }
323
- /** Cached loader for monthly usage data */
324
- async function getCachedMonthlyData() {
325
- return getCachedData('monthly', CACHE_TTL.monthly, async () => {
326
- return await (0, data_aggregator_1.loadMonthlyUsageData)();
327
- });
328
- }
329
- /** Cached loader for session data */
330
- async function getCachedSessionData() {
331
- return getCachedData('session', CACHE_TTL.session, async () => {
332
- return await (0, data_aggregator_1.loadSessionData)();
333
- });
334
- }
335
- /** Cached loader for hourly usage data */
336
- async function getCachedHourlyData() {
337
- return getCachedData('hourly', CACHE_TTL.daily, async () => {
338
- return await (0, data_aggregator_1.loadHourlyUsageData)();
339
- });
340
- }
341
- /**
342
- * Clear all cached data (useful for manual refresh)
343
- */
344
- function clearUsageCache() {
345
- cache.clear();
346
- (0, usage_disk_cache_1.clearDiskCache)();
347
- // Reset so next API call will try to reload from disk/source
348
- diskCacheInitialized = false;
349
- }
350
- exports.clearUsageCache = clearUsageCache;
351
- // Track if background refresh is in progress
352
- let isRefreshing = false;
353
- /**
354
- * Load fresh data and update both memory and disk caches
355
- * Aggregates data from default ~/.claude/ AND all CCS instances
356
- */
357
- async function refreshFromSource() {
358
- // Load default data (from ~/.claude/projects/ or CLAUDE_CONFIG_DIR)
359
- const defaultData = await (0, data_aggregator_1.loadAllUsageData)();
360
- // Load data from all CCS instances sequentially (to avoid env var race condition)
361
- const instancePaths = getInstancePaths();
362
- const instanceDataResults = [];
363
- for (const instancePath of instancePaths) {
364
- try {
365
- const data = await loadInstanceData(instancePath);
366
- instanceDataResults.push(data);
367
- }
368
- catch (err) {
369
- const instanceName = path.basename(instancePath);
370
- console.error((0, ui_1.fail)(`Failed to load instance ${instanceName}: ${err}`));
371
- }
372
- }
373
- // Collect successful instance data
374
- const allDailySources = [defaultData.daily];
375
- const allHourlySources = [defaultData.hourly];
376
- const allMonthlySources = [defaultData.monthly];
377
- const allSessionSources = [defaultData.session];
378
- for (const result of instanceDataResults) {
379
- allDailySources.push(result.daily);
380
- allHourlySources.push(result.hourly);
381
- allMonthlySources.push(result.monthly);
382
- allSessionSources.push(result.session);
383
- }
384
- if (instanceDataResults.length > 0) {
385
- console.log((0, ui_1.info)(`Aggregated usage data from ${instanceDataResults.length} CCS instance(s)`));
386
- }
387
- // Merge all data sources
388
- const daily = mergeDailyData(allDailySources);
389
- const hourly = mergeHourlyData(allHourlySources);
390
- const monthly = mergeMonthlyData(allMonthlySources);
391
- const session = mergeSessionData(allSessionSources);
392
- // Update in-memory cache
393
- const now = Date.now();
394
- cache.set('daily', { data: daily, timestamp: now });
395
- cache.set('hourly', { data: hourly, timestamp: now });
396
- cache.set('monthly', { data: monthly, timestamp: now });
397
- cache.set('session', { data: session, timestamp: now });
398
- lastFetchTimestamp = now;
399
- // Persist to disk
400
- (0, usage_disk_cache_1.writeDiskCache)(daily, hourly, monthly, session);
401
- return { daily, hourly, monthly, session };
402
- }
403
- // ============================================================================
404
- // Module Initialization - Load disk cache immediately for instant API responses
405
- // ============================================================================
406
- /**
407
- * Initialize in-memory cache from disk cache (lazy - called on first API request).
408
- * This ensures first API request gets instant data without calling better-ccusage.
409
- * Background refresh is NOT triggered here - it happens via SWR pattern in getCachedData().
410
- */
411
- function ensureDiskCacheLoaded() {
412
- if (diskCacheInitialized)
413
- return;
414
- diskCacheInitialized = true;
415
- const diskCache = (0, usage_disk_cache_1.readDiskCache)();
416
- if (!diskCache)
417
- return;
418
- // Load disk cache into memory (regardless of freshness)
419
- // SWR pattern in getCachedData() will handle background refresh
420
- cache.set('daily', { data: diskCache.daily, timestamp: diskCache.timestamp });
421
- cache.set('hourly', { data: diskCache.hourly || [], timestamp: diskCache.timestamp });
422
- cache.set('monthly', { data: diskCache.monthly, timestamp: diskCache.timestamp });
423
- cache.set('session', { data: diskCache.session, timestamp: diskCache.timestamp });
424
- lastFetchTimestamp = diskCache.timestamp;
425
- }
426
- /**
427
- * Pre-warm usage caches on server startup
428
- *
429
- * Strategy:
430
- * 1. Check disk cache - if fresh, use it (instant startup)
431
- * 2. If stale, use it immediately but trigger background refresh
432
- * 3. If no cache, return immediately and let first request trigger load
433
- *
434
- * This ensures dashboard opens in <1s regardless of cache state
435
- */
436
- async function prewarmUsageCache() {
437
- const start = Date.now();
438
- console.log((0, ui_1.info)('Pre-warming usage cache...'));
439
- try {
440
- const diskCache = (0, usage_disk_cache_1.readDiskCache)();
441
- // Fresh disk cache - use it directly
442
- if (diskCache && (0, usage_disk_cache_1.isDiskCacheFresh)(diskCache)) {
443
- const now = Date.now();
444
- cache.set('daily', { data: diskCache.daily, timestamp: diskCache.timestamp });
445
- cache.set('hourly', { data: diskCache.hourly || [], timestamp: diskCache.timestamp });
446
- cache.set('monthly', { data: diskCache.monthly, timestamp: diskCache.timestamp });
447
- cache.set('session', { data: diskCache.session, timestamp: diskCache.timestamp });
448
- lastFetchTimestamp = diskCache.timestamp;
449
- const elapsed = Date.now() - start;
450
- console.log((0, ui_1.ok)(`Usage cache ready from disk (${elapsed}ms, cached ${(0, usage_disk_cache_1.getCacheAge)(diskCache)})`));
451
- return { timestamp: now, elapsed, source: 'disk-fresh' };
452
- }
453
- // Stale disk cache - use it immediately, refresh in background
454
- if (diskCache && (0, usage_disk_cache_1.isDiskCacheStale)(diskCache)) {
455
- const now = Date.now();
456
- cache.set('daily', { data: diskCache.daily, timestamp: diskCache.timestamp });
457
- cache.set('hourly', { data: diskCache.hourly || [], timestamp: diskCache.timestamp });
458
- cache.set('monthly', { data: diskCache.monthly, timestamp: diskCache.timestamp });
459
- cache.set('session', { data: diskCache.session, timestamp: diskCache.timestamp });
460
- lastFetchTimestamp = diskCache.timestamp;
461
- const elapsed = Date.now() - start;
462
- console.log((0, ui_1.ok)(`Usage cache ready from disk (${elapsed}ms, stale ${(0, usage_disk_cache_1.getCacheAge)(diskCache)}, refreshing...)`));
463
- // Background refresh
464
- if (!isRefreshing) {
465
- isRefreshing = true;
466
- refreshFromSource()
467
- .then(() => console.log((0, ui_1.ok)('Background refresh complete')))
468
- .catch((err) => console.error((0, ui_1.fail)(`Background refresh failed: ${err}`)))
469
- .finally(() => {
470
- isRefreshing = false;
471
- });
472
- }
473
- return { timestamp: now, elapsed, source: 'disk-stale' };
474
- }
475
- // No usable disk cache - refresh from source (blocking for first startup only)
476
- console.log((0, ui_1.info)('No disk cache, loading from source...'));
477
- await refreshFromSource();
478
- const elapsed = Date.now() - start;
479
- console.log((0, ui_1.ok)(`Usage cache ready (${elapsed}ms)`));
480
- return { timestamp: Date.now(), elapsed, source: 'fresh' };
481
- }
482
- catch (err) {
483
- console.error((0, ui_1.fail)(`Failed to prewarm usage cache: ${err}`));
484
- throw err;
485
- }
486
- }
487
- exports.prewarmUsageCache = prewarmUsageCache;
488
- // ============================================================================
489
- // Validation Helpers
490
- // ============================================================================
491
- /**
492
- * Validate date string in YYYYMMDD format
493
- */
494
- function validateDate(dateString) {
495
- if (!dateString)
496
- return undefined;
497
- if (!DATE_REGEX.test(dateString)) {
498
- throw new Error('Invalid date format. Use YYYYMMDD');
499
- }
500
- // Basic range check
501
- const year = parseInt(dateString.substring(0, 4), 10);
502
- const month = parseInt(dateString.substring(4, 6), 10);
503
- const day = parseInt(dateString.substring(6, 8), 10);
504
- if (year < 2024 || year > 2100)
505
- throw new Error('Year out of valid range');
506
- if (month < 1 || month > 12)
507
- throw new Error('Month out of valid range');
508
- if (day < 1 || day > 31)
509
- throw new Error('Day out of valid range');
510
- return dateString;
511
- }
512
- /**
513
- * Validate and parse limit parameter
514
- */
515
- function validateLimit(limit) {
516
- if (!limit)
517
- return DEFAULT_LIMIT;
518
- const num = parseInt(limit, 10);
519
- if (isNaN(num) || num < 1 || num > MAX_LIMIT) {
520
- throw new Error(`Limit must be between 1 and ${MAX_LIMIT}`);
521
- }
522
- return num;
523
- }
524
- /**
525
- * Validate and parse offset parameter
526
- */
527
- function validateOffset(offset) {
528
- if (!offset)
529
- return 0;
530
- const num = parseInt(offset, 10);
531
- if (isNaN(num) || num < 0) {
532
- throw new Error('Offset must be a non-negative number');
533
- }
534
- return num;
535
- }
536
- /**
537
- * Filter data by date range
538
- */
539
- function filterByDateRange(data, since, until) {
540
- if (!data || !Array.isArray(data))
541
- return [];
542
- if (!since && !until)
543
- return data;
544
- return data.filter((item) => {
545
- // Get the date field (prioritize date, then month, then lastActivity)
546
- const itemDate = item.date || item.month?.replace('-', '') || item.lastActivity?.replace(/-/g, '');
547
- if (!itemDate)
548
- return true;
549
- // Normalize to YYYYMMDD for comparison
550
- const normalizedDate = itemDate.replace(/-/g, '').substring(0, 8);
551
- if (since && normalizedDate < since)
552
- return false;
553
- if (until && normalizedDate > until)
554
- return false;
555
- return true;
556
- });
557
- }
558
- /**
559
- * Create standard error response
560
- */
561
- function errorResponse(res, error, defaultMessage) {
562
- console.error(defaultMessage + ':', error);
563
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
564
- const isValidationError = errorMessage.includes('Invalid') ||
565
- errorMessage.includes('format') ||
566
- errorMessage.includes('range') ||
567
- errorMessage.includes('must be');
568
- const statusCode = isValidationError ? 400 : 500;
569
- res.status(statusCode).json({
570
- success: false,
571
- error: isValidationError ? errorMessage : defaultMessage,
572
- });
573
- }
574
- /**
575
- * Calculate cost breakdown for token categories
576
- * Uses weighted average pricing across models in the dataset
577
- */
578
- function calculateTokenBreakdownCosts(dailyData) {
579
- let inputTokens = 0;
580
- let outputTokens = 0;
581
- let cacheCreationTokens = 0;
582
- let cacheReadTokens = 0;
583
- let inputCost = 0;
584
- let outputCost = 0;
585
- let cacheCreationCost = 0;
586
- let cacheReadCost = 0;
587
- for (const day of dailyData) {
588
- for (const breakdown of day.modelBreakdowns) {
589
- const pricing = (0, model_pricing_1.getModelPricing)(breakdown.modelName);
590
- inputTokens += breakdown.inputTokens;
591
- outputTokens += breakdown.outputTokens;
592
- cacheCreationTokens += breakdown.cacheCreationTokens;
593
- cacheReadTokens += breakdown.cacheReadTokens;
594
- inputCost += (breakdown.inputTokens / 1000000) * pricing.inputPerMillion;
595
- outputCost += (breakdown.outputTokens / 1000000) * pricing.outputPerMillion;
596
- cacheCreationCost +=
597
- (breakdown.cacheCreationTokens / 1000000) * pricing.cacheCreationPerMillion;
598
- cacheReadCost += (breakdown.cacheReadTokens / 1000000) * pricing.cacheReadPerMillion;
599
- }
600
- }
601
- return {
602
- input: { tokens: inputTokens, cost: Math.round(inputCost * 100) / 100 },
603
- output: { tokens: outputTokens, cost: Math.round(outputCost * 100) / 100 },
604
- cacheCreation: { tokens: cacheCreationTokens, cost: Math.round(cacheCreationCost * 100) / 100 },
605
- cacheRead: { tokens: cacheReadTokens, cost: Math.round(cacheReadCost * 100) / 100 },
606
- };
607
- }
608
- /**
609
- * GET /api/usage/summary
610
- *
611
- * Returns usage summary data for quick dashboard display.
612
- * Query: ?since=YYYYMMDD&until=YYYYMMDD
613
- */
614
- exports.usageRoutes.get('/summary', async (req, res) => {
615
- try {
616
- const since = validateDate(req.query.since);
617
- const until = validateDate(req.query.until);
618
- const dailyData = await getCachedDailyData();
619
- const filtered = filterByDateRange(dailyData, since, until);
620
- // Calculate totals
621
- let totalInputTokens = 0;
622
- let totalOutputTokens = 0;
623
- let totalCacheCreationTokens = 0;
624
- let totalCacheReadTokens = 0;
625
- let totalCost = 0;
626
- for (const day of filtered) {
627
- totalInputTokens += day.inputTokens;
628
- totalOutputTokens += day.outputTokens;
629
- totalCacheCreationTokens += day.cacheCreationTokens;
630
- totalCacheReadTokens += day.cacheReadTokens;
631
- totalCost += day.totalCost;
632
- }
633
- const totalTokens = totalInputTokens + totalOutputTokens;
634
- const totalCacheTokens = totalCacheCreationTokens + totalCacheReadTokens;
635
- // Calculate detailed token breakdown with costs
636
- const tokenBreakdown = calculateTokenBreakdownCosts(filtered);
637
- res.json({
638
- success: true,
639
- data: {
640
- totalTokens,
641
- totalInputTokens,
642
- totalOutputTokens,
643
- totalCacheTokens,
644
- totalCacheCreationTokens,
645
- totalCacheReadTokens,
646
- totalCost: Math.round(totalCost * 100) / 100,
647
- tokenBreakdown,
648
- totalDays: filtered.length,
649
- averageTokensPerDay: filtered.length > 0 ? Math.round(totalTokens / filtered.length) : 0,
650
- averageCostPerDay: filtered.length > 0 ? Math.round((totalCost / filtered.length) * 100) / 100 : 0,
651
- },
652
- });
653
- }
654
- catch (error) {
655
- errorResponse(res, error, 'Failed to fetch usage summary');
656
- }
657
- });
658
- /**
659
- * GET /api/usage/daily
660
- *
661
- * Returns daily usage trends for chart visualization.
662
- * Query: ?since=YYYYMMDD&until=YYYYMMDD
663
- */
664
- exports.usageRoutes.get('/daily', async (req, res) => {
665
- try {
666
- const since = validateDate(req.query.since);
667
- const until = validateDate(req.query.until);
668
- const dailyData = await getCachedDailyData();
669
- const filtered = filterByDateRange(dailyData, since, until);
670
- // Transform for chart consumption
671
- const trends = filtered.map((day) => ({
672
- date: day.date,
673
- tokens: day.inputTokens + day.outputTokens,
674
- inputTokens: day.inputTokens,
675
- outputTokens: day.outputTokens,
676
- cacheTokens: day.cacheCreationTokens + day.cacheReadTokens,
677
- cost: Math.round(day.totalCost * 100) / 100,
678
- modelsUsed: day.modelsUsed.length,
679
- }));
680
- res.json({
681
- success: true,
682
- data: trends,
683
- });
684
- }
685
- catch (error) {
686
- errorResponse(res, error, 'Failed to fetch daily usage');
687
- }
688
- });
689
- /**
690
- * GET /api/usage/hourly
691
- *
692
- * Returns hourly usage trends for chart visualization.
693
- * Query: ?since=YYYYMMDD&until=YYYYMMDD (defaults to last 24 hours)
694
- * Fills in gaps with zero values for hours without activity.
695
- */
696
- exports.usageRoutes.get('/hourly', async (req, res) => {
697
- try {
698
- const since = validateDate(req.query.since);
699
- const until = validateDate(req.query.until);
700
- const hourlyData = await getCachedHourlyData();
701
- // Filter by date range (guard against undefined)
702
- const filtered = (hourlyData || []).filter((h) => {
703
- // Extract date from hour format "YYYY-MM-DD HH:00"
704
- const hourDate = h.hour.slice(0, 10).replace(/-/g, '');
705
- if (since && hourDate < since)
706
- return false;
707
- if (until && hourDate > until)
708
- return false;
709
- return true;
710
- });
711
- // Transform for chart consumption
712
- const trends = filtered.map((hour) => ({
713
- hour: hour.hour,
714
- tokens: hour.inputTokens + hour.outputTokens,
715
- inputTokens: hour.inputTokens,
716
- outputTokens: hour.outputTokens,
717
- cacheTokens: hour.cacheCreationTokens + hour.cacheReadTokens,
718
- cost: Math.round(hour.totalCost * 100) / 100,
719
- modelsUsed: hour.modelsUsed.length,
720
- requests: hour.modelBreakdowns.length,
721
- }));
722
- // Fill gaps with zero values for hours without activity
723
- const filledTrends = fillHourlyGaps(trends, since, until);
724
- res.json({
725
- success: true,
726
- data: filledTrends,
727
- });
728
- }
729
- catch (error) {
730
- errorResponse(res, error, 'Failed to fetch hourly usage');
731
- }
732
- });
733
- /**
734
- * Fill gaps in hourly data with zero values
735
- * Ensures continuous timeline for chart display
736
- */
737
- function fillHourlyGaps(data, since, until) {
738
- // If no date range specified, return as-is
739
- if (!since && !until) {
740
- return data.sort((a, b) => a.hour.localeCompare(b.hour));
741
- }
742
- // Create a map of existing hours for O(1) lookup
743
- const hourMap = new Map(data.map((d) => [d.hour, d]));
744
- // Determine the hour range (use UTC to match stored hour keys)
745
- const now = new Date();
746
- const startDate = since
747
- ? new Date(Date.UTC(parseInt(since.slice(0, 4)), parseInt(since.slice(4, 6)) - 1, parseInt(since.slice(6, 8)), 0, 0, 0))
748
- : new Date(now.getTime() - 24 * 60 * 60 * 1000); // Default: 24 hours ago
749
- const endDate = until
750
- ? new Date(Date.UTC(parseInt(until.slice(0, 4)), parseInt(until.slice(4, 6)) - 1, parseInt(until.slice(6, 8)), 23, 59, 59))
751
- : now;
752
- // Cap endDate at current time to avoid filling future hours with zeros
753
- const cappedEndDate = endDate > now ? now : endDate;
754
- const result = [];
755
- // Iterate through each hour in the range
756
- const current = new Date(startDate);
757
- current.setMinutes(0, 0, 0);
758
- while (current <= cappedEndDate) {
759
- // Format hour key as "YYYY-MM-DD HH:00" in UTC to match storage format
760
- const year = current.getUTCFullYear();
761
- const month = String(current.getUTCMonth() + 1).padStart(2, '0');
762
- const day = String(current.getUTCDate()).padStart(2, '0');
763
- const hour = String(current.getUTCHours()).padStart(2, '0');
764
- const hourKey = `${year}-${month}-${day} ${hour}:00`;
765
- if (hourMap.has(hourKey)) {
766
- const entry = hourMap.get(hourKey);
767
- if (entry) {
768
- result.push(entry);
769
- }
770
- }
771
- else {
772
- // Insert zero entry for this hour
773
- result.push({
774
- hour: hourKey,
775
- tokens: 0,
776
- inputTokens: 0,
777
- outputTokens: 0,
778
- cacheTokens: 0,
779
- cost: 0,
780
- modelsUsed: 0,
781
- requests: 0,
782
- });
783
- }
784
- // Move to next hour
785
- current.setTime(current.getTime() + 60 * 60 * 1000);
786
- }
787
- return result;
788
- }
789
- /**
790
- * GET /api/usage/models
791
- *
792
- * Returns usage breakdown by model for pie/bar charts.
793
- * Query: ?since=YYYYMMDD&until=YYYYMMDD
794
- */
795
- exports.usageRoutes.get('/models', async (req, res) => {
796
- try {
797
- const since = validateDate(req.query.since);
798
- const until = validateDate(req.query.until);
799
- const dailyData = await getCachedDailyData();
800
- const filtered = filterByDateRange(dailyData, since, until);
801
- // Aggregate model usage across all days with detailed breakdown
802
- const modelMap = new Map();
803
- for (const day of filtered) {
804
- for (const breakdown of day.modelBreakdowns) {
805
- const existing = modelMap.get(breakdown.modelName) || {
806
- model: breakdown.modelName,
807
- inputTokens: 0,
808
- outputTokens: 0,
809
- cacheCreationTokens: 0,
810
- cacheReadTokens: 0,
811
- cost: 0,
812
- };
813
- existing.inputTokens += breakdown.inputTokens;
814
- existing.outputTokens += breakdown.outputTokens;
815
- existing.cacheCreationTokens += breakdown.cacheCreationTokens;
816
- existing.cacheReadTokens += breakdown.cacheReadTokens;
817
- existing.cost += breakdown.cost;
818
- modelMap.set(breakdown.modelName, existing);
819
- }
820
- }
821
- // Calculate totals for percentage
822
- const models = Array.from(modelMap.values());
823
- const totalTokens = models.reduce((sum, m) => sum + m.inputTokens + m.outputTokens, 0);
824
- // Add percentage, cost breakdown, and I/O ratio
825
- const result = models
826
- .map((m) => {
827
- const pricing = (0, model_pricing_1.getModelPricing)(m.model);
828
- // Calculate cost breakdown
829
- const inputCost = (m.inputTokens / 1000000) * pricing.inputPerMillion;
830
- const outputCost = (m.outputTokens / 1000000) * pricing.outputPerMillion;
831
- const cacheCreationCost = (m.cacheCreationTokens / 1000000) * pricing.cacheCreationPerMillion;
832
- const cacheReadCost = (m.cacheReadTokens / 1000000) * pricing.cacheReadPerMillion;
833
- // Calculate I/O ratio
834
- const ioRatio = m.outputTokens > 0 ? m.inputTokens / m.outputTokens : 0;
835
- return {
836
- model: m.model,
837
- tokens: m.inputTokens + m.outputTokens,
838
- inputTokens: m.inputTokens,
839
- outputTokens: m.outputTokens,
840
- cacheCreationTokens: m.cacheCreationTokens,
841
- cacheReadTokens: m.cacheReadTokens,
842
- cacheTokens: m.cacheCreationTokens + m.cacheReadTokens,
843
- cost: Math.round(m.cost * 100) / 100,
844
- percentage: totalTokens > 0
845
- ? Math.round(((m.inputTokens + m.outputTokens) / totalTokens) * 1000) / 10
846
- : 0,
847
- costBreakdown: {
848
- input: { tokens: m.inputTokens, cost: Math.round(inputCost * 100) / 100 },
849
- output: { tokens: m.outputTokens, cost: Math.round(outputCost * 100) / 100 },
850
- cacheCreation: {
851
- tokens: m.cacheCreationTokens,
852
- cost: Math.round(cacheCreationCost * 100) / 100,
853
- },
854
- cacheRead: { tokens: m.cacheReadTokens, cost: Math.round(cacheReadCost * 100) / 100 },
855
- },
856
- ioRatio: Math.round(ioRatio * 10) / 10,
857
- };
858
- })
859
- .sort((a, b) => b.tokens - a.tokens);
860
- res.json({
861
- success: true,
862
- data: result,
863
- });
864
- }
865
- catch (error) {
866
- errorResponse(res, error, 'Failed to fetch model usage');
867
- }
868
- });
869
- /**
870
- * GET /api/usage/sessions
871
- *
872
- * Returns paginated list of sessions.
873
- * Query: ?since=YYYYMMDD&until=YYYYMMDD&limit=50&offset=0
874
- */
875
- exports.usageRoutes.get('/sessions', async (req, res) => {
876
- try {
877
- const since = validateDate(req.query.since);
878
- const until = validateDate(req.query.until);
879
- const limit = validateLimit(req.query.limit);
880
- const offset = validateOffset(req.query.offset);
881
- const sessionData = await getCachedSessionData();
882
- // Filter by date range using lastActivity
883
- const filtered = filterByDateRange(sessionData, since, until);
884
- // Sort by lastActivity descending
885
- const sorted = [...filtered].sort((a, b) => new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime());
886
- // Paginate
887
- const paginated = sorted.slice(offset, offset + limit);
888
- // Transform for frontend
889
- const sessions = paginated.map((s) => ({
890
- sessionId: s.sessionId,
891
- projectPath: s.projectPath,
892
- tokens: s.inputTokens + s.outputTokens,
893
- inputTokens: s.inputTokens,
894
- outputTokens: s.outputTokens,
895
- cost: Math.round(s.totalCost * 100) / 100,
896
- lastActivity: s.lastActivity,
897
- modelsUsed: s.modelsUsed,
898
- }));
899
- res.json({
900
- success: true,
901
- data: {
902
- sessions,
903
- total: filtered.length,
904
- limit,
905
- offset,
906
- hasMore: offset + limit < filtered.length,
907
- },
908
- });
909
- }
910
- catch (error) {
911
- errorResponse(res, error, 'Failed to fetch sessions');
912
- }
913
- });
914
- /**
915
- * GET /api/usage/monthly
916
- *
917
- * Returns monthly usage summary for charts.
918
- * Query: ?since=YYYYMMDD&until=YYYYMMDD
919
- */
920
- exports.usageRoutes.get('/monthly', async (req, res) => {
921
- try {
922
- const since = validateDate(req.query.since);
923
- const until = validateDate(req.query.until);
924
- const monthlyData = await getCachedMonthlyData();
925
- // Filter by date range (convert month YYYY-MM to YYYYMM01 for comparison)
926
- const filtered = since || until
927
- ? monthlyData.filter((m) => {
928
- const monthDate = m.month.replace('-', '') + '01';
929
- if (since && monthDate < since)
930
- return false;
931
- if (until && monthDate > until)
932
- return false;
933
- return true;
934
- })
935
- : monthlyData;
936
- // Transform for charts
937
- const result = filtered.map((m) => ({
938
- month: m.month,
939
- tokens: m.inputTokens + m.outputTokens,
940
- inputTokens: m.inputTokens,
941
- outputTokens: m.outputTokens,
942
- cacheTokens: m.cacheCreationTokens + m.cacheReadTokens,
943
- cost: Math.round(m.totalCost * 100) / 100,
944
- modelsUsed: m.modelsUsed.length,
945
- }));
946
- res.json({
947
- success: true,
948
- data: result.sort((a, b) => a.month.localeCompare(b.month)),
949
- });
950
- }
951
- catch (error) {
952
- errorResponse(res, error, 'Failed to fetch monthly usage');
953
- }
954
- });
955
- /**
956
- * POST /api/usage/refresh
957
- *
958
- * Clears the usage cache to force fresh data fetch.
959
- * Useful when user wants to see latest data immediately.
960
- */
961
- exports.usageRoutes.post('/refresh', (_req, res) => {
962
- clearUsageCache();
963
- res.json({
964
- success: true,
965
- message: 'Usage cache cleared',
966
- });
967
- });
968
- /**
969
- * GET /api/usage/status
970
- *
971
- * Returns cache status including last fetch timestamp.
972
- * Used by UI to show "Last updated: X ago" indicator.
973
- */
974
- exports.usageRoutes.get('/status', (_req, res) => {
975
- res.json({
976
- success: true,
977
- data: {
978
- lastFetch: lastFetchTimestamp,
979
- cacheSize: cache.size,
980
- },
981
- });
982
- });
983
- // ============================================================================
984
- // ANOMALY DETECTION
985
- // ============================================================================
986
- /** Anomaly detection thresholds */
987
- const ANOMALY_THRESHOLDS = {
988
- HIGH_INPUT_TOKENS: 10000000, // 10M tokens/day/model
989
- HIGH_IO_RATIO: 100, // 100x input/output ratio
990
- COST_SPIKE_MULTIPLIER: 2, // 2x average daily cost
991
- HIGH_CACHE_READ_TOKENS: 1000000000, // 1B cache read tokens
992
- };
993
- /**
994
- * Detect anomalies in usage data
995
- */
996
- function detectAnomalies(dailyData) {
997
- const anomalies = [];
998
- // Calculate average daily cost for spike detection
999
- const totalCost = dailyData.reduce((sum, day) => sum + day.totalCost, 0);
1000
- const avgDailyCost = dailyData.length > 0 ? totalCost / dailyData.length : 0;
1001
- const costSpikeThreshold = avgDailyCost * ANOMALY_THRESHOLDS.COST_SPIKE_MULTIPLIER;
1002
- for (const day of dailyData) {
1003
- // Check for cost spikes
1004
- if (avgDailyCost > 0 && day.totalCost > costSpikeThreshold) {
1005
- const multiplier = Math.round((day.totalCost / avgDailyCost) * 10) / 10;
1006
- anomalies.push({
1007
- date: day.date,
1008
- type: 'cost_spike',
1009
- value: day.totalCost,
1010
- threshold: avgDailyCost,
1011
- message: `Cost ${multiplier}x above daily average ($${Math.round(day.totalCost)} vs $${Math.round(avgDailyCost)})`,
1012
- });
1013
- }
1014
- // Check per-model anomalies
1015
- for (const breakdown of day.modelBreakdowns) {
1016
- // High input tokens per model
1017
- if (breakdown.inputTokens > ANOMALY_THRESHOLDS.HIGH_INPUT_TOKENS) {
1018
- const multiplier = Math.round((breakdown.inputTokens / ANOMALY_THRESHOLDS.HIGH_INPUT_TOKENS) * 10) / 10;
1019
- anomalies.push({
1020
- date: day.date,
1021
- type: 'high_input',
1022
- model: breakdown.modelName,
1023
- value: breakdown.inputTokens,
1024
- threshold: ANOMALY_THRESHOLDS.HIGH_INPUT_TOKENS,
1025
- message: `Input tokens ${multiplier}x above threshold (${formatTokenCount(breakdown.inputTokens)})`,
1026
- });
1027
- }
1028
- // High I/O ratio
1029
- if (breakdown.outputTokens > 0) {
1030
- const ioRatio = breakdown.inputTokens / breakdown.outputTokens;
1031
- if (ioRatio > ANOMALY_THRESHOLDS.HIGH_IO_RATIO) {
1032
- const multiplier = Math.round((ioRatio / ANOMALY_THRESHOLDS.HIGH_IO_RATIO) * 10) / 10;
1033
- anomalies.push({
1034
- date: day.date,
1035
- type: 'high_io_ratio',
1036
- model: breakdown.modelName,
1037
- value: ioRatio,
1038
- threshold: ANOMALY_THRESHOLDS.HIGH_IO_RATIO,
1039
- message: `I/O ratio ${multiplier}x above threshold (${Math.round(ioRatio)}:1)`,
1040
- });
1041
- }
1042
- }
1043
- // High cache read tokens
1044
- if (breakdown.cacheReadTokens > ANOMALY_THRESHOLDS.HIGH_CACHE_READ_TOKENS) {
1045
- const multiplier = Math.round((breakdown.cacheReadTokens / ANOMALY_THRESHOLDS.HIGH_CACHE_READ_TOKENS) * 10) /
1046
- 10;
1047
- anomalies.push({
1048
- date: day.date,
1049
- type: 'high_cache_read',
1050
- model: breakdown.modelName,
1051
- value: breakdown.cacheReadTokens,
1052
- threshold: ANOMALY_THRESHOLDS.HIGH_CACHE_READ_TOKENS,
1053
- message: `Cache reads ${multiplier}x above threshold (${formatTokenCount(breakdown.cacheReadTokens)})`,
1054
- });
1055
- }
1056
- }
1057
- }
1058
- // Sort by date descending
1059
- return anomalies.sort((a, b) => b.date.localeCompare(a.date));
1060
- }
1061
- /**
1062
- * Format token count for human readability
1063
- */
1064
- function formatTokenCount(tokens) {
1065
- if (tokens >= 1000000000) {
1066
- return `${(tokens / 1000000000).toFixed(1)}B`;
1067
- }
1068
- else if (tokens >= 1000000) {
1069
- return `${(tokens / 1000000).toFixed(1)}M`;
1070
- }
1071
- else if (tokens >= 1000) {
1072
- return `${(tokens / 1000).toFixed(1)}K`;
1073
- }
1074
- return tokens.toString();
1075
- }
1076
- /**
1077
- * Summarize anomalies by type
1078
- */
1079
- function summarizeAnomalies(anomalies) {
1080
- const uniqueDates = new Set();
1081
- let highInputDays = 0;
1082
- let highIoRatioDays = 0;
1083
- let costSpikeDays = 0;
1084
- let highCacheReadDays = 0;
1085
- // Track unique dates per anomaly type
1086
- const highInputDates = new Set();
1087
- const highIoRatioDates = new Set();
1088
- const costSpikeDates = new Set();
1089
- const highCacheReadDates = new Set();
1090
- for (const anomaly of anomalies) {
1091
- uniqueDates.add(anomaly.date);
1092
- switch (anomaly.type) {
1093
- case 'high_input':
1094
- highInputDates.add(anomaly.date);
1095
- break;
1096
- case 'high_io_ratio':
1097
- highIoRatioDates.add(anomaly.date);
1098
- break;
1099
- case 'cost_spike':
1100
- costSpikeDates.add(anomaly.date);
1101
- break;
1102
- case 'high_cache_read':
1103
- highCacheReadDates.add(anomaly.date);
1104
- break;
1105
- }
1106
- }
1107
- highInputDays = highInputDates.size;
1108
- highIoRatioDays = highIoRatioDates.size;
1109
- costSpikeDays = costSpikeDates.size;
1110
- highCacheReadDays = highCacheReadDates.size;
1111
- return {
1112
- totalAnomalies: anomalies.length,
1113
- highInputDays,
1114
- highIoRatioDays,
1115
- costSpikeDays,
1116
- highCacheReadDays,
1117
- };
1118
- }
1119
- /**
1120
- * GET /api/usage/insights
1121
- *
1122
- * Returns anomaly detection results for usage patterns.
1123
- * Query: ?since=YYYYMMDD&until=YYYYMMDD
1124
- */
1125
- exports.usageRoutes.get('/insights', async (req, res) => {
1126
- try {
1127
- const since = validateDate(req.query.since);
1128
- const until = validateDate(req.query.until);
1129
- const dailyData = await getCachedDailyData();
1130
- const filtered = filterByDateRange(dailyData, since, until);
1131
- const anomalies = detectAnomalies(filtered);
1132
- const summary = summarizeAnomalies(anomalies);
1133
- res.json({
1134
- success: true,
1135
- data: {
1136
- anomalies,
1137
- summary,
1138
- },
1139
- });
1140
- }
1141
- catch (error) {
1142
- errorResponse(res, error, 'Failed to fetch usage insights');
1143
- }
1144
- });
8
+ exports.getLastFetchTimestamp = exports.clearUsageCache = exports.prewarmUsageCache = exports.usageRoutes = void 0;
9
+ var routes_1 = require("./usage/routes");
10
+ Object.defineProperty(exports, "usageRoutes", { enumerable: true, get: function () { return routes_1.usageRoutes; } });
11
+ Object.defineProperty(exports, "prewarmUsageCache", { enumerable: true, get: function () { return routes_1.prewarmUsageCache; } });
12
+ Object.defineProperty(exports, "clearUsageCache", { enumerable: true, get: function () { return routes_1.clearUsageCache; } });
13
+ Object.defineProperty(exports, "getLastFetchTimestamp", { enumerable: true, get: function () { return routes_1.getLastFetchTimestamp; } });
1145
14
  //# sourceMappingURL=usage-routes.js.map