cli-claw-kit 0.0.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 (295) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +245 -0
  3. package/config/default-groups.json +1 -0
  4. package/config/global-agents-md.template.md +37 -0
  5. package/config/mount-allowlist.json +11 -0
  6. package/container/Dockerfile +160 -0
  7. package/container/agent-runner/dist/.tsbuildinfo +1 -0
  8. package/container/agent-runner/dist/agent-definitions.js +22 -0
  9. package/container/agent-runner/dist/channel-prefixes.js +16 -0
  10. package/container/agent-runner/dist/codex-config.js +29 -0
  11. package/container/agent-runner/dist/image-detector.js +96 -0
  12. package/container/agent-runner/dist/index.js +2587 -0
  13. package/container/agent-runner/dist/mcp-tools.js +1076 -0
  14. package/container/agent-runner/dist/stream-event.types.js +5 -0
  15. package/container/agent-runner/dist/stream-processor.js +867 -0
  16. package/container/agent-runner/dist/types.js +6 -0
  17. package/container/agent-runner/dist/utils.js +115 -0
  18. package/container/agent-runner/package.json +36 -0
  19. package/container/agent-runner/prompts/security-rules.md +31 -0
  20. package/container/agent-runner/src/agent-definitions.ts +27 -0
  21. package/container/agent-runner/src/channel-prefixes.ts +16 -0
  22. package/container/agent-runner/src/codex-config.ts +40 -0
  23. package/container/agent-runner/src/image-detector.ts +116 -0
  24. package/container/agent-runner/src/index.ts +3107 -0
  25. package/container/agent-runner/src/mcp-tools.ts +1295 -0
  26. package/container/agent-runner/src/stream-event.types.ts +10 -0
  27. package/container/agent-runner/src/stream-processor.ts +932 -0
  28. package/container/agent-runner/src/types.ts +75 -0
  29. package/container/agent-runner/src/utils.ts +114 -0
  30. package/container/agent-runner/tsconfig.json +17 -0
  31. package/container/build.sh +28 -0
  32. package/container/entrypoint.sh +64 -0
  33. package/container/skills/agent-browser/SKILL.md +159 -0
  34. package/container/skills/install-skill/SKILL.md +64 -0
  35. package/container/skills/post-test-cleanup/SKILL.md +121 -0
  36. package/dist/.tsbuildinfo +1 -0
  37. package/dist/agent-output-parser.js +459 -0
  38. package/dist/app-root.js +52 -0
  39. package/dist/assistant-meta-footer.js +1 -0
  40. package/dist/auth.js +91 -0
  41. package/dist/billing.js +694 -0
  42. package/dist/channel-prefixes.js +16 -0
  43. package/dist/cli.js +86 -0
  44. package/dist/commands.js +79 -0
  45. package/dist/config.js +120 -0
  46. package/dist/container-runner.js +981 -0
  47. package/dist/daily-summary.js +210 -0
  48. package/dist/db.js +3683 -0
  49. package/dist/dingtalk.js +1347 -0
  50. package/dist/feishu-markdown-style.js +97 -0
  51. package/dist/feishu-streaming-card.js +1875 -0
  52. package/dist/feishu.js +1628 -0
  53. package/dist/file-manager.js +270 -0
  54. package/dist/group-queue.js +1070 -0
  55. package/dist/group-runtime.js +35 -0
  56. package/dist/host-workspace-cwd.js +85 -0
  57. package/dist/im-channel.js +384 -0
  58. package/dist/im-command-utils.js +142 -0
  59. package/dist/im-downloader.js +45 -0
  60. package/dist/im-manager.js +527 -0
  61. package/dist/im-utils.js +53 -0
  62. package/dist/image-detector.js +96 -0
  63. package/dist/index.js +5828 -0
  64. package/dist/logger.js +22 -0
  65. package/dist/mcp-utils.js +66 -0
  66. package/dist/message-attachments.js +69 -0
  67. package/dist/message-notifier.js +36 -0
  68. package/dist/middleware/auth.js +85 -0
  69. package/dist/mount-security.js +315 -0
  70. package/dist/permissions.js +67 -0
  71. package/dist/project-memory.js +6 -0
  72. package/dist/provider-pool.js +189 -0
  73. package/dist/qq.js +826 -0
  74. package/dist/reset-admin.js +42 -0
  75. package/dist/routes/admin.js +543 -0
  76. package/dist/routes/agent-definitions.js +241 -0
  77. package/dist/routes/agents.js +533 -0
  78. package/dist/routes/auth.js +675 -0
  79. package/dist/routes/billing.js +490 -0
  80. package/dist/routes/browse.js +210 -0
  81. package/dist/routes/bug-report.js +387 -0
  82. package/dist/routes/config.js +1868 -0
  83. package/dist/routes/files.js +671 -0
  84. package/dist/routes/groups.js +1367 -0
  85. package/dist/routes/mcp-servers.js +320 -0
  86. package/dist/routes/memory.js +523 -0
  87. package/dist/routes/monitor.js +307 -0
  88. package/dist/routes/skills.js +777 -0
  89. package/dist/routes/tasks.js +509 -0
  90. package/dist/routes/usage.js +64 -0
  91. package/dist/routes/workspace-config.js +458 -0
  92. package/dist/runtime-build.js +112 -0
  93. package/dist/runtime-command-handler.js +189 -0
  94. package/dist/runtime-command-registry.js +1 -0
  95. package/dist/runtime-config.js +1777 -0
  96. package/dist/runtime-identity.js +52 -0
  97. package/dist/schemas.js +590 -0
  98. package/dist/script-runner.js +64 -0
  99. package/dist/sdk-query.js +82 -0
  100. package/dist/skill-utils.js +145 -0
  101. package/dist/sqlite-compat.js +19 -0
  102. package/dist/stream-event.types.js +5 -0
  103. package/dist/streaming-runtime-meta.js +29 -0
  104. package/dist/task-scheduler.js +695 -0
  105. package/dist/task-utils.js +13 -0
  106. package/dist/telegram-pairing.js +59 -0
  107. package/dist/telegram.js +897 -0
  108. package/dist/terminal-manager.js +307 -0
  109. package/dist/tool-step-display.js +1 -0
  110. package/dist/types.js +1 -0
  111. package/dist/utils.js +85 -0
  112. package/dist/web-context.js +161 -0
  113. package/dist/web.js +1377 -0
  114. package/dist/wechat-crypto.js +182 -0
  115. package/dist/wechat.js +589 -0
  116. package/dist/workspace-runtime-reset.js +35 -0
  117. package/package.json +107 -0
  118. package/shared/assistant-meta-footer.ts +127 -0
  119. package/shared/channel-prefixes.ts +16 -0
  120. package/shared/dist/assistant-meta-footer.d.ts +29 -0
  121. package/shared/dist/assistant-meta-footer.js +85 -0
  122. package/shared/dist/channel-prefixes.d.ts +4 -0
  123. package/shared/dist/channel-prefixes.js +16 -0
  124. package/shared/dist/image-detector.d.ts +20 -0
  125. package/shared/dist/image-detector.js +96 -0
  126. package/shared/dist/runtime-command-registry.d.ts +38 -0
  127. package/shared/dist/runtime-command-registry.js +185 -0
  128. package/shared/dist/stream-event.d.ts +65 -0
  129. package/shared/dist/stream-event.js +8 -0
  130. package/shared/dist/tool-step-display.d.ts +4 -0
  131. package/shared/dist/tool-step-display.js +11 -0
  132. package/shared/image-detector.ts +116 -0
  133. package/shared/runtime-command-registry.ts +252 -0
  134. package/shared/stream-event.ts +67 -0
  135. package/shared/tool-step-display.ts +21 -0
  136. package/shared/tsconfig.json +24 -0
  137. package/web/dist/assets/BillingPage-B1wBR_o-.js +52 -0
  138. package/web/dist/assets/ChatPage-6GBZ9nXN.css +32 -0
  139. package/web/dist/assets/ChatPage-BOJcXtaj.js +161 -0
  140. package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  141. package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  142. package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  143. package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  144. package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  145. package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  146. package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  147. package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  148. package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  149. package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  150. package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  151. package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  152. package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  153. package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  154. package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  155. package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  156. package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  157. package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  158. package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  159. package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  160. package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  161. package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  162. package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  163. package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  164. package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  165. package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  166. package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  167. package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  168. package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  169. package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  170. package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  171. package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  172. package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  173. package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  174. package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  175. package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  176. package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  177. package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  178. package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  179. package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  180. package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  181. package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  182. package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  183. package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  184. package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  185. package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  186. package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  187. package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  188. package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  189. package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  190. package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  191. package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  192. package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  193. package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  194. package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  195. package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  196. package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  197. package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  198. package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  199. package/web/dist/assets/SettingsPage-DoY7FoZ_.js +153 -0
  200. package/web/dist/assets/ShareImageDialog-C1ga8b7l.js +22 -0
  201. package/web/dist/assets/TasksPage-CRivnNsx.js +14 -0
  202. package/web/dist/assets/_basePickBy-Bf-bSoS9.js +1 -0
  203. package/web/dist/assets/_baseUniq-zAOaCuKw.js +1 -0
  204. package/web/dist/assets/arc-Dm9mVQ9U.js +1 -0
  205. package/web/dist/assets/architectureDiagram-2XIMDMQ5-BLmzX1wr.js +36 -0
  206. package/web/dist/assets/band-CquvqAHh.js +1 -0
  207. package/web/dist/assets/blockDiagram-WCTKOSBZ-B9pcqm3j.js +132 -0
  208. package/web/dist/assets/c4Diagram-IC4MRINW-Cytx1q3b.js +10 -0
  209. package/web/dist/assets/channel-BOVj73LR.js +1 -0
  210. package/web/dist/assets/channel-meta-CQD0Pei-.js +41 -0
  211. package/web/dist/assets/chunk-4BX2VUAB-0ToDr6RE.js +1 -0
  212. package/web/dist/assets/chunk-55IACEB6-DQDjnXfS.js +1 -0
  213. package/web/dist/assets/chunk-FMBD7UC4-Di8ABm6c.js +15 -0
  214. package/web/dist/assets/chunk-JSJVCQXG-BZQN6rnX.js +1 -0
  215. package/web/dist/assets/chunk-KX2RTZJC-zBbcpaN_.js +1 -0
  216. package/web/dist/assets/chunk-NQ4KR5QH-BCrLoU88.js +220 -0
  217. package/web/dist/assets/chunk-QZHKN3VN-Bqk8juan.js +1 -0
  218. package/web/dist/assets/chunk-WL4C6EOR-D2YX-MHY.js +189 -0
  219. package/web/dist/assets/classDiagram-VBA2DB6C-DUUoMyaK.js +1 -0
  220. package/web/dist/assets/classDiagram-v2-RAHNMMFH-DUUoMyaK.js +1 -0
  221. package/web/dist/assets/clone-BmaCesfa.js +1 -0
  222. package/web/dist/assets/cose-bilkent-S5V4N54A-CTsv6qQA.js +1 -0
  223. package/web/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
  224. package/web/dist/assets/dagre-KLK3FWXG-Ci4Jh9nu.js +4 -0
  225. package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  226. package/web/dist/assets/diagram-E7M64L7V-BFRnfTI2.js +24 -0
  227. package/web/dist/assets/diagram-IFDJBPK2-B7Zhnp0b.js +43 -0
  228. package/web/dist/assets/diagram-P4PSJMXO-BVyP7nwq.js +24 -0
  229. package/web/dist/assets/erDiagram-INFDFZHY-NorKdTOF.js +70 -0
  230. package/web/dist/assets/error-CGD5mp5f.js +1 -0
  231. package/web/dist/assets/flowDiagram-PKNHOUZH-Ch97nABF.js +162 -0
  232. package/web/dist/assets/ganttDiagram-A5KZAMGK-BQ2pLWsy.js +292 -0
  233. package/web/dist/assets/gitGraphDiagram-K3NZZRJ6-bcvnBsD2.js +65 -0
  234. package/web/dist/assets/graph-CeAEckur.js +1 -0
  235. package/web/dist/assets/index-CPnL1_qC.js +768 -0
  236. package/web/dist/assets/index-DVevCbcO.css +10 -0
  237. package/web/dist/assets/infoDiagram-LFFYTUFH-CcsrFdj-.js +2 -0
  238. package/web/dist/assets/init-Dmth1JHB.js +1 -0
  239. package/web/dist/assets/ishikawaDiagram-PHBUUO56-1upyMfHN.js +70 -0
  240. package/web/dist/assets/journeyDiagram-4ABVD52K-CKUi-V0c.js +139 -0
  241. package/web/dist/assets/kanban-definition-K7BYSVSG-DOnQwXfL.js +89 -0
  242. package/web/dist/assets/layout-BmMMqTnJ.js +1 -0
  243. package/web/dist/assets/linear-DiaJloY5.js +1 -0
  244. package/web/dist/assets/mermaid.core-BWLV1B2v.js +254 -0
  245. package/web/dist/assets/mindmap-definition-YRQLILUH-BeAKHVWP.js +68 -0
  246. package/web/dist/assets/ordinal-DILIJJjt.js +1 -0
  247. package/web/dist/assets/pieDiagram-SKSYHLDU-DfiMSfWo.js +30 -0
  248. package/web/dist/assets/quadrantDiagram-337W2JSQ-wZxZOJxd.js +7 -0
  249. package/web/dist/assets/requirementDiagram-Z7DCOOCP-BK4HHm17.js +73 -0
  250. package/web/dist/assets/sankeyDiagram-WA2Y5GQK-BX6t2avX.js +10 -0
  251. package/web/dist/assets/sequenceDiagram-2WXFIKYE-BPQlkbAa.js +145 -0
  252. package/web/dist/assets/sheet-rI0FfB1g.js +6 -0
  253. package/web/dist/assets/sliders-horizontal-CuijWFNK.js +6 -0
  254. package/web/dist/assets/sparkles-BsMYXJoT.js +11 -0
  255. package/web/dist/assets/square-0CqMX1Q3.js +11 -0
  256. package/web/dist/assets/stateDiagram-RAJIS63D-DxkV0Vwd.js +1 -0
  257. package/web/dist/assets/stateDiagram-v2-FVOUBMTO-qLYoiOPe.js +1 -0
  258. package/web/dist/assets/step-D51IIHGA.js +1 -0
  259. package/web/dist/assets/tasks-D8JjBTwx.js +1 -0
  260. package/web/dist/assets/time-O8zIGux3.js +1 -0
  261. package/web/dist/assets/timeline-definition-YZTLITO2-kNp1DyFc.js +61 -0
  262. package/web/dist/assets/treemap-KZPCXAKY-CkrClVhk.js +162 -0
  263. package/web/dist/assets/utils-KGAn0XTg.js +11 -0
  264. package/web/dist/assets/vennDiagram-LZ73GAT5-CgdzEZz4.js +34 -0
  265. package/web/dist/assets/xychartDiagram-JWTSCODW-DfYGPfNB.js +7 -0
  266. package/web/dist/assets/zap-_hKJYy7J.js +6 -0
  267. package/web/dist/favicon.svg +332 -0
  268. package/web/dist/fonts/AlibabaPuHuiTi-3-55-Regular.woff2 +0 -0
  269. package/web/dist/fonts/AlibabaPuHuiTi-3-65-Medium.woff2 +0 -0
  270. package/web/dist/fonts/AlibabaPuHuiTi-3-75-SemiBold.woff2 +0 -0
  271. package/web/dist/fonts/DMSans-latin-ext.woff2 +0 -0
  272. package/web/dist/fonts/DMSans-latin.woff2 +0 -0
  273. package/web/dist/icons/README.md +20 -0
  274. package/web/dist/icons/apple-touch-icon-180.png +0 -0
  275. package/web/dist/icons/icon-128.png +0 -0
  276. package/web/dist/icons/icon-144.png +0 -0
  277. package/web/dist/icons/icon-152.png +0 -0
  278. package/web/dist/icons/icon-192.png +0 -0
  279. package/web/dist/icons/icon-192.svg +332 -0
  280. package/web/dist/icons/icon-384.png +0 -0
  281. package/web/dist/icons/icon-48.png +0 -0
  282. package/web/dist/icons/icon-512-maskable.png +0 -0
  283. package/web/dist/icons/icon-512.png +0 -0
  284. package/web/dist/icons/icon-512.svg +332 -0
  285. package/web/dist/icons/icon-72.png +0 -0
  286. package/web/dist/icons/icon-96.png +0 -0
  287. package/web/dist/icons/loading-logo.svg +332 -0
  288. package/web/dist/icons/logo-1024.png +0 -0
  289. package/web/dist/icons/logo-icon.svg +332 -0
  290. package/web/dist/icons/logo-text.svg +332 -0
  291. package/web/dist/index.html +30 -0
  292. package/web/dist/manifest.webmanifest +1 -0
  293. package/web/dist/registerSW.js +1 -0
  294. package/web/dist/sw.js +1 -0
  295. package/web/dist/workbox-08d6266a.js +1 -0
@@ -0,0 +1,490 @@
1
+ /**
2
+ * Billing API routes: user-facing + admin management.
3
+ */
4
+ import { Hono } from 'hono';
5
+ import { authMiddleware, requirePermission } from '../middleware/auth.js';
6
+ import { getActiveBillingPlans, getAllBillingPlans, getBillingPlan, createBillingPlan, updateBillingPlan, deleteBillingPlan, getUserActiveSubscription, getUserBalance, getBalanceTransactions, getMonthlyUsage, getUserMonthlyUsageHistory, getUserDailyUsageHistory, getSubscriptionHistory, getRedeemCodeUsageDetails, getDashboardStats, getRevenueTrend, getAllRedeemCodes, getAllPlanSubscriberCounts, createRedeemCode as dbCreateRedeemCode, deleteRedeemCode as dbDeleteRedeemCode, logBillingAudit, getBillingAuditLog, getAllUserBillingOverview, getRevenueStats, getUserById, getDailyUsage, getWeeklyUsageSummary, } from '../db.js';
7
+ import { isBillingEnabled, checkBillingAccess, getUserEffectivePlan, checkQuota, assignPlan, applyAdminBalanceAdjustment, batchAssignPlans, cancelSubscription, redeemCode, generateRedeemCode, invalidateAllBillingCaches, } from '../billing.js';
8
+ import { BillingPlanCreateSchema, BillingPlanPatchSchema, AssignPlanSchema, AdjustBalanceSchema, BatchAssignPlanSchema, RedeemCodeCreateSchema, RedeemCodeSchema, } from '../schemas.js';
9
+ import { getSystemSettings } from '../runtime-config.js';
10
+ const billingRoutes = new Hono();
11
+ const billingManageMiddleware = requirePermission('manage_billing');
12
+ // ==================== User-facing endpoints ====================
13
+ // GET /plans — list available plans
14
+ billingRoutes.get('/plans', authMiddleware, async (c) => {
15
+ const plans = getActiveBillingPlans();
16
+ return c.json({ plans });
17
+ });
18
+ // GET /my/subscription — current subscription + plan details
19
+ billingRoutes.get('/my/subscription', authMiddleware, async (c) => {
20
+ const user = c.get('user');
21
+ const effective = getUserEffectivePlan(user.id);
22
+ if (!effective) {
23
+ return c.json({ subscription: null, plan: null });
24
+ }
25
+ return c.json({
26
+ subscription: effective.subscription,
27
+ plan: effective.plan,
28
+ });
29
+ });
30
+ // GET /my/balance — current balance
31
+ billingRoutes.get('/my/balance', authMiddleware, async (c) => {
32
+ const user = c.get('user');
33
+ const balance = getUserBalance(user.id);
34
+ return c.json(balance);
35
+ });
36
+ // GET /my/usage — current month usage + quota progress
37
+ billingRoutes.get('/my/usage', authMiddleware, async (c) => {
38
+ const user = c.get('user');
39
+ const month = new Date().toISOString().slice(0, 7);
40
+ const usage = getMonthlyUsage(user.id, month);
41
+ const effective = getUserEffectivePlan(user.id);
42
+ const history = getUserMonthlyUsageHistory(user.id, 6);
43
+ return c.json({
44
+ currentMonth: month,
45
+ usage: usage ?? {
46
+ user_id: user.id,
47
+ month,
48
+ total_input_tokens: 0,
49
+ total_output_tokens: 0,
50
+ total_cost_usd: 0,
51
+ message_count: 0,
52
+ updated_at: new Date().toISOString(),
53
+ },
54
+ plan: effective?.plan ?? null,
55
+ history,
56
+ });
57
+ });
58
+ // GET /my/transactions — balance change history (paginated)
59
+ billingRoutes.get('/my/transactions', authMiddleware, async (c) => {
60
+ const user = c.get('user');
61
+ const limit = Math.min(parseInt(c.req.query('limit') || '50'), 100);
62
+ const offset = parseInt(c.req.query('offset') || '0');
63
+ const result = getBalanceTransactions(user.id, limit, offset);
64
+ return c.json(result);
65
+ });
66
+ // GET /my/quota — enhanced current quota status (daily/weekly/monthly)
67
+ billingRoutes.get('/my/quota', authMiddleware, async (c) => {
68
+ const user = c.get('user');
69
+ const result = checkQuota(user.id, user.role);
70
+ return c.json(result);
71
+ });
72
+ // GET /my/access — current billing access status (wallet-first)
73
+ billingRoutes.get('/my/access', authMiddleware, async (c) => {
74
+ const user = c.get('user');
75
+ const result = checkBillingAccess(user.id, user.role);
76
+ return c.json(result);
77
+ });
78
+ // POST /my/redeem — use a redeem code
79
+ billingRoutes.post('/my/redeem', authMiddleware, async (c) => {
80
+ const user = c.get('user');
81
+ const body = await c.req.json().catch(() => ({}));
82
+ const validation = RedeemCodeSchema.safeParse(body);
83
+ if (!validation.success) {
84
+ return c.json({ error: 'Invalid request body' }, 400);
85
+ }
86
+ const result = redeemCode(user.id, validation.data.code);
87
+ if (!result.success) {
88
+ return c.json({ error: result.message }, 400);
89
+ }
90
+ return c.json({ message: result.message });
91
+ });
92
+ // PATCH /my/auto-renew — toggle auto-renew
93
+ billingRoutes.patch('/my/auto-renew', authMiddleware, async (c) => {
94
+ return c.json({ error: '余额优先模式下不支持用户自助自动续费,请联系管理员处理套餐' }, 409);
95
+ });
96
+ // POST /my/cancel-subscription — user self-cancel
97
+ billingRoutes.post('/my/cancel-subscription', authMiddleware, async (c) => {
98
+ return c.json({ error: '余额优先模式下不支持用户自助取消套餐,请联系管理员处理' }, 409);
99
+ });
100
+ // ==================== Admin endpoints ====================
101
+ // POST /admin/plans — create plan
102
+ billingRoutes.post('/admin/plans', authMiddleware, billingManageMiddleware, async (c) => {
103
+ const body = await c.req.json().catch(() => ({}));
104
+ const validation = BillingPlanCreateSchema.safeParse(body);
105
+ if (!validation.success) {
106
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
107
+ }
108
+ const existing = getBillingPlan(validation.data.id);
109
+ if (existing) {
110
+ return c.json({ error: 'Plan ID already exists' }, 409);
111
+ }
112
+ const now = new Date().toISOString();
113
+ const plan = {
114
+ id: validation.data.id,
115
+ name: validation.data.name,
116
+ description: validation.data.description ?? null,
117
+ tier: validation.data.tier ?? 0,
118
+ monthly_cost_usd: validation.data.monthly_cost_usd ?? 0,
119
+ monthly_token_quota: validation.data.monthly_token_quota ?? null,
120
+ monthly_cost_quota: validation.data.monthly_cost_quota ?? null,
121
+ daily_cost_quota: validation.data.daily_cost_quota ?? null,
122
+ weekly_cost_quota: validation.data.weekly_cost_quota ?? null,
123
+ daily_token_quota: validation.data.daily_token_quota ?? null,
124
+ weekly_token_quota: validation.data.weekly_token_quota ?? null,
125
+ rate_multiplier: validation.data.rate_multiplier ?? 1.0,
126
+ trial_days: validation.data.trial_days ?? null,
127
+ sort_order: validation.data.sort_order ?? 0,
128
+ display_price: validation.data.display_price ?? null,
129
+ highlight: validation.data.highlight ?? false,
130
+ max_groups: validation.data.max_groups ?? null,
131
+ max_concurrent_containers: validation.data.max_concurrent_containers ?? null,
132
+ max_im_channels: validation.data.max_im_channels ?? null,
133
+ max_mcp_servers: validation.data.max_mcp_servers ?? null,
134
+ max_storage_mb: validation.data.max_storage_mb ?? null,
135
+ allow_overage: validation.data.allow_overage ?? false,
136
+ features: validation.data.features ?? [],
137
+ is_default: validation.data.is_default ?? false,
138
+ is_active: validation.data.is_active ?? true,
139
+ created_at: now,
140
+ updated_at: now,
141
+ };
142
+ createBillingPlan(plan);
143
+ const actor = c.get('user');
144
+ logBillingAudit('plan_created', actor.id, actor.id, {
145
+ planId: plan.id,
146
+ planName: plan.name,
147
+ });
148
+ return c.json(plan, 201);
149
+ });
150
+ // PATCH /admin/plans/:id — update plan
151
+ billingRoutes.patch('/admin/plans/:id', authMiddleware, billingManageMiddleware, async (c) => {
152
+ const id = c.req.param('id');
153
+ const existing = getBillingPlan(id);
154
+ if (!existing)
155
+ return c.json({ error: 'Plan not found' }, 404);
156
+ const body = await c.req.json().catch(() => ({}));
157
+ const validation = BillingPlanPatchSchema.safeParse(body);
158
+ if (!validation.success) {
159
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
160
+ }
161
+ updateBillingPlan(id, validation.data);
162
+ invalidateAllBillingCaches();
163
+ const actor = c.get('user');
164
+ logBillingAudit('plan_updated', actor.id, actor.id, {
165
+ planId: id,
166
+ updates: validation.data,
167
+ });
168
+ const updated = getBillingPlan(id);
169
+ return c.json(updated);
170
+ });
171
+ // DELETE /admin/plans/:id — delete plan
172
+ billingRoutes.delete('/admin/plans/:id', authMiddleware, billingManageMiddleware, async (c) => {
173
+ const id = c.req.param('id');
174
+ const plan = getBillingPlan(id);
175
+ if (!plan)
176
+ return c.json({ error: 'Plan not found' }, 404);
177
+ if (plan.is_default) {
178
+ return c.json({ error: 'Cannot delete the default plan' }, 400);
179
+ }
180
+ const deleted = deleteBillingPlan(id);
181
+ if (!deleted) {
182
+ return c.json({ error: 'Cannot delete plan with active subscribers' }, 409);
183
+ }
184
+ const actor = c.get('user');
185
+ logBillingAudit('plan_deleted', actor.id, actor.id, {
186
+ planId: id,
187
+ planName: plan.name,
188
+ });
189
+ return c.json({ success: true });
190
+ });
191
+ // GET /admin/users — all users billing overview
192
+ billingRoutes.get('/admin/users', authMiddleware, billingManageMiddleware, async (c) => {
193
+ const users = getAllUserBillingOverview().map((u) => {
194
+ // Fill in fallback plan info for users without a real subscription
195
+ const access = checkBillingAccess(u.user_id, u.role);
196
+ if (!u.plan_id) {
197
+ const effective = getUserEffectivePlan(u.user_id);
198
+ if (effective) {
199
+ return {
200
+ ...u,
201
+ plan_id: effective.plan.id,
202
+ plan_name: effective.plan.name,
203
+ is_fallback: true,
204
+ access_allowed: access.allowed,
205
+ access_block_type: access.blockType ?? null,
206
+ access_reason: access.reason ?? null,
207
+ min_balance_usd: access.minBalanceUsd,
208
+ };
209
+ }
210
+ }
211
+ return {
212
+ ...u,
213
+ is_fallback: false,
214
+ access_allowed: access.allowed,
215
+ access_block_type: access.blockType ?? null,
216
+ access_reason: access.reason ?? null,
217
+ min_balance_usd: access.minBalanceUsd,
218
+ };
219
+ });
220
+ return c.json({ users });
221
+ });
222
+ // GET /admin/users/:id/detail — user billing detail (flat structure for drawer)
223
+ billingRoutes.get('/admin/users/:id/detail', authMiddleware, billingManageMiddleware, async (c) => {
224
+ const userId = c.req.param('id');
225
+ const user = getUserById(userId);
226
+ if (!user) {
227
+ return c.json({ error: '用户不存在' }, 404);
228
+ }
229
+ const effective = getUserEffectivePlan(userId);
230
+ const realSubscription = getUserActiveSubscription(userId);
231
+ const balance = getUserBalance(userId);
232
+ const month = new Date().toISOString().slice(0, 7);
233
+ const today = new Date().toISOString().slice(0, 10);
234
+ const usage = getMonthlyUsage(userId, month);
235
+ const dailyUsage = getDailyUsage(userId, today);
236
+ const weeklySummary = getWeeklyUsageSummary(userId);
237
+ const plan = effective?.plan ?? null;
238
+ const isFallback = !realSubscription && !!effective;
239
+ const access = checkBillingAccess(userId, user.role);
240
+ return c.json({
241
+ // User info
242
+ user_id: user.id,
243
+ username: user.username,
244
+ display_name: user.display_name,
245
+ role: user.role,
246
+ // Plan info
247
+ plan_id: plan?.id ?? null,
248
+ plan_name: plan?.name ?? null,
249
+ subscription_status: realSubscription?.status ?? (isFallback ? 'default' : null),
250
+ is_fallback: isFallback,
251
+ has_real_subscription: !!realSubscription,
252
+ access_allowed: access.allowed,
253
+ access_block_type: access.blockType ?? null,
254
+ access_reason: access.reason ?? null,
255
+ min_balance_usd: access.minBalanceUsd,
256
+ // Balance
257
+ balance_usd: balance.balance_usd,
258
+ // Usage
259
+ current_month_cost: usage?.total_cost_usd ?? 0,
260
+ daily_cost_used: dailyUsage?.total_cost_usd ?? 0,
261
+ daily_cost_quota: plan?.daily_cost_quota ?? null,
262
+ weekly_cost_used: weeklySummary.totalCost,
263
+ weekly_cost_quota: plan?.weekly_cost_quota ?? null,
264
+ monthly_cost_quota: plan?.monthly_cost_quota ?? null,
265
+ });
266
+ });
267
+ // GET /admin/users/:id/transactions — user balance transactions
268
+ billingRoutes.get('/admin/users/:id/transactions', authMiddleware, billingManageMiddleware, async (c) => {
269
+ const userId = c.req.param('id');
270
+ const limit = Math.min(parseInt(c.req.query('limit') || '20'), 100);
271
+ const offset = parseInt(c.req.query('offset') || '0');
272
+ const result = getBalanceTransactions(userId, limit, offset);
273
+ return c.json(result);
274
+ });
275
+ // POST /admin/users/:id/assign-plan — assign plan to user
276
+ billingRoutes.post('/admin/users/:id/assign-plan', authMiddleware, billingManageMiddleware, async (c) => {
277
+ const userId = c.req.param('id');
278
+ const body = await c.req.json().catch(() => ({}));
279
+ const validation = AssignPlanSchema.safeParse(body);
280
+ if (!validation.success) {
281
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
282
+ }
283
+ const actor = c.get('user');
284
+ try {
285
+ assignPlan(userId, validation.data.plan_id, actor.id, validation.data.duration_days);
286
+ return c.json({ success: true });
287
+ }
288
+ catch (err) {
289
+ const message = err instanceof Error ? err.message : 'Failed to assign plan';
290
+ return c.json({ error: message }, 400);
291
+ }
292
+ });
293
+ // POST /admin/users/:id/adjust-balance — adjust user balance
294
+ billingRoutes.post('/admin/users/:id/adjust-balance', authMiddleware, billingManageMiddleware, async (c) => {
295
+ const userId = c.req.param('id');
296
+ const body = await c.req.json().catch(() => ({}));
297
+ const validation = AdjustBalanceSchema.safeParse(body);
298
+ if (!validation.success) {
299
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
300
+ }
301
+ const actor = c.get('user');
302
+ try {
303
+ const tx = applyAdminBalanceAdjustment(userId, validation.data.amount_usd, validation.data.description, actor.id, validation.data.idempotency_key);
304
+ return c.json(tx);
305
+ }
306
+ catch (err) {
307
+ const message = err instanceof Error ? err.message : '调整余额失败';
308
+ return c.json({ error: message }, 400);
309
+ }
310
+ });
311
+ // GET /admin/redeem-codes — list all redeem codes
312
+ billingRoutes.get('/admin/redeem-codes', authMiddleware, billingManageMiddleware, async (c) => {
313
+ const codes = getAllRedeemCodes();
314
+ return c.json({ codes });
315
+ });
316
+ // POST /admin/redeem-codes — create redeem code(s)
317
+ billingRoutes.post('/admin/redeem-codes', authMiddleware, billingManageMiddleware, async (c) => {
318
+ const body = await c.req.json().catch(() => ({}));
319
+ const validation = RedeemCodeCreateSchema.safeParse(body);
320
+ if (!validation.success) {
321
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
322
+ }
323
+ const actor = c.get('user');
324
+ const count = validation.data.count ?? 1;
325
+ const prefix = validation.data.prefix ?? '';
326
+ const now = new Date();
327
+ const expiresAt = validation.data.expires_in_hours
328
+ ? new Date(now.getTime() + validation.data.expires_in_hours * 60 * 60 * 1000).toISOString()
329
+ : null;
330
+ const batchId = count > 1 ? `batch_${Date.now()}` : null;
331
+ const codes = [];
332
+ for (let i = 0; i < count; i++) {
333
+ const rawCode = generateRedeemCode();
334
+ const code = {
335
+ code: prefix ? `${prefix}-${rawCode}` : rawCode,
336
+ type: validation.data.type,
337
+ value_usd: validation.data.value_usd ?? null,
338
+ plan_id: validation.data.plan_id ?? null,
339
+ duration_days: validation.data.duration_days ?? null,
340
+ max_uses: validation.data.max_uses ?? 1,
341
+ used_count: 0,
342
+ expires_at: expiresAt,
343
+ created_by: actor.id,
344
+ notes: validation.data.notes ?? null,
345
+ batch_id: batchId,
346
+ created_at: now.toISOString(),
347
+ };
348
+ dbCreateRedeemCode(code);
349
+ codes.push(code);
350
+ }
351
+ logBillingAudit('code_created', actor.id, actor.id, {
352
+ count,
353
+ type: validation.data.type,
354
+ codes: codes.map((c) => c.code),
355
+ });
356
+ return c.json({ codes }, 201);
357
+ });
358
+ // DELETE /admin/redeem-codes/:code — delete redeem code
359
+ billingRoutes.delete('/admin/redeem-codes/:code', authMiddleware, billingManageMiddleware, async (c) => {
360
+ const code = c.req.param('code');
361
+ const deleted = dbDeleteRedeemCode(code);
362
+ if (!deleted)
363
+ return c.json({ error: 'Code not found' }, 404);
364
+ const actor = c.get('user');
365
+ logBillingAudit('code_deleted', actor.id, actor.id, { code });
366
+ return c.json({ success: true });
367
+ });
368
+ // GET /admin/audit-log — billing audit log
369
+ billingRoutes.get('/admin/audit-log', authMiddleware, billingManageMiddleware, async (c) => {
370
+ const limit = Math.min(parseInt(c.req.query('limit') || '50'), 200);
371
+ const offset = parseInt(c.req.query('offset') || '0');
372
+ const userId = c.req.query('user_id');
373
+ const eventType = c.req.query('event_type');
374
+ const result = getBillingAuditLog(limit, offset, userId || undefined, eventType || undefined);
375
+ return c.json(result);
376
+ });
377
+ // GET /admin/revenue — revenue summary
378
+ billingRoutes.get('/admin/revenue', authMiddleware, billingManageMiddleware, async (c) => {
379
+ const stats = getRevenueStats();
380
+ return c.json(stats);
381
+ });
382
+ // GET /admin/plans — all plans (including inactive)
383
+ billingRoutes.get('/admin/plans', authMiddleware, billingManageMiddleware, async (c) => {
384
+ const plans = getAllBillingPlans();
385
+ const subscriberCounts = getAllPlanSubscriberCounts();
386
+ const plansWithCount = plans.map((p) => ({
387
+ ...p,
388
+ subscriber_count: subscriberCounts[p.id] ?? 0,
389
+ }));
390
+ return c.json({ plans: plansWithCount });
391
+ });
392
+ // POST /admin/users/batch-assign-plan — batch assign plan
393
+ billingRoutes.post('/admin/users/batch-assign-plan', authMiddleware, billingManageMiddleware, async (c) => {
394
+ const body = await c.req.json().catch(() => ({}));
395
+ const validation = BatchAssignPlanSchema.safeParse(body);
396
+ if (!validation.success) {
397
+ return c.json({ error: 'Invalid request body', details: validation.error.format() }, 400);
398
+ }
399
+ const actor = c.get('user');
400
+ try {
401
+ const count = batchAssignPlans(validation.data.user_ids, validation.data.plan_id, actor.id, validation.data.duration_days);
402
+ return c.json({ success: true, count });
403
+ }
404
+ catch (err) {
405
+ const message = err instanceof Error ? err.message : 'Failed to batch assign';
406
+ return c.json({ error: message }, 400);
407
+ }
408
+ });
409
+ // POST /admin/users/:id/cancel-subscription — cancel user subscription
410
+ billingRoutes.post('/admin/users/:id/cancel-subscription', authMiddleware, billingManageMiddleware, async (c) => {
411
+ const userId = c.req.param('id');
412
+ const actor = c.get('user');
413
+ cancelSubscription(userId, actor.id);
414
+ return c.json({ success: true });
415
+ });
416
+ // GET /admin/users/:id/subscription-history — user subscription history
417
+ billingRoutes.get('/admin/users/:id/subscription-history', authMiddleware, billingManageMiddleware, async (c) => {
418
+ const userId = c.req.param('id');
419
+ const history = getSubscriptionHistory(userId);
420
+ return c.json({ history });
421
+ });
422
+ // GET /admin/redeem-codes/export — CSV export
423
+ billingRoutes.get('/admin/redeem-codes/export', authMiddleware, billingManageMiddleware, async (c) => {
424
+ const codes = getAllRedeemCodes();
425
+ const header = 'code,type,value_usd,plan_id,duration_days,max_uses,used_count,expires_at,notes,batch_id,created_at\n';
426
+ const csvEscape = (val) => {
427
+ const s = String(val ?? '');
428
+ if (s.includes(',') || s.includes('"') || s.includes('\n')) {
429
+ return `"${s.replace(/"/g, '""')}"`;
430
+ }
431
+ return s;
432
+ };
433
+ const rows = codes.map((rc) => [
434
+ csvEscape(rc.code),
435
+ csvEscape(rc.type),
436
+ csvEscape(rc.value_usd ?? ''),
437
+ csvEscape(rc.plan_id ?? ''),
438
+ csvEscape(rc.duration_days ?? ''),
439
+ csvEscape(rc.max_uses),
440
+ csvEscape(rc.used_count),
441
+ csvEscape(rc.expires_at ?? ''),
442
+ csvEscape(rc.notes ?? ''),
443
+ csvEscape(rc.batch_id ?? ''),
444
+ csvEscape(rc.created_at ?? ''),
445
+ ].join(','));
446
+ const csv = '\uFEFF' + header + rows.join('\n');
447
+ return new Response(csv, {
448
+ headers: {
449
+ 'Content-Type': 'text/csv; charset=utf-8',
450
+ 'Content-Disposition': `attachment; filename=redeem-codes-${new Date().toISOString().slice(0, 10)}.csv`,
451
+ },
452
+ });
453
+ });
454
+ // GET /admin/redeem-codes/:code/usage — redeem code usage details
455
+ billingRoutes.get('/admin/redeem-codes/:code/usage', authMiddleware, billingManageMiddleware, async (c) => {
456
+ const code = c.req.param('code');
457
+ const details = getRedeemCodeUsageDetails(code);
458
+ return c.json({ details });
459
+ });
460
+ // GET /admin/dashboard — dashboard stats
461
+ billingRoutes.get('/admin/dashboard', authMiddleware, billingManageMiddleware, async (c) => {
462
+ const stats = getDashboardStats();
463
+ return c.json(stats);
464
+ });
465
+ // GET /admin/revenue/trend — revenue trend by month
466
+ billingRoutes.get('/admin/revenue/trend', authMiddleware, billingManageMiddleware, async (c) => {
467
+ const months = Math.min(parseInt(c.req.query('months') || '6'), 24);
468
+ const trend = getRevenueTrend(months);
469
+ return c.json({ trend });
470
+ });
471
+ // GET /my/usage/daily — daily usage history for charts
472
+ billingRoutes.get('/my/usage/daily', authMiddleware, async (c) => {
473
+ const user = c.get('user');
474
+ const days = Math.min(parseInt(c.req.query('days') || '14'), 90);
475
+ const history = getUserDailyUsageHistory(user.id, days);
476
+ return c.json({ history });
477
+ });
478
+ // GET /status — billing enabled status + currency settings (no manage_billing required)
479
+ billingRoutes.get('/status', authMiddleware, async (c) => {
480
+ const enabled = isBillingEnabled();
481
+ const settings = getSystemSettings();
482
+ return c.json({
483
+ enabled,
484
+ mode: settings.billingMode ?? 'wallet_first',
485
+ minStartBalanceUsd: settings.billingMinStartBalanceUsd ?? 0.01,
486
+ currency: settings.billingCurrency ?? 'USD',
487
+ currencyRate: settings.billingCurrencyRate ?? 1,
488
+ });
489
+ });
490
+ export default billingRoutes;