@jingyi0605/codingns4dsh 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/README.md +1114 -0
  2. package/assets/provider-icons/claude-code.png +0 -0
  3. package/assets/provider-icons/codex.png +0 -0
  4. package/assets/provider-icons/command-code.svg +5 -0
  5. package/assets/provider-icons/deepseek-harness.svg +9 -0
  6. package/assets/provider-icons/gemini.png +0 -0
  7. package/assets/provider-icons/grok.png +0 -0
  8. package/assets/provider-icons/kimi.png +0 -0
  9. package/assets/provider-icons/opencode.png +0 -0
  10. package/assets/provider-icons/pi.svg +7 -0
  11. package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +97 -0
  12. package/data/build/dist/bootstrap/dsh-connection-adapter.js +169 -0
  13. package/data/build/dist/bootstrap/index.d.ts +38 -0
  14. package/data/build/dist/bootstrap/index.js +44 -0
  15. package/data/build/dist/client/account-bar.d.ts +9 -0
  16. package/data/build/dist/client/account-bar.js +398 -0
  17. package/data/build/dist/client/bundle.js +26167 -0
  18. package/data/build/dist/client/cli-catalog.d.ts +24 -0
  19. package/data/build/dist/client/cli-catalog.js +108 -0
  20. package/data/build/dist/client/cli-slots.d.ts +38 -0
  21. package/data/build/dist/client/cli-slots.js +297 -0
  22. package/data/build/dist/client/debug/ui.d.ts +12 -0
  23. package/data/build/dist/client/debug/ui.js +451 -0
  24. package/data/build/dist/client/dsh-h5-bootstrap.d.ts +64 -0
  25. package/data/build/dist/client/dsh-h5-bootstrap.js +293 -0
  26. package/data/build/dist/client/dsh-runtime-version.d.ts +3 -0
  27. package/data/build/dist/client/dsh-runtime-version.js +11 -0
  28. package/data/build/dist/client/external-tool-stream.d.ts +24 -0
  29. package/data/build/dist/client/external-tool-stream.js +114 -0
  30. package/data/build/dist/client/features/cli-adapters.d.ts +7 -0
  31. package/data/build/dist/client/features/cli-adapters.js +215 -0
  32. package/data/build/dist/client/features/debug.d.ts +4 -0
  33. package/data/build/dist/client/features/debug.js +24 -0
  34. package/data/build/dist/client/features/index.d.ts +33 -0
  35. package/data/build/dist/client/features/index.js +44 -0
  36. package/data/build/dist/client/features/lan-access-panel.d.ts +5 -0
  37. package/data/build/dist/client/features/lan-access-panel.js +146 -0
  38. package/data/build/dist/client/features/lan-access.d.ts +9 -0
  39. package/data/build/dist/client/features/lan-access.js +30 -0
  40. package/data/build/dist/client/features/login-protection-panel.d.ts +5 -0
  41. package/data/build/dist/client/features/login-protection-panel.js +91 -0
  42. package/data/build/dist/client/features/login-protection-session.d.ts +6 -0
  43. package/data/build/dist/client/features/login-protection-session.js +25 -0
  44. package/data/build/dist/client/features/login-protection.d.ts +4 -0
  45. package/data/build/dist/client/features/login-protection.js +23 -0
  46. package/data/build/dist/client/features/reverse-proxy-panel.d.ts +9 -0
  47. package/data/build/dist/client/features/reverse-proxy-panel.js +159 -0
  48. package/data/build/dist/client/features/reverse-proxy.d.ts +12 -0
  49. package/data/build/dist/client/features/reverse-proxy.js +112 -0
  50. package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +5 -0
  51. package/data/build/dist/client/features/terminal-enhancement-panel.js +160 -0
  52. package/data/build/dist/client/features/terminal-enhancement.d.ts +4 -0
  53. package/data/build/dist/client/features/terminal-enhancement.js +24 -0
  54. package/data/build/dist/client/features/types.d.ts +58 -0
  55. package/data/build/dist/client/features/types.js +2 -0
  56. package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +5 -0
  57. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +66 -0
  58. package/data/build/dist/client/features/workspace-session-enhancement.d.ts +4 -0
  59. package/data/build/dist/client/features/workspace-session-enhancement.js +101 -0
  60. package/data/build/dist/client/h5-bootstrap-entry.d.ts +2 -0
  61. package/data/build/dist/client/h5-bootstrap-entry.js +17 -0
  62. package/data/build/dist/client/index.d.ts +31 -0
  63. package/data/build/dist/client/index.js +98 -0
  64. package/data/build/dist/client/lan-access.d.ts +28 -0
  65. package/data/build/dist/client/lan-access.js +68 -0
  66. package/data/build/dist/client/locale.d.ts +20 -0
  67. package/data/build/dist/client/locale.js +393 -0
  68. package/data/build/dist/client/provider-icon-assets.d.ts +2 -0
  69. package/data/build/dist/client/provider-icon-assets.js +23 -0
  70. package/data/build/dist/client/provider-icons.d.ts +19 -0
  71. package/data/build/dist/client/provider-icons.js +39 -0
  72. package/data/build/dist/client/remote-web-context.d.ts +57 -0
  73. package/data/build/dist/client/remote-web-context.js +939 -0
  74. package/data/build/dist/client/session-adapter-cache.d.ts +18 -0
  75. package/data/build/dist/client/session-adapter-cache.js +84 -0
  76. package/data/build/dist/client/settings-bridge.d.ts +32 -0
  77. package/data/build/dist/client/settings-bridge.js +126 -0
  78. package/data/build/dist/client/settings-section.d.ts +30 -0
  79. package/data/build/dist/client/settings-section.js +93 -0
  80. package/data/build/dist/client/subscription-slot.d.ts +18 -0
  81. package/data/build/dist/client/subscription-slot.js +223 -0
  82. package/data/build/dist/client/terminal/index.d.ts +4 -0
  83. package/data/build/dist/client/terminal/index.js +3 -0
  84. package/data/build/dist/client/terminal/model.d.ts +116 -0
  85. package/data/build/dist/client/terminal/model.js +435 -0
  86. package/data/build/dist/client/terminal/styles.d.ts +21 -0
  87. package/data/build/dist/client/terminal/styles.js +51 -0
  88. package/data/build/dist/client/terminal/ui.d.ts +19 -0
  89. package/data/build/dist/client/terminal/ui.js +257 -0
  90. package/data/build/dist/client/terminal/xterm-view.d.ts +13 -0
  91. package/data/build/dist/client/terminal/xterm-view.js +213 -0
  92. package/data/build/dist/client/theme.d.ts +56 -0
  93. package/data/build/dist/client/theme.js +186 -0
  94. package/data/build/dist/client/workspace-session-archive-dom.d.ts +29 -0
  95. package/data/build/dist/client/workspace-session-archive-dom.js +525 -0
  96. package/data/build/dist/client/workspace-session-fiber.d.ts +3 -0
  97. package/data/build/dist/client/workspace-session-fiber.js +33 -0
  98. package/data/build/dist/client/workspace-session-logo-dom.d.ts +26 -0
  99. package/data/build/dist/client/workspace-session-logo-dom.js +128 -0
  100. package/data/build/dist/features/index.d.ts +3 -0
  101. package/data/build/dist/features/index.js +2 -0
  102. package/data/build/dist/features/registry.d.ts +74 -0
  103. package/data/build/dist/features/registry.js +382 -0
  104. package/data/build/dist/features/resource-scope/index.d.ts +39 -0
  105. package/data/build/dist/features/resource-scope/index.js +161 -0
  106. package/data/build/dist/host/auth-session.d.ts +42 -0
  107. package/data/build/dist/host/auth-session.js +186 -0
  108. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +14 -0
  109. package/data/build/dist/host/cli-adapters/claude-driver.js +103 -0
  110. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +45 -0
  111. package/data/build/dist/host/cli-adapters/codex-driver.js +534 -0
  112. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +38 -0
  113. package/data/build/dist/host/cli-adapters/command-code-driver.js +394 -0
  114. package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +19 -0
  115. package/data/build/dist/host/cli-adapters/command-code-subscription.js +126 -0
  116. package/data/build/dist/host/cli-adapters/driver.d.ts +41 -0
  117. package/data/build/dist/host/cli-adapters/driver.js +2 -0
  118. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +37 -0
  119. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +180 -0
  120. package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +48 -0
  121. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +373 -0
  122. package/data/build/dist/host/cli-adapters/feature.d.ts +7 -0
  123. package/data/build/dist/host/cli-adapters/feature.js +335 -0
  124. package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +24 -0
  125. package/data/build/dist/host/cli-adapters/gemini-driver.js +427 -0
  126. package/data/build/dist/host/cli-adapters/grok-driver.d.ts +38 -0
  127. package/data/build/dist/host/cli-adapters/grok-driver.js +372 -0
  128. package/data/build/dist/host/cli-adapters/http-sse-client.d.ts +22 -0
  129. package/data/build/dist/host/cli-adapters/http-sse-client.js +99 -0
  130. package/data/build/dist/host/cli-adapters/interaction-events.d.ts +11 -0
  131. package/data/build/dist/host/cli-adapters/interaction-events.js +80 -0
  132. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +65 -0
  133. package/data/build/dist/host/cli-adapters/json-rpc-process.js +256 -0
  134. package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +21 -0
  135. package/data/build/dist/host/cli-adapters/kimi-driver.js +382 -0
  136. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +22 -0
  137. package/data/build/dist/host/cli-adapters/model-catalog.js +89 -0
  138. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +55 -0
  139. package/data/build/dist/host/cli-adapters/opencode-driver.js +728 -0
  140. package/data/build/dist/host/cli-adapters/pi-driver.d.ts +43 -0
  141. package/data/build/dist/host/cli-adapters/pi-driver.js +312 -0
  142. package/data/build/dist/host/cli-adapters/process-utils.d.ts +16 -0
  143. package/data/build/dist/host/cli-adapters/process-utils.js +27 -0
  144. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +82 -0
  145. package/data/build/dist/host/cli-adapters/provider-subscription.js +610 -0
  146. package/data/build/dist/host/cli-adapters/registry.d.ts +95 -0
  147. package/data/build/dist/host/cli-adapters/registry.js +659 -0
  148. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +24 -0
  149. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +119 -0
  150. package/data/build/dist/host/cli-adapters/session-probe.d.ts +20 -0
  151. package/data/build/dist/host/cli-adapters/session-probe.js +139 -0
  152. package/data/build/dist/host/cli-adapters/session-store.d.ts +68 -0
  153. package/data/build/dist/host/cli-adapters/session-store.js +180 -0
  154. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +45 -0
  155. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +205 -0
  156. package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +25 -0
  157. package/data/build/dist/host/cli-adapters/stream-normalizer.js +83 -0
  158. package/data/build/dist/host/cli-adapters/tool-observation.d.ts +9 -0
  159. package/data/build/dist/host/cli-adapters/tool-observation.js +39 -0
  160. package/data/build/dist/host/control-api-client.d.ts +84 -0
  161. package/data/build/dist/host/control-api-client.js +130 -0
  162. package/data/build/dist/host/credential-store.d.ts +54 -0
  163. package/data/build/dist/host/credential-store.js +114 -0
  164. package/data/build/dist/host/debug.d.ts +79 -0
  165. package/data/build/dist/host/debug.js +295 -0
  166. package/data/build/dist/host/dsh-device-runtime.d.ts +39 -0
  167. package/data/build/dist/host/dsh-device-runtime.js +118 -0
  168. package/data/build/dist/host/dsh-gateway-feature.d.ts +5 -0
  169. package/data/build/dist/host/dsh-gateway-feature.js +55 -0
  170. package/data/build/dist/host/dsh-runtime-version.d.ts +12 -0
  171. package/data/build/dist/host/dsh-runtime-version.js +69 -0
  172. package/data/build/dist/host/features/auth.d.ts +11 -0
  173. package/data/build/dist/host/features/auth.js +273 -0
  174. package/data/build/dist/host/features/debug.d.ts +5 -0
  175. package/data/build/dist/host/features/debug.js +90 -0
  176. package/data/build/dist/host/features/host-status.d.ts +12 -0
  177. package/data/build/dist/host/features/host-status.js +54 -0
  178. package/data/build/dist/host/features/index.d.ts +19 -0
  179. package/data/build/dist/host/features/index.js +29 -0
  180. package/data/build/dist/host/features/lan-access-dsh.d.ts +8 -0
  181. package/data/build/dist/host/features/lan-access-dsh.js +45 -0
  182. package/data/build/dist/host/features/terminal-process.d.ts +5 -0
  183. package/data/build/dist/host/features/terminal-process.js +110 -0
  184. package/data/build/dist/host/features/terminal-status.d.ts +18 -0
  185. package/data/build/dist/host/features/terminal-status.js +56 -0
  186. package/data/build/dist/host/features/types.d.ts +41 -0
  187. package/data/build/dist/host/features/types.js +2 -0
  188. package/data/build/dist/host/index.d.ts +43 -0
  189. package/data/build/dist/host/index.js +135 -0
  190. package/data/build/dist/host/lan-access-dsh.d.ts +83 -0
  191. package/data/build/dist/host/lan-access-dsh.js +831 -0
  192. package/data/build/dist/host/native-session-bridge.d.ts +130 -0
  193. package/data/build/dist/host/native-session-bridge.js +317 -0
  194. package/data/build/dist/host/relay-tunnel-runtime.d.ts +97 -0
  195. package/data/build/dist/host/relay-tunnel-runtime.js +361 -0
  196. package/data/build/dist/host/remote-web-runtime.d.ts +78 -0
  197. package/data/build/dist/host/remote-web-runtime.js +559 -0
  198. package/data/build/dist/host/rpc-table.d.ts +32 -0
  199. package/data/build/dist/host/rpc-table.js +47 -0
  200. package/data/build/dist/host/rpc.d.ts +18 -0
  201. package/data/build/dist/host/rpc.js +248 -0
  202. package/data/build/dist/host/settings.d.ts +19 -0
  203. package/data/build/dist/host/settings.js +80 -0
  204. package/data/build/dist/host/terminal/attachment-registry.d.ts +17 -0
  205. package/data/build/dist/host/terminal/attachment-registry.js +49 -0
  206. package/data/build/dist/host/terminal/backends/conpty-backend.d.ts +39 -0
  207. package/data/build/dist/host/terminal/backends/conpty-backend.js +160 -0
  208. package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts +36 -0
  209. package/data/build/dist/host/terminal/backends/local-pty-backend.js +178 -0
  210. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -0
  211. package/data/build/dist/host/terminal/backends/tmux-backend.js +191 -0
  212. package/data/build/dist/host/terminal/broker/conpty-broker-client.d.ts +27 -0
  213. package/data/build/dist/host/terminal/broker/conpty-broker-client.js +154 -0
  214. package/data/build/dist/host/terminal/broker/conpty-broker-process.d.ts +10 -0
  215. package/data/build/dist/host/terminal/broker/conpty-broker-process.js +147 -0
  216. package/data/build/dist/host/terminal/broker/conpty-broker-protocol.d.ts +92 -0
  217. package/data/build/dist/host/terminal/broker/conpty-broker-protocol.js +134 -0
  218. package/data/build/dist/host/terminal/controller-factory.d.ts +37 -0
  219. package/data/build/dist/host/terminal/controller-factory.js +82 -0
  220. package/data/build/dist/host/terminal/index.d.ts +17 -0
  221. package/data/build/dist/host/terminal/index.js +17 -0
  222. package/data/build/dist/host/terminal/runtime-adapter.d.ts +77 -0
  223. package/data/build/dist/host/terminal/runtime-adapter.js +18 -0
  224. package/data/build/dist/host/terminal/runtime-manager.d.ts +41 -0
  225. package/data/build/dist/host/terminal/runtime-manager.js +179 -0
  226. package/data/build/dist/host/terminal/shell-detection.d.ts +23 -0
  227. package/data/build/dist/host/terminal/shell-detection.js +105 -0
  228. package/data/build/dist/host/terminal/startup.d.ts +33 -0
  229. package/data/build/dist/host/terminal/startup.js +100 -0
  230. package/data/build/dist/host/terminal/terminal-controller.d.ts +78 -0
  231. package/data/build/dist/host/terminal/terminal-controller.js +324 -0
  232. package/data/build/dist/host/terminal/terminal-process-service.d.ts +30 -0
  233. package/data/build/dist/host/terminal/terminal-process-service.js +194 -0
  234. package/data/build/dist/host/terminal/terminal-process-store.d.ts +45 -0
  235. package/data/build/dist/host/terminal/terminal-process-store.js +183 -0
  236. package/data/build/dist/host/terminal/terminal-service.d.ts +87 -0
  237. package/data/build/dist/host/terminal/terminal-service.js +528 -0
  238. package/data/build/dist/host/terminal/terminal-store.d.ts +49 -0
  239. package/data/build/dist/host/terminal/terminal-store.js +242 -0
  240. package/data/build/dist/index.d.ts +37 -0
  241. package/data/build/dist/index.js +26 -0
  242. package/data/build/dist/shared/contracts/auth.d.ts +122 -0
  243. package/data/build/dist/shared/contracts/auth.js +8 -0
  244. package/data/build/dist/shared/contracts/cli-adapter.d.ts +192 -0
  245. package/data/build/dist/shared/contracts/cli-adapter.js +2 -0
  246. package/data/build/dist/shared/contracts/command-code.d.ts +5 -0
  247. package/data/build/dist/shared/contracts/command-code.js +2 -0
  248. package/data/build/dist/shared/contracts/config.d.ts +113 -0
  249. package/data/build/dist/shared/contracts/config.js +86 -0
  250. package/data/build/dist/shared/contracts/debug-config.d.ts +28 -0
  251. package/data/build/dist/shared/contracts/debug-config.js +78 -0
  252. package/data/build/dist/shared/contracts/debug.d.ts +75 -0
  253. package/data/build/dist/shared/contracts/debug.js +121 -0
  254. package/data/build/dist/shared/contracts/dsh-device.d.ts +72 -0
  255. package/data/build/dist/shared/contracts/dsh-device.js +2 -0
  256. package/data/build/dist/shared/contracts/errors.d.ts +33 -0
  257. package/data/build/dist/shared/contracts/errors.js +41 -0
  258. package/data/build/dist/shared/contracts/feature.d.ts +71 -0
  259. package/data/build/dist/shared/contracts/feature.js +2 -0
  260. package/data/build/dist/shared/contracts/host-status.d.ts +14 -0
  261. package/data/build/dist/shared/contracts/host-status.js +2 -0
  262. package/data/build/dist/shared/contracts/lan-access-dsh.d.ts +30 -0
  263. package/data/build/dist/shared/contracts/lan-access-dsh.js +2 -0
  264. package/data/build/dist/shared/contracts/peer-host.d.ts +37 -0
  265. package/data/build/dist/shared/contracts/peer-host.js +9 -0
  266. package/data/build/dist/shared/contracts/signaling.d.ts +67 -0
  267. package/data/build/dist/shared/contracts/signaling.js +2 -0
  268. package/data/build/dist/shared/contracts/subscription.d.ts +69 -0
  269. package/data/build/dist/shared/contracts/subscription.js +2 -0
  270. package/data/build/dist/shared/contracts/terminal-process.d.ts +65 -0
  271. package/data/build/dist/shared/contracts/terminal-process.js +2 -0
  272. package/data/build/dist/shared/contracts/terminal.d.ts +127 -0
  273. package/data/build/dist/shared/contracts/terminal.js +2 -0
  274. package/data/build/dist/shared/contracts/transport.d.ts +45 -0
  275. package/data/build/dist/shared/contracts/transport.js +3 -0
  276. package/data/build/dist/shared/contracts/version.d.ts +19 -0
  277. package/data/build/dist/shared/contracts/version.js +74 -0
  278. package/data/build/dist/shared/index.d.ts +22 -0
  279. package/data/build/dist/shared/index.js +9 -0
  280. package/data/build/dist/transport/carrier.d.ts +38 -0
  281. package/data/build/dist/transport/carrier.js +268 -0
  282. package/data/build/dist/transport/debug.d.ts +22 -0
  283. package/data/build/dist/transport/debug.js +57 -0
  284. package/data/build/dist/transport/dsh-envelope.d.ts +41 -0
  285. package/data/build/dist/transport/dsh-envelope.js +107 -0
  286. package/data/build/dist/transport/dsh-gateway.d.ts +64 -0
  287. package/data/build/dist/transport/dsh-gateway.js +254 -0
  288. package/data/build/dist/transport/dsh-session.d.ts +55 -0
  289. package/data/build/dist/transport/dsh-session.js +256 -0
  290. package/data/build/dist/transport/dsh-transport.d.ts +57 -0
  291. package/data/build/dist/transport/dsh-transport.js +125 -0
  292. package/data/build/dist/transport/frame.d.ts +132 -0
  293. package/data/build/dist/transport/frame.js +168 -0
  294. package/data/build/dist/transport/index.d.ts +11 -0
  295. package/data/build/dist/transport/index.js +11 -0
  296. package/data/build/dist/transport/multiplexer.d.ts +63 -0
  297. package/data/build/dist/transport/multiplexer.js +323 -0
  298. package/data/build/dist/transport/webrtc-client.d.ts +65 -0
  299. package/data/build/dist/transport/webrtc-client.js +255 -0
  300. package/data/build/dist/transport/webrtc-host.d.ts +58 -0
  301. package/data/build/dist/transport/webrtc-host.js +252 -0
  302. package/data/build/dist/typert.host.d.ts +25 -0
  303. package/data/build/dist/typert.host.js +150 -0
  304. package/dsh.bundle.patch +12 -0
  305. package/package.json +135 -0
  306. package/profile/README.md +27 -0
  307. package/profile/cordis.patch.yml +2 -0
  308. package/profile/package.json +20 -0
  309. package/profile/scripts/check-dsh-install.mjs +71 -0
  310. package/profile/version.json +5 -0
  311. package/scripts/check-dsh-install.mjs +88 -0
  312. package/version.json +6 -0
@@ -0,0 +1,610 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { spawn, spawnSync } from 'node:child_process';
5
+ import { JsonRpcProcess } from './json-rpc-process.js';
6
+ import { detectBinary } from './rpc-driver-utils.js';
7
+ /** 统一读取 Codex、Claude Code、OpenCode 订阅摘要。所有原始凭据都留在 Host。 */
8
+ export class ProviderSubscriptionService {
9
+ commandCode;
10
+ codex;
11
+ claudeCode;
12
+ opencode;
13
+ sub2api;
14
+ constructor(options = {}) {
15
+ this.commandCode = options.commandCode;
16
+ this.codex = new CodexSubscriptionService(options.codex);
17
+ this.claudeCode = new ClaudeCodeSubscriptionService(options.claudeCode);
18
+ this.opencode = new OpenCodeSubscriptionService(options.opencode);
19
+ this.sub2api = new Sub2ApiUsageService(options.sub2api);
20
+ }
21
+ read(adapterId) {
22
+ if (adapterId === 'codex' || adapterId === 'claude-code' || adapterId === 'dsh' || adapterId === 'grok' || adapterId === 'opencode') {
23
+ return this.readSub2ApiFirst(adapterId);
24
+ }
25
+ switch (adapterId) {
26
+ case 'command-code': return this.commandCode?.read() ?? Promise.resolve(null);
27
+ case 'codex': return this.codex.read();
28
+ case 'claude-code': return this.claudeCode.read();
29
+ case 'opencode': return this.opencode.read();
30
+ default: return Promise.resolve(null);
31
+ }
32
+ }
33
+ async readSub2ApiFirst(adapterId) {
34
+ // 已配置第三方上游时,官方额度接口没有意义;即使 Sub2API 探测失败也必须隐藏,
35
+ // 不能把旧的官方订阅窗口误显示成当前上游的用量。
36
+ const hasThirdPartySource = this.sub2api.hasSource(adapterId);
37
+ const upstream = await this.sub2api.read(adapterId);
38
+ if (upstream !== null)
39
+ return upstream;
40
+ if (hasThirdPartySource)
41
+ return null;
42
+ if (adapterId === 'codex')
43
+ return this.codex.read();
44
+ if (adapterId === 'claude-code')
45
+ return this.claudeCode.read();
46
+ return null;
47
+ }
48
+ }
49
+ /** 通过 Provider 的上游 base URL 检测 Sub2API;只返回脱敏后的统计摘要。 */
50
+ export class Sub2ApiUsageService {
51
+ request;
52
+ timeoutMs;
53
+ configuredSources;
54
+ constructor(options = {}) {
55
+ this.request = options.fetch ?? fetch;
56
+ this.timeoutMs = options.timeoutMs ?? 8_000;
57
+ this.configuredSources = options.sources;
58
+ }
59
+ hasSource(adapterId) {
60
+ const configured = this.configuredSources?.[adapterId];
61
+ if (configured !== undefined)
62
+ return Array.isArray(configured) ? configured.length > 0 : true;
63
+ return resolveSub2ApiSources(adapterId).length > 0;
64
+ }
65
+ async read(adapterId) {
66
+ const configured = this.configuredSources?.[adapterId];
67
+ const sources = configured === undefined ? resolveSub2ApiSources(adapterId) : Array.isArray(configured) ? configured : [configured];
68
+ for (const source of sources) {
69
+ const result = await this.readSource(source);
70
+ if (result !== null)
71
+ return result;
72
+ }
73
+ return null;
74
+ }
75
+ async readSource(source) {
76
+ const baseUrl = source.baseUrl.trim().replace(/\/+$/u, '');
77
+ const usagePath = /\/v1$/u.test(baseUrl) ? '/usage' : '/v1/usage';
78
+ const controller = new AbortController();
79
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
80
+ try {
81
+ const response = await this.request(`${baseUrl}${usagePath}`, {
82
+ headers: { Authorization: source.apiKey.trim().startsWith('Bearer ') ? source.apiKey.trim() : `Bearer ${source.apiKey.trim()}`, Accept: 'application/json' },
83
+ signal: controller.signal,
84
+ });
85
+ if (!response.ok)
86
+ return null;
87
+ const usage = normalizeSub2ApiUsage(await response.json(), baseUrl);
88
+ if (usage === null)
89
+ return null;
90
+ const logoUrl = buildLogoUrl(baseUrl);
91
+ const logoDataUrl = logoUrl === '' ? '' : await readLogoDataUrl(logoUrl, this.request, this.timeoutMs);
92
+ return { authenticated: true, planType: usage.planName, primary: null, secondary: null, monthly: null, rateLimitReachedType: null, resetCredits: null, capturedAt: new Date().toISOString(), sub2api: { ...usage, logoUrl, logoDataUrl } };
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ finally {
98
+ clearTimeout(timer);
99
+ }
100
+ }
101
+ }
102
+ export class CodexSubscriptionService {
103
+ binaries;
104
+ runSpawnSync;
105
+ runSpawn;
106
+ timeoutMs;
107
+ homeDirectory;
108
+ command = null;
109
+ constructor(options = {}) {
110
+ this.binaries = options.binaries ?? ['codex'];
111
+ this.runSpawnSync = options.spawnSync ?? spawnSync;
112
+ this.runSpawn = options.spawn ?? spawn;
113
+ this.timeoutMs = options.timeoutMs ?? 8_000;
114
+ this.homeDirectory = options.homeDirectory ?? (process.env.CODEX_HOME ?? join(homedir(), '.codex'));
115
+ }
116
+ async read() {
117
+ if (hasThirdPartyCodexConfig(this.homeDirectory))
118
+ return null;
119
+ try {
120
+ this.command ??= (await detectBinary({ binaries: this.binaries, spawnSync: this.runSpawnSync })).command;
121
+ if (this.command === null)
122
+ return null;
123
+ const rpc = new JsonRpcProcess({ command: this.command, args: ['app-server'], spawn: this.runSpawn });
124
+ const controller = new AbortController();
125
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
126
+ try {
127
+ await rpc.request('initialize', { clientInfo: { name: 'codingns4dsh', version: '0.1.0' }, capabilities: {} }, { signal: controller.signal });
128
+ rpc.notify('initialized', {});
129
+ const result = await rpc.request('account/rateLimits/read', {}, { signal: controller.signal });
130
+ return normalizeCodexSnapshot(result);
131
+ }
132
+ finally {
133
+ clearTimeout(timer);
134
+ rpc.dispose();
135
+ }
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ }
142
+ export class ClaudeCodeSubscriptionService {
143
+ homeDirectory;
144
+ request;
145
+ timeoutMs;
146
+ constructor(options = {}) {
147
+ this.homeDirectory = options.homeDirectory ?? join(homedir(), '.claude');
148
+ this.request = options.fetch ?? fetch;
149
+ this.timeoutMs = options.timeoutMs ?? 8_000;
150
+ }
151
+ async read() {
152
+ const auth = readJson(join(this.homeDirectory, '.credentials.json'));
153
+ const oauth = recordValue(auth)?.claudeAiOauth;
154
+ const token = textValue(recordValue(oauth)?.accessToken);
155
+ if (token === null)
156
+ return null;
157
+ if (hasThirdPartyClaudeConfig(this.homeDirectory))
158
+ return null;
159
+ const controller = new AbortController();
160
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
161
+ try {
162
+ const response = await this.request('https://api.anthropic.com/api/oauth/usage', {
163
+ headers: {
164
+ Authorization: `Bearer ${token}`,
165
+ Accept: 'application/json',
166
+ 'anthropic-beta': 'oauth-2025-04-20',
167
+ },
168
+ signal: controller.signal,
169
+ });
170
+ if (!response.ok)
171
+ return null;
172
+ const snapshot = normalizeClaudeSnapshot(await response.json(), textValue(recordValue(oauth)?.subscriptionType));
173
+ return hasSubscriptionWindow(snapshot) ? snapshot : null;
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ finally {
179
+ clearTimeout(timer);
180
+ }
181
+ }
182
+ }
183
+ export class OpenCodeSubscriptionService {
184
+ homeDirectory;
185
+ constructor(options = {}) {
186
+ this.homeDirectory = options.homeDirectory ?? defaultOpenCodeDataDirectory();
187
+ }
188
+ async read() {
189
+ const candidates = [
190
+ join(this.homeDirectory, 'auth.json'),
191
+ join(homedir(), '.config', 'opencode', 'auth.json'),
192
+ join(homedir(), 'Library', 'Application Support', 'opencode', 'auth.json'),
193
+ ...(process.platform === 'win32' && process.env.APPDATA
194
+ ? [join(process.env.APPDATA, 'opencode', 'auth.json')]
195
+ : []),
196
+ ];
197
+ const auth = candidates.map(readJson).find((value) => value !== null);
198
+ if (auth === null || auth === undefined || Object.keys(auth).length === 0)
199
+ return null;
200
+ // OpenCode 支持多个第三方 Provider,目前没有稳定的统一额度协议;只报告认证状态。
201
+ const snapshot = normalizeOpenCodeSnapshot(auth);
202
+ if (snapshot !== null)
203
+ return snapshot;
204
+ const provider = Object.keys(auth).find((key) => key.trim() !== '') ?? null;
205
+ return null;
206
+ }
207
+ }
208
+ function normalizeCodexSnapshot(value) {
209
+ const root = recordValue(value);
210
+ const source = recordValue(root?.rateLimits ?? root?.rate_limits) ?? root;
211
+ if (source === null)
212
+ return null;
213
+ const primary = normalizeRateWindow(source.primary);
214
+ const secondary = normalizeRateWindow(source.secondary);
215
+ if (primary === null && secondary === null)
216
+ return null;
217
+ return {
218
+ authenticated: true,
219
+ planType: textValue(source.planType ?? source.plan_type),
220
+ primary,
221
+ secondary,
222
+ monthly: null,
223
+ rateLimitReachedType: textValue(source.rateLimitReachedType ?? source.rate_limit_reached_type),
224
+ resetCredits: null,
225
+ capturedAt: new Date().toISOString(),
226
+ };
227
+ }
228
+ function normalizeClaudeSnapshot(value, planType) {
229
+ const root = recordValue(value) ?? {};
230
+ const source = recordValue(root.data) ?? root;
231
+ return {
232
+ authenticated: true,
233
+ planType,
234
+ primary: normalizeUsageWindow(source.five_hour ?? source.fiveHour),
235
+ secondary: normalizeUsageWindow(source.seven_day ?? source.sevenDay),
236
+ monthly: normalizeUsageWindow(source.seven_day_opus ?? source.sevenDayOpus),
237
+ rateLimitReachedType: null,
238
+ resetCredits: null,
239
+ capturedAt: new Date().toISOString(),
240
+ };
241
+ }
242
+ function normalizeRateWindow(value) {
243
+ const source = recordValue(value);
244
+ if (source === null)
245
+ return null;
246
+ const usedPercent = numberValue(source.usedPercent ?? source.used_percent);
247
+ if (usedPercent === null)
248
+ return null;
249
+ return {
250
+ usedPercent: clamp(usedPercent),
251
+ remainingPercent: 100 - clamp(usedPercent),
252
+ windowDurationMins: integerValue(source.windowDurationMins ?? source.window_duration_mins),
253
+ resetsAt: timestampValue(source.resetsAt ?? source.resets_at),
254
+ };
255
+ }
256
+ function normalizeUsageWindow(value) {
257
+ const source = recordValue(value);
258
+ if (source === null)
259
+ return null;
260
+ const utilization = numberValue(source.utilization ?? source.usedPercent ?? source.used_percent);
261
+ if (utilization === null)
262
+ return null;
263
+ const usedPercent = clamp(utilization);
264
+ return { usedPercent, remainingPercent: 100 - usedPercent, windowDurationMins: null, resetsAt: timestampValue(source.resets_at ?? source.resetsAt) };
265
+ }
266
+ function normalizeOpenCodeSnapshot(value) {
267
+ const source = recordValue(value.rateLimits ?? value.rate_limits ?? value.usage ?? value.subscription);
268
+ if (source === null)
269
+ return null;
270
+ const primary = normalizeUsageWindow(source.five_hour ?? source.fiveHour ?? source.primary);
271
+ const secondary = normalizeUsageWindow(source.seven_day ?? source.sevenDay ?? source.secondary);
272
+ const monthly = normalizeUsageWindow(source.monthly);
273
+ if (primary === null && secondary === null && monthly === null)
274
+ return null;
275
+ return { authenticated: true, planType: textValue(source.planType ?? source.plan), primary, secondary, monthly, rateLimitReachedType: null, resetCredits: null, capturedAt: new Date().toISOString() };
276
+ }
277
+ function hasSubscriptionWindow(value) {
278
+ return value !== null && (value.primary !== null || value.secondary !== null || value.monthly !== null);
279
+ }
280
+ function hasThirdPartyCodexConfig(homeDirectory) {
281
+ if (textValue(process.env.OPENAI_API_KEY) !== null)
282
+ return true;
283
+ const config = readText(join(homeDirectory, 'config.toml'));
284
+ if (config === null)
285
+ return false;
286
+ const baseUrls = [...config.matchAll(/^\s*base_url\s*=\s*["']([^"']+)["']/gmu)].map((match) => match[1] ?? '');
287
+ return baseUrls.some((value) => !isOfficialOpenAiUrl(value));
288
+ }
289
+ function hasThirdPartyClaudeConfig(homeDirectory) {
290
+ const baseUrl = textValue(process.env.ANTHROPIC_BASE_URL);
291
+ if (baseUrl !== null && !isOfficialAnthropicUrl(baseUrl))
292
+ return true;
293
+ if (textValue(process.env.ANTHROPIC_API_KEY) !== null || textValue(process.env.ANTHROPIC_AUTH_TOKEN) !== null)
294
+ return true;
295
+ const settings = readJson(join(homeDirectory, 'settings.json'));
296
+ const env = recordValue(settings)?.env;
297
+ const configuredBaseUrl = textValue(recordValue(env)?.ANTHROPIC_BASE_URL);
298
+ if (configuredBaseUrl !== null && !isOfficialAnthropicUrl(configuredBaseUrl))
299
+ return true;
300
+ return textValue(recordValue(env)?.ANTHROPIC_API_KEY) !== null || textValue(recordValue(env)?.ANTHROPIC_AUTH_TOKEN) !== null;
301
+ }
302
+ function isOfficialOpenAiUrl(value) {
303
+ try {
304
+ return new URL(value).hostname === 'api.openai.com';
305
+ }
306
+ catch {
307
+ return false;
308
+ }
309
+ }
310
+ function isOfficialAnthropicUrl(value) {
311
+ try {
312
+ return new URL(value).hostname === 'api.anthropic.com';
313
+ }
314
+ catch {
315
+ return false;
316
+ }
317
+ }
318
+ function readText(path) {
319
+ if (!existsSync(path))
320
+ return null;
321
+ try {
322
+ return readFileSync(path, 'utf8');
323
+ }
324
+ catch {
325
+ return null;
326
+ }
327
+ }
328
+ function readJson(path) {
329
+ if (!existsSync(path))
330
+ return null;
331
+ try {
332
+ return recordValue(JSON.parse(readFileSync(path, 'utf8')));
333
+ }
334
+ catch {
335
+ return null;
336
+ }
337
+ }
338
+ function recordValue(value) { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null; }
339
+ function textValue(value) { return typeof value === 'string' && value.trim() ? value.trim() : null; }
340
+ function numberValue(value) { const result = typeof value === 'number' ? value : Number(value); return Number.isFinite(result) ? result : null; }
341
+ function integerValue(value) { const result = numberValue(value); return result === null ? null : Math.round(result); }
342
+ function timestampValue(value) {
343
+ const numeric = numberValue(value);
344
+ if (numeric !== null)
345
+ return numeric > 10_000_000_000 ? Math.round(numeric / 1000) : Math.round(numeric);
346
+ if (typeof value === 'string') {
347
+ const parsed = Date.parse(value);
348
+ return Number.isFinite(parsed) ? Math.round(parsed / 1000) : null;
349
+ }
350
+ return null;
351
+ }
352
+ function clamp(value) { return Math.max(0, Math.min(100, value)); }
353
+ function resolveSub2ApiSources(adapterId) {
354
+ const sources = [];
355
+ const add = (source) => {
356
+ if (source === null || source.baseUrl.trim() === '' || source.apiKey.trim() === '')
357
+ return;
358
+ if (!sources.some((item) => item.baseUrl === source.baseUrl && item.apiKey === source.apiKey))
359
+ sources.push(source);
360
+ };
361
+ if (adapterId === 'codex') {
362
+ const home = process.env.CODEX_HOME ?? join(homedir(), '.codex');
363
+ const config = readText(join(home, 'config.toml')) ?? '';
364
+ const baseUrl = config.match(/^\s*base_url\s*=\s*["']([^"']+)["']/mu)?.[1];
365
+ const key = textValue(readJsonValue(join(home, 'auth.json'), 'OPENAI_API_KEY'));
366
+ add(baseUrl === undefined || key === null ? null : { baseUrl, apiKey: key });
367
+ }
368
+ if (adapterId === 'claude-code') {
369
+ const home = process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude');
370
+ const settings = readJson(join(home, 'settings.json'));
371
+ const env = recordValue(settings)?.env;
372
+ add({
373
+ baseUrl: textValue(process.env.ANTHROPIC_BASE_URL) ?? textValue(recordValue(env)?.ANTHROPIC_BASE_URL) ?? '',
374
+ apiKey: textValue(process.env.ANTHROPIC_AUTH_TOKEN) ?? textValue(process.env.ANTHROPIC_API_KEY) ?? textValue(recordValue(env)?.ANTHROPIC_AUTH_TOKEN) ?? textValue(recordValue(env)?.ANTHROPIC_API_KEY) ?? '',
375
+ });
376
+ }
377
+ if (adapterId === 'opencode') {
378
+ const configCandidates = [
379
+ join(homedir(), '.config', 'opencode', 'opencode.json'),
380
+ ...(process.platform === 'win32' && process.env.APPDATA
381
+ ? [join(process.env.APPDATA, 'opencode', 'opencode.json')]
382
+ : []),
383
+ ];
384
+ const config = configCandidates.map(readJson).find((value) => value !== null) ?? null;
385
+ add(findConfigSource(config));
386
+ }
387
+ if (adapterId === 'dsh') {
388
+ add(envSource('DSH_BASE_URL', 'DSH_API_KEY'));
389
+ add(envSource('DEEPSEEK_BASE_URL', 'DEEPSEEK_API_KEY'));
390
+ add(envSource('OPENAI_BASE_URL', 'OPENAI_API_KEY'));
391
+ add(findConfigSource(readJson(join(homedir(), '.dsh', 'config.json'))));
392
+ }
393
+ if (adapterId === 'grok') {
394
+ add(envSource('GROK_BASE_URL', 'GROK_API_KEY'));
395
+ add(envSource('XAI_BASE_URL', 'XAI_API_KEY'));
396
+ add(envSource('XAI_API_BASE_URL', 'XAI_API_KEY'));
397
+ add(findConfigSource(readJson(join(homedir(), '.grok', 'config.json'))));
398
+ }
399
+ return sources;
400
+ }
401
+ function defaultOpenCodeDataDirectory() {
402
+ if (process.platform === 'win32')
403
+ return join(process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming'), 'opencode');
404
+ return join(homedir(), '.local', 'share', 'opencode');
405
+ }
406
+ function envSource(baseName, keyName) {
407
+ const baseUrl = textValue(process.env[baseName]);
408
+ const apiKey = textValue(process.env[keyName]);
409
+ return baseUrl === null || apiKey === null ? null : { baseUrl, apiKey };
410
+ }
411
+ function findConfigSource(value, depth = 0) {
412
+ if (depth > 6 || !value || typeof value !== 'object' || Array.isArray(value))
413
+ return null;
414
+ const record = value;
415
+ const baseUrl = textValue(record.baseURL ?? record.baseUrl ?? record.base_url);
416
+ const apiKey = textValue(record.apiKey ?? record.api_key ?? record.key);
417
+ if (baseUrl !== null && apiKey !== null)
418
+ return { baseUrl, apiKey };
419
+ for (const child of Object.values(record)) {
420
+ const source = findConfigSource(child, depth + 1);
421
+ if (source !== null)
422
+ return source;
423
+ }
424
+ return null;
425
+ }
426
+ function normalizeSub2ApiUsage(value, baseUrl) {
427
+ const root = recordValue(value);
428
+ if (root === null)
429
+ return null;
430
+ const usage = recordValue(root.usage) ?? {};
431
+ const daily = Array.isArray(root.daily_usage) ? root.daily_usage.flatMap((item) => normalizeDailyPoint(item)) : [];
432
+ const latestDaily = daily.at(-1);
433
+ const today = normalizePoint(usage.today) ?? latestDaily;
434
+ const total = normalizePoint(usage.total);
435
+ const balance = numberValue(root.balance) ?? numberValue(root.remaining);
436
+ if (balance === null || today === undefined || today === null || total === undefined || total === null)
437
+ return null;
438
+ const models = Array.isArray(root.model_stats) ? root.model_stats.flatMap((item) => normalizeModelPoint(item)) : [];
439
+ return {
440
+ upstreamType: detectUpstreamType(root, baseUrl),
441
+ upstreamUrl: sanitizeUpstreamUrl(baseUrl),
442
+ logoDataUrl: '',
443
+ logoUrl: '',
444
+ balance,
445
+ remaining: numberValue(root.remaining) ?? balance,
446
+ unit: textValue(root.unit) ?? 'USD',
447
+ planName: textValue(root.planName ?? root.plan_name),
448
+ mode: textValue(root.mode),
449
+ today,
450
+ total,
451
+ daily,
452
+ models,
453
+ rpm: numberValue(usage.rpm),
454
+ tpm: numberValue(usage.tpm),
455
+ averageDurationMs: numberValue(usage.average_duration_ms),
456
+ };
457
+ }
458
+ function detectUpstreamType(root, baseUrl) {
459
+ const provider = `${textValue(root.provider) ?? ''} ${textValue(root.source) ?? ''} ${textValue(root.platform) ?? ''}`.toLowerCase();
460
+ if (provider.includes('one-api') || provider.includes('oneapi'))
461
+ return 'OneAPI';
462
+ if (provider.includes('sub2api') || Array.isArray(root.daily_usage) || Array.isArray(root.model_stats))
463
+ return 'Sub2API';
464
+ try {
465
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
466
+ if (hostname.includes('oneapi'))
467
+ return 'OneAPI';
468
+ if (hostname.includes('sub2api'))
469
+ return 'Sub2API';
470
+ }
471
+ catch {
472
+ // 非标准地址仍然可以展示统计,只标记为其他上游。
473
+ }
474
+ return '其他';
475
+ }
476
+ function sanitizeUpstreamUrl(value) {
477
+ try {
478
+ const url = new URL(value);
479
+ url.username = '';
480
+ url.password = '';
481
+ url.search = '';
482
+ url.hash = '';
483
+ return url.toString().replace(/\/$/u, '');
484
+ }
485
+ catch {
486
+ return '';
487
+ }
488
+ }
489
+ function buildLogoUrl(baseUrl) {
490
+ try {
491
+ return new URL('/logo.svg', baseUrl).toString();
492
+ }
493
+ catch {
494
+ return '';
495
+ }
496
+ }
497
+ /** 获取小型公开图标并转为 CSP 允许的 data URL;图标失败不影响订阅数据。 */
498
+ async function readLogoDataUrl(url, request, timeoutMs) {
499
+ let parsed;
500
+ try {
501
+ parsed = new URL(url);
502
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
503
+ return '';
504
+ }
505
+ catch {
506
+ return '';
507
+ }
508
+ const controller = new AbortController();
509
+ const timer = setTimeout(() => controller.abort(), Math.min(timeoutMs, 3_000));
510
+ try {
511
+ const response = await request(parsed.toString(), { headers: { Accept: 'image/*' }, signal: controller.signal });
512
+ if (!response.ok)
513
+ return '';
514
+ const contentType = (response.headers.get('content-type') ?? '').split(';', 1)[0]?.trim().toLowerCase() ?? '';
515
+ if (!isSupportedLogoContentType(contentType))
516
+ return '';
517
+ if (response.url !== '' && new URL(response.url).origin !== parsed.origin)
518
+ return '';
519
+ const bytes = await readResponseBytes(response, 64 * 1024);
520
+ if (bytes === null || (contentType === 'image/svg+xml' && !isSafeSvg(bytes)))
521
+ return '';
522
+ return `data:${contentType};base64,${Buffer.from(bytes).toString('base64')}`;
523
+ }
524
+ catch {
525
+ return '';
526
+ }
527
+ finally {
528
+ clearTimeout(timer);
529
+ }
530
+ }
531
+ function isSupportedLogoContentType(value) {
532
+ return value === 'image/svg+xml' || value === 'image/png' || value === 'image/jpeg' || value === 'image/gif' || value === 'image/webp' || value === 'image/avif' || value === 'image/x-icon' || value === 'image/vnd.microsoft.icon';
533
+ }
534
+ async function readResponseBytes(response, maxBytes) {
535
+ const declaredLength = Number(response.headers.get('content-length'));
536
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes)
537
+ return null;
538
+ if (response.body === null) {
539
+ const bytes = new Uint8Array(await response.arrayBuffer());
540
+ return bytes.byteLength > maxBytes ? null : bytes;
541
+ }
542
+ const reader = response.body.getReader();
543
+ const chunks = [];
544
+ let total = 0;
545
+ try {
546
+ while (true) {
547
+ const next = await reader.read();
548
+ if (next.done)
549
+ break;
550
+ total += next.value.byteLength;
551
+ if (total > maxBytes) {
552
+ await reader.cancel();
553
+ return null;
554
+ }
555
+ chunks.push(next.value);
556
+ }
557
+ }
558
+ finally {
559
+ reader.releaseLock();
560
+ }
561
+ const bytes = new Uint8Array(total);
562
+ let offset = 0;
563
+ for (const chunk of chunks) {
564
+ bytes.set(chunk, offset);
565
+ offset += chunk.byteLength;
566
+ }
567
+ return bytes;
568
+ }
569
+ function isSafeSvg(bytes) {
570
+ const source = new TextDecoder().decode(bytes).toLowerCase();
571
+ return !/<\/?script\b|<\/?foreignobject\b|\bon[a-z]+\s*=|(?:href|xlink:href)\s*=\s*["']https?:|url\(\s*https?:/u.test(source);
572
+ }
573
+ function normalizePoint(value) {
574
+ const source = recordValue(value);
575
+ if (source === null)
576
+ return null;
577
+ const inputTokens = nonNegative(source.input_tokens);
578
+ const outputTokens = nonNegative(source.output_tokens);
579
+ const cacheCreationTokens = nonNegative(source.cache_creation_tokens ?? source.cache_write_tokens);
580
+ const cacheReadTokens = nonNegative(source.cache_read_tokens);
581
+ const totalTokens = nonNegative(source.total_tokens) || inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
582
+ const requests = nonNegative(source.requests);
583
+ const cost = numberValue(source.cost) ?? numberValue(source.actual_cost) ?? 0;
584
+ const actualCost = numberValue(source.actual_cost) ?? cost;
585
+ return { requests, inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens, totalTokens, cost, actualCost, cacheHitRate: calculateCacheHitRate(inputTokens, cacheReadTokens) };
586
+ }
587
+ function normalizeDailyPoint(value) {
588
+ const source = recordValue(value);
589
+ const point = normalizePoint(source);
590
+ const date = textValue(source?.date);
591
+ return point === null || date === null ? [] : [{ date, ...point }];
592
+ }
593
+ function normalizeModelPoint(value) {
594
+ const source = recordValue(value);
595
+ const point = normalizePoint(source);
596
+ const model = textValue(source?.model);
597
+ return point === null || model === null ? [] : [{ model, accountCost: numberValue(source?.account_cost) ?? point.actualCost, ...point }];
598
+ }
599
+ function calculateCacheHitRate(inputTokens, cacheReadTokens) {
600
+ const denominator = inputTokens + cacheReadTokens;
601
+ return denominator === 0 ? 0 : Number((cacheReadTokens / denominator * 100).toFixed(4));
602
+ }
603
+ function nonNegative(value) {
604
+ const result = numberValue(value);
605
+ return result === null ? 0 : Math.max(0, result);
606
+ }
607
+ function readJsonValue(path, key) {
608
+ return readJson(path)?.[key];
609
+ }
610
+ //# sourceMappingURL=provider-subscription.js.map