@ai-devkit/agent-manager 0.37.0 → 0.38.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 (295) hide show
  1. package/dist/AgentManager.d.ts +0 -4
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +2 -2
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/capacity/anthropic.test.js +115 -0
  6. package/dist/__tests__/capacity/anthropic.test.js.map +1 -0
  7. package/dist/__tests__/capacity/boundaries.test.js +45 -0
  8. package/dist/__tests__/capacity/boundaries.test.js.map +1 -0
  9. package/dist/__tests__/capacity/claude.test.js +3 -3
  10. package/dist/__tests__/capacity/claude.test.js.map +1 -1
  11. package/dist/__tests__/capacity/codex.test.js +3 -1
  12. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  13. package/dist/__tests__/capacity/openai.test.js +21 -12
  14. package/dist/__tests__/capacity/openai.test.js.map +1 -1
  15. package/dist/__tests__/capacity/wham.test.js +1 -1
  16. package/dist/__tests__/capacity/wham.test.js.map +1 -1
  17. package/dist/__tests__/capacity/zai.test.js +7 -8
  18. package/dist/__tests__/capacity/zai.test.js.map +1 -1
  19. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js +2 -1
  20. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js.map +1 -1
  21. package/dist/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.js.map +1 -1
  22. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js +7 -15
  23. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js.map +1 -1
  24. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js +3 -1
  25. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js.map +1 -1
  26. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js +11 -26
  27. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js.map +1 -1
  28. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js +3 -7
  29. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js.map +1 -1
  30. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js +5 -11
  31. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
  32. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
  33. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
  34. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
  35. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
  36. package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +31 -10
  37. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
  38. package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
  39. package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
  40. package/dist/__tests__/print/DurableAgent.test.js +4 -6
  41. package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
  42. package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
  43. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
  44. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
  45. package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
  46. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
  47. package/dist/capacity/index.d.ts +9 -19
  48. package/dist/capacity/index.d.ts.map +1 -1
  49. package/dist/capacity/index.js +11 -4
  50. package/dist/capacity/index.js.map +1 -1
  51. package/dist/capacity/providers/anthropic.d.ts +9 -0
  52. package/dist/capacity/providers/anthropic.d.ts.map +1 -0
  53. package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
  54. package/dist/capacity/providers/anthropic.js.map +1 -0
  55. package/dist/capacity/providers/openai-platform.d.ts +14 -0
  56. package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
  57. package/dist/capacity/providers/openai-platform.js +120 -0
  58. package/dist/capacity/providers/openai-platform.js.map +1 -0
  59. package/dist/capacity/providers/openai-subscription.d.ts +20 -0
  60. package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
  61. package/dist/capacity/providers/openai-subscription.js +129 -0
  62. package/dist/capacity/providers/openai-subscription.js.map +1 -0
  63. package/dist/capacity/providers/zai.d.ts +8 -0
  64. package/dist/capacity/providers/zai.d.ts.map +1 -0
  65. package/dist/capacity/{zai.js → providers/zai.js} +17 -62
  66. package/dist/capacity/providers/zai.js.map +1 -0
  67. package/dist/capacity/sources/claude.d.ts +9 -0
  68. package/dist/capacity/sources/claude.d.ts.map +1 -0
  69. package/dist/capacity/sources/claude.js +17 -0
  70. package/dist/capacity/sources/claude.js.map +1 -0
  71. package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
  72. package/dist/capacity/sources/codex.d.ts.map +1 -0
  73. package/dist/capacity/{codex.js → sources/codex.js} +31 -84
  74. package/dist/capacity/sources/codex.js.map +1 -0
  75. package/dist/capacity/sources/pi.d.ts +30 -0
  76. package/dist/capacity/sources/pi.d.ts.map +1 -0
  77. package/dist/capacity/sources/pi.js +81 -0
  78. package/dist/capacity/sources/pi.js.map +1 -0
  79. package/dist/capacity/types.d.ts +2 -0
  80. package/dist/capacity/types.d.ts.map +1 -1
  81. package/dist/capacity/types.js +1 -1
  82. package/dist/capacity/types.js.map +1 -1
  83. package/dist/harnesses/claude/credentials.d.ts +10 -0
  84. package/dist/harnesses/claude/credentials.d.ts.map +1 -0
  85. package/dist/harnesses/claude/credentials.js +112 -0
  86. package/dist/harnesses/claude/credentials.js.map +1 -0
  87. package/dist/harnesses/claude/readiness.d.ts +3 -0
  88. package/dist/harnesses/claude/readiness.d.ts.map +1 -0
  89. package/dist/harnesses/claude/readiness.js +63 -0
  90. package/dist/harnesses/claude/readiness.js.map +1 -0
  91. package/dist/harnesses/codex/credentials.d.ts +15 -0
  92. package/dist/harnesses/codex/credentials.d.ts.map +1 -0
  93. package/dist/harnesses/codex/credentials.js +58 -0
  94. package/dist/harnesses/codex/credentials.js.map +1 -0
  95. package/dist/harnesses/codex/readiness.d.ts +3 -0
  96. package/dist/harnesses/codex/readiness.d.ts.map +1 -0
  97. package/dist/harnesses/codex/readiness.js +60 -0
  98. package/dist/harnesses/codex/readiness.js.map +1 -0
  99. package/dist/harnesses/copilot/readiness.d.ts +3 -0
  100. package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
  101. package/dist/harnesses/copilot/readiness.js +39 -0
  102. package/dist/harnesses/copilot/readiness.js.map +1 -0
  103. package/dist/harnesses/opencode/readiness.d.ts +3 -0
  104. package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
  105. package/dist/harnesses/opencode/readiness.js +50 -0
  106. package/dist/harnesses/opencode/readiness.js.map +1 -0
  107. package/dist/harnesses/pi/credentials.d.ts +22 -0
  108. package/dist/harnesses/pi/credentials.d.ts.map +1 -0
  109. package/dist/harnesses/pi/credentials.js +98 -0
  110. package/dist/harnesses/pi/credentials.js.map +1 -0
  111. package/dist/harnesses/pi/readiness.d.ts +3 -0
  112. package/dist/harnesses/pi/readiness.d.ts.map +1 -0
  113. package/dist/harnesses/pi/readiness.js +96 -0
  114. package/dist/harnesses/pi/readiness.js.map +1 -0
  115. package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
  116. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
  117. package/dist/harnesses/readiness/AgentReadiness.js +68 -0
  118. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
  119. package/dist/harnesses/readiness/checks.d.ts +31 -0
  120. package/dist/harnesses/readiness/checks.d.ts.map +1 -0
  121. package/dist/harnesses/readiness/checks.js +270 -0
  122. package/dist/harnesses/readiness/checks.js.map +1 -0
  123. package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
  124. package/dist/harnesses/readiness/types.d.ts.map +1 -0
  125. package/dist/harnesses/readiness/types.js +4 -0
  126. package/dist/harnesses/readiness/types.js.map +1 -0
  127. package/dist/harnesses/runtimeProfiles.d.ts +11 -0
  128. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
  129. package/dist/harnesses/runtimeProfiles.js +99 -0
  130. package/dist/harnesses/runtimeProfiles.js.map +1 -0
  131. package/dist/index.d.ts +16 -42
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +9 -23
  134. package/dist/index.js.map +1 -1
  135. package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
  136. package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
  137. package/dist/runtime/ManagedAgentRuntime.js +6 -20
  138. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  139. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
  140. package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
  141. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  142. package/dist/runtime/tmux/PidPolling.d.ts +1 -1
  143. package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
  144. package/dist/runtime/tmux/PidPolling.js.map +1 -1
  145. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
  146. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
  147. package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
  148. package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
  149. package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
  150. package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
  151. package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
  152. package/dist/runtime/tmux/TmuxManager.js.map +1 -0
  153. package/dist/runtime/types.d.ts +1 -1
  154. package/dist/runtime/types.d.ts.map +1 -1
  155. package/dist/runtime/types.js.map +1 -1
  156. package/dist/terminal/TerminalFocusManager.d.ts +4 -32
  157. package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
  158. package/dist/terminal/TerminalFocusManager.js +17 -267
  159. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  160. package/dist/terminal/TtyWriter.d.ts +1 -44
  161. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  162. package/dist/terminal/TtyWriter.js +11 -347
  163. package/dist/terminal/TtyWriter.js.map +1 -1
  164. package/dist/terminal/backends/index.d.ts +5 -0
  165. package/dist/terminal/backends/index.d.ts.map +1 -0
  166. package/dist/terminal/backends/index.js +19 -0
  167. package/dist/terminal/backends/index.js.map +1 -0
  168. package/dist/terminal/backends/iterm2.d.ts +3 -0
  169. package/dist/terminal/backends/iterm2.d.ts.map +1 -0
  170. package/dist/terminal/backends/iterm2.js +135 -0
  171. package/dist/terminal/backends/iterm2.js.map +1 -0
  172. package/dist/terminal/backends/shared.d.ts +7 -0
  173. package/dist/terminal/backends/shared.d.ts.map +1 -0
  174. package/dist/terminal/backends/shared.js +35 -0
  175. package/dist/terminal/backends/shared.js.map +1 -0
  176. package/dist/terminal/backends/terminal-app.d.ts +3 -0
  177. package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
  178. package/dist/terminal/backends/terminal-app.js +129 -0
  179. package/dist/terminal/backends/terminal-app.js.map +1 -0
  180. package/dist/terminal/backends/tmux.d.ts +3 -0
  181. package/dist/terminal/backends/tmux.d.ts.map +1 -0
  182. package/dist/terminal/backends/tmux.js +76 -0
  183. package/dist/terminal/backends/tmux.js.map +1 -0
  184. package/dist/terminal/backends/wezterm.d.ts +3 -0
  185. package/dist/terminal/backends/wezterm.d.ts.map +1 -0
  186. package/dist/terminal/backends/wezterm.js +71 -0
  187. package/dist/terminal/backends/wezterm.js.map +1 -0
  188. package/dist/terminal/types.d.ts +21 -0
  189. package/dist/terminal/types.d.ts.map +1 -0
  190. package/dist/terminal/types.js +10 -0
  191. package/dist/terminal/types.js.map +1 -0
  192. package/package.json +1 -1
  193. package/src/AgentManager.ts +1 -1
  194. package/src/__tests__/capacity/anthropic.test.ts +105 -0
  195. package/src/__tests__/capacity/boundaries.test.ts +36 -0
  196. package/src/__tests__/capacity/claude.test.ts +3 -8
  197. package/src/__tests__/capacity/codex.test.ts +12 -35
  198. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
  199. package/src/__tests__/capacity/openai.test.ts +15 -15
  200. package/src/__tests__/capacity/wham.test.ts +1 -1
  201. package/src/__tests__/capacity/zai.test.ts +27 -62
  202. package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
  203. package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
  204. package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
  205. package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
  206. package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
  207. package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
  208. package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
  209. package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
  210. package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
  211. package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +36 -9
  212. package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
  213. package/src/__tests__/print/DurableAgent.test.ts +4 -13
  214. package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
  215. package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
  216. package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
  217. package/src/capacity/index.ts +29 -23
  218. package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
  219. package/src/capacity/providers/openai-platform.ts +150 -0
  220. package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
  221. package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
  222. package/src/capacity/sources/claude.ts +30 -0
  223. package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
  224. package/src/capacity/sources/pi.ts +131 -0
  225. package/src/capacity/types.ts +3 -0
  226. package/src/harnesses/claude/credentials.ts +122 -0
  227. package/src/harnesses/claude/readiness.ts +69 -0
  228. package/src/harnesses/codex/credentials.ts +81 -0
  229. package/src/harnesses/codex/readiness.ts +72 -0
  230. package/src/harnesses/copilot/readiness.ts +33 -0
  231. package/src/harnesses/opencode/readiness.ts +48 -0
  232. package/src/harnesses/pi/credentials.ts +129 -0
  233. package/src/harnesses/pi/readiness.ts +104 -0
  234. package/src/harnesses/readiness/AgentReadiness.ts +87 -0
  235. package/src/harnesses/readiness/checks.ts +336 -0
  236. package/src/harnesses/readiness/types.ts +90 -0
  237. package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +34 -38
  238. package/src/index.ts +15 -128
  239. package/src/runtime/ManagedAgentRuntime.ts +7 -22
  240. package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
  241. package/src/runtime/tmux/PidPolling.ts +1 -1
  242. package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
  243. package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
  244. package/src/runtime/types.ts +1 -1
  245. package/src/terminal/TerminalFocusManager.ts +19 -292
  246. package/src/terminal/TtyWriter.ts +16 -336
  247. package/src/terminal/backends/index.ts +21 -0
  248. package/src/terminal/backends/iterm2.ts +126 -0
  249. package/src/terminal/backends/shared.ts +42 -0
  250. package/src/terminal/backends/terminal-app.ts +122 -0
  251. package/src/terminal/backends/tmux.ts +62 -0
  252. package/src/terminal/backends/wezterm.ts +65 -0
  253. package/src/terminal/types.ts +22 -0
  254. package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
  255. package/dist/__tests__/utils/agents.test.js +0 -93
  256. package/dist/__tests__/utils/agents.test.js.map +0 -1
  257. package/dist/capacity/claude.d.ts +0 -17
  258. package/dist/capacity/claude.d.ts.map +0 -1
  259. package/dist/capacity/claude.js.map +0 -1
  260. package/dist/capacity/codex.d.ts.map +0 -1
  261. package/dist/capacity/codex.js.map +0 -1
  262. package/dist/capacity/openai.d.ts +0 -47
  263. package/dist/capacity/openai.d.ts.map +0 -1
  264. package/dist/capacity/openai.js +0 -307
  265. package/dist/capacity/openai.js.map +0 -1
  266. package/dist/capacity/wham.d.ts +0 -16
  267. package/dist/capacity/wham.d.ts.map +0 -1
  268. package/dist/capacity/wham.js +0 -66
  269. package/dist/capacity/wham.js.map +0 -1
  270. package/dist/capacity/zai.d.ts +0 -14
  271. package/dist/capacity/zai.d.ts.map +0 -1
  272. package/dist/capacity/zai.js.map +0 -1
  273. package/dist/readiness/AgentReadiness.d.ts.map +0 -1
  274. package/dist/readiness/AgentReadiness.js +0 -614
  275. package/dist/readiness/AgentReadiness.js.map +0 -1
  276. package/dist/terminal/TmuxManager.d.ts +0 -28
  277. package/dist/terminal/TmuxManager.d.ts.map +0 -1
  278. package/dist/terminal/TmuxManager.js.map +0 -1
  279. package/dist/terminal/index.d.ts +0 -5
  280. package/dist/terminal/index.d.ts.map +0 -1
  281. package/dist/terminal/index.js +0 -5
  282. package/dist/terminal/index.js.map +0 -1
  283. package/dist/utils/agents.d.ts +0 -16
  284. package/dist/utils/agents.d.ts.map +0 -1
  285. package/dist/utils/agents.js +0 -117
  286. package/dist/utils/agents.js.map +0 -1
  287. package/dist/utils/index.d.ts +0 -8
  288. package/dist/utils/index.d.ts.map +0 -1
  289. package/dist/utils/index.js +0 -6
  290. package/dist/utils/index.js.map +0 -1
  291. package/src/__tests__/utils/agents.test.ts +0 -105
  292. package/src/capacity/openai.ts +0 -355
  293. package/src/readiness/AgentReadiness.ts +0 -730
  294. package/src/terminal/index.ts +0 -4
  295. package/src/utils/index.ts +0 -14
@@ -1,13 +1,6 @@
1
- import { execFile } from "node:child_process";
2
- import { readFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { isAbsolute, join, resolve } from "node:path";
5
- import { promisify } from "node:util";
6
- const execFileAsync = promisify(execFile);
7
- const CLAUDE_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
8
- const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
9
1
  const SESSION_DURATION_MINUTES = 5 * 60;
10
2
  const WEEK_DURATION_MINUTES = 7 * 24 * 60;
3
+ const ANTHROPIC_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
11
4
  function record(value) {
12
5
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
13
6
  }
@@ -17,103 +10,6 @@ function percent(value) {
17
10
  function nonEmptyText(value) {
18
11
  return typeof value === "string" && value.trim() ? value.trim() : null;
19
12
  }
20
- function credentialsPath(options) {
21
- const env = options.env ?? process.env;
22
- const configured = nonEmptyText(env.CLAUDE_CONFIG_DIR);
23
- if (configured) {
24
- const profileRoot = isAbsolute(configured) ? configured : resolve(options.cwd ?? process.cwd(), configured);
25
- return join(profileRoot, ".credentials.json");
26
- }
27
- return join(env.HOME || homedir(), ".claude", ".credentials.json");
28
- }
29
- function credentialFromPayload(payload, now) {
30
- if (!payload) return {
31
- token: null,
32
- expired: false
33
- };
34
- try {
35
- return credentialFromRoot(record(JSON.parse(payload)), now);
36
- } catch {
37
- return {
38
- token: null,
39
- expired: false
40
- };
41
- }
42
- }
43
- async function readProfileCredential(options, now) {
44
- try {
45
- const payload = await (options.readFile ?? readFile)(credentialsPath(options), "utf8");
46
- return credentialFromPayload(payload, now);
47
- } catch {
48
- return {
49
- token: null,
50
- expired: false
51
- };
52
- }
53
- }
54
- async function readKeychainCredential(options, now) {
55
- const env = options.env ?? process.env;
56
- const usesDefaultProfile = !nonEmptyText(env.CLAUDE_CONFIG_DIR);
57
- if (!usesDefaultProfile || (options.platform ?? process.platform) !== "darwin") {
58
- return {
59
- token: null,
60
- expired: false
61
- };
62
- }
63
- try {
64
- const payload = await (options.keychainRead ?? readClaudeKeychain)(CLAUDE_KEYCHAIN_SERVICE);
65
- return credentialFromPayload(payload, now);
66
- } catch {
67
- return {
68
- token: null,
69
- expired: false
70
- };
71
- }
72
- }
73
- async function resolveToken(options) {
74
- const env = options.env ?? process.env;
75
- const environmentToken = nonEmptyText(env.CLAUDE_CODE_OAUTH_TOKEN);
76
- if (environmentToken) return environmentToken;
77
- const now = options.now?.() ?? new Date();
78
- const profileCredential = await readProfileCredential(options, now);
79
- if (profileCredential.token) return profileCredential.token;
80
- const keychainCredential = await readKeychainCredential(options, now);
81
- if (keychainCredential.token) return keychainCredential.token;
82
- if (profileCredential.expired || keychainCredential.expired) {
83
- throw new Error("Claude OAuth credentials expired");
84
- }
85
- throw new Error("Claude OAuth credentials not found or malformed");
86
- }
87
- function credentialFromRoot(root, now) {
88
- const oauth = record(root?.claudeAiOauth);
89
- const token = nonEmptyText(oauth?.accessToken);
90
- if (!token) return {
91
- token: null,
92
- expired: false
93
- };
94
- const expired = typeof oauth?.expiresAt === "number" && Number.isFinite(oauth.expiresAt) && oauth.expiresAt <= now.getTime();
95
- return {
96
- token: expired ? null : token,
97
- expired
98
- };
99
- }
100
- async function readClaudeKeychain(service) {
101
- try {
102
- const { stdout } = await execFileAsync("/usr/bin/security", [
103
- "find-generic-password",
104
- "-s",
105
- service,
106
- "-w"
107
- ], {
108
- encoding: "utf8",
109
- timeout: 1500,
110
- maxBuffer: 1024 * 1024
111
- });
112
- return nonEmptyText(stdout);
113
- } catch {
114
- return null;
115
- }
116
- }
117
13
  function resetTime(value) {
118
14
  if (typeof value !== "string") return null;
119
15
  const milliseconds = Date.parse(value);
@@ -146,9 +42,7 @@ function scopedWindows(value) {
146
42
  continue;
147
43
  }
148
44
  const modelSlug = slug(identity);
149
- if (!modelSlug || modelSlug === "all-models" || modelSlug.endsWith("-all-models")) {
150
- continue;
151
- }
45
+ if (!modelSlug || modelSlug === "all-models" || modelSlug.endsWith("-all-models")) continue;
152
46
  const id = `claude:weekly:${modelSlug}`;
153
47
  if (seen.has(id)) continue;
154
48
  seen.add(id);
@@ -203,7 +97,7 @@ function retryAfterTime(response, now) {
203
97
  const milliseconds = Date.parse(raw);
204
98
  return Number.isNaN(milliseconds) ? null : new Date(milliseconds).toISOString();
205
99
  }
206
- export function parseClaudeUsage(raw, checkedAt) {
100
+ export function parseAnthropicCapacity(raw) {
207
101
  const input = record(raw);
208
102
  if (!input) throw new Error("Claude usage response is malformed");
209
103
  const windows = [
@@ -215,22 +109,16 @@ export function parseClaudeUsage(raw, checkedAt) {
215
109
  extraUsageWindow(input.extra_usage)
216
110
  ].filter((window)=>window !== null);
217
111
  return {
218
- harness: "claude",
219
- provider: "anthropic",
220
- generatedAt: checkedAt,
221
112
  authenticated: true,
222
113
  available: availability(windows),
223
114
  windows,
224
115
  creditsRemaining: null
225
116
  };
226
117
  }
227
- export async function probeClaudeCapacity(options) {
228
- const token = await resolveToken(options);
229
- const controller = new AbortController();
230
- const timer = setTimeout(()=>controller.abort(), options.timeoutMs ?? 5000);
118
+ export async function fetchAnthropicCapacity(token, options = {}) {
231
119
  let response;
232
120
  try {
233
- response = await (options.fetch ?? globalThis.fetch)(CLAUDE_USAGE_URL, {
121
+ response = await (options.fetch ?? globalThis.fetch)(ANTHROPIC_USAGE_URL, {
234
122
  method: "GET",
235
123
  headers: {
236
124
  Accept: "application/json",
@@ -239,19 +127,13 @@ export async function probeClaudeCapacity(options) {
239
127
  "User-Agent": "claude-code/2.1.0",
240
128
  "anthropic-beta": "oauth-2025-04-20"
241
129
  },
242
- signal: controller.signal
130
+ signal: AbortSignal.timeout(options.timeoutMs ?? 5000)
243
131
  });
244
132
  } catch {
245
133
  throw new Error("Claude OAuth usage request failed");
246
- } finally{
247
- clearTimeout(timer);
248
- }
249
- if (response.status === 401) {
250
- throw new Error("Claude OAuth request unauthorized (HTTP 401)");
251
- }
252
- if (response.status === 403) {
253
- throw new Error("Claude OAuth request forbidden (HTTP 403)");
254
134
  }
135
+ if (response.status === 401) throw new Error("Claude OAuth request unauthorized (HTTP 401)");
136
+ if (response.status === 403) throw new Error("Claude OAuth request forbidden (HTTP 403)");
255
137
  if (response.status === 429) {
256
138
  const retryAt = retryAfterTime(response, options.now?.() ?? new Date());
257
139
  throw new Error(retryAt ? `Claude OAuth usage rate limited until ${retryAt}` : "Claude OAuth usage rate limited (HTTP 429)");
@@ -265,7 +147,7 @@ export async function probeClaudeCapacity(options) {
265
147
  } catch {
266
148
  throw new Error("Claude usage response is malformed");
267
149
  }
268
- return parseClaudeUsage(raw, options.checkedAt);
150
+ return parseAnthropicCapacity(raw);
269
151
  }
270
152
 
271
- //# sourceMappingURL=claude.js.map
153
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/capacity/providers/anthropic.ts"],"sourcesContent":["import type { Availability, CapacityWindow, ProviderCapacitySnapshot } from \"../types.js\";\n\nconst SESSION_DURATION_MINUTES = 5 * 60;\nconst WEEK_DURATION_MINUTES = 7 * 24 * 60;\nconst ANTHROPIC_USAGE_URL = \"https://api.anthropic.com/api/oauth/usage\";\n\nexport type AnthropicCapacityRequestOptions = {\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n now?: () => Date;\n};\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction percent(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 && value <= 100\n ? value\n : null;\n}\n\nfunction nonEmptyText(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction resetTime(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const milliseconds = Date.parse(value);\n return Number.isNaN(milliseconds) ? null : new Date(milliseconds).toISOString();\n}\n\nfunction usageWindow(\n value: unknown,\n id: string,\n label: string,\n durationMinutes: number,\n): CapacityWindow | null {\n const input = record(value);\n if (!input) return null;\n return {\n id,\n label,\n durationMinutes,\n usedPercent: percent(input.utilization),\n resetsAt: resetTime(input.resets_at),\n };\n}\n\nfunction slug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction scopedWindows(value: unknown): CapacityWindow[] {\n if (!Array.isArray(value)) return [];\n const seen = new Set<string>();\n const windows: CapacityWindow[] = [];\n for (const entry of value) {\n const limit = record(entry);\n const model = record(record(limit?.scope)?.model);\n const name = nonEmptyText(model?.display_name);\n const identity = nonEmptyText(model?.id) ?? name;\n if (limit?.kind !== \"weekly_scoped\" || limit.group !== \"weekly\" || !name || !identity) {\n continue;\n }\n const modelSlug = slug(identity);\n if (!modelSlug || modelSlug === \"all-models\" || modelSlug.endsWith(\"-all-models\")) continue;\n const id = `claude:weekly:${modelSlug}`;\n if (seen.has(id)) continue;\n seen.add(id);\n windows.push({\n id,\n label: `${name} weekly`,\n durationMinutes: WEEK_DURATION_MINUTES,\n usedPercent: percent(limit.percent),\n resetsAt: resetTime(limit.resets_at),\n });\n }\n return windows;\n}\n\nfunction finiteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction extraUsageWindow(value: unknown): CapacityWindow | null {\n const extra = record(value);\n if (!extra || extra.is_enabled !== true) return null;\n const limitMinor = finiteNumber(extra.monthly_limit);\n const usedMinor = finiteNumber(extra.used_credits);\n if (limitMinor === null || usedMinor === null) return null;\n const total = limitMinor / 100;\n const current = usedMinor / 100;\n const currency = nonEmptyText(extra.currency)?.toUpperCase() ?? \"USD\";\n return {\n id: \"claude:extra-usage\",\n label: `Extra usage · ${currency}`,\n limitType: \"CREDIT_LIMIT\",\n durationMinutes: null,\n usedPercent:\n percent(extra.utilization) ??\n (total > 0 ? Math.max(0, Math.min(100, (current / total) * 100)) : null),\n resetsAt: null,\n total,\n current,\n remaining: Math.max(0, total - current),\n };\n}\n\nfunction availability(windows: CapacityWindow[]): Availability {\n const known = windows.flatMap((window) =>\n window.usedPercent === null ? [] : [window.usedPercent],\n );\n if (known.length === 0) return \"unknown\";\n return known.some((used) => used < 100) ? \"yes\" : \"no\";\n}\n\nfunction retryAfterTime(response: Response, now: Date): string | null {\n const raw = response.headers.get(\"Retry-After\")?.trim();\n if (!raw) return null;\n const seconds = Number(raw);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return new Date(now.getTime() + seconds * 1000).toISOString();\n }\n const milliseconds = Date.parse(raw);\n return Number.isNaN(milliseconds) ? null : new Date(milliseconds).toISOString();\n}\n\nexport function parseAnthropicCapacity(raw: unknown): ProviderCapacitySnapshot {\n const input = record(raw);\n if (!input) throw new Error(\"Claude usage response is malformed\");\n const windows = [\n usageWindow(input.five_hour, \"session\", \"Session\", SESSION_DURATION_MINUTES),\n usageWindow(input.seven_day, \"weekly\", \"Weekly\", WEEK_DURATION_MINUTES),\n usageWindow(\n input.seven_day_sonnet,\n \"claude:sonnet:weekly\",\n \"Sonnet weekly\",\n WEEK_DURATION_MINUTES,\n ),\n usageWindow(input.seven_day_opus, \"claude:opus:weekly\", \"Opus weekly\", WEEK_DURATION_MINUTES),\n ...scopedWindows(input.limits),\n extraUsageWindow(input.extra_usage),\n ].filter((window): window is CapacityWindow => window !== null);\n return {\n authenticated: true,\n available: availability(windows),\n windows,\n creditsRemaining: null,\n };\n}\n\nexport async function fetchAnthropicCapacity(\n token: string,\n options: AnthropicCapacityRequestOptions = {},\n): Promise<ProviderCapacitySnapshot> {\n let response: Response;\n try {\n response = await (options.fetch ?? globalThis.fetch)(ANTHROPIC_USAGE_URL, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"claude-code/2.1.0\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n },\n signal: AbortSignal.timeout(options.timeoutMs ?? 5000),\n });\n } catch {\n throw new Error(\"Claude OAuth usage request failed\");\n }\n if (response.status === 401) throw new Error(\"Claude OAuth request unauthorized (HTTP 401)\");\n if (response.status === 403) throw new Error(\"Claude OAuth request forbidden (HTTP 403)\");\n if (response.status === 429) {\n const retryAt = retryAfterTime(response, options.now?.() ?? new Date());\n throw new Error(\n retryAt\n ? `Claude OAuth usage rate limited until ${retryAt}`\n : \"Claude OAuth usage rate limited (HTTP 429)\",\n );\n }\n if (!response.ok) {\n throw new Error(`Claude OAuth usage request failed (HTTP ${response.status})`);\n }\n let raw: unknown;\n try {\n raw = await response.json();\n } catch {\n throw new Error(\"Claude usage response is malformed\");\n }\n return parseAnthropicCapacity(raw);\n}\n"],"names":["SESSION_DURATION_MINUTES","WEEK_DURATION_MINUTES","ANTHROPIC_USAGE_URL","record","value","Array","isArray","percent","Number","isFinite","nonEmptyText","trim","resetTime","milliseconds","Date","parse","isNaN","toISOString","usageWindow","id","label","durationMinutes","input","usedPercent","utilization","resetsAt","resets_at","slug","toLowerCase","replace","scopedWindows","seen","Set","windows","entry","limit","model","scope","name","display_name","identity","kind","group","modelSlug","endsWith","has","add","push","finiteNumber","extraUsageWindow","extra","is_enabled","limitMinor","monthly_limit","usedMinor","used_credits","total","current","currency","toUpperCase","limitType","Math","max","min","remaining","availability","known","flatMap","window","length","some","used","retryAfterTime","response","now","raw","headers","get","seconds","getTime","parseAnthropicCapacity","Error","five_hour","seven_day","seven_day_sonnet","seven_day_opus","limits","extra_usage","filter","authenticated","available","creditsRemaining","fetchAnthropicCapacity","token","options","fetch","globalThis","method","Accept","Authorization","signal","AbortSignal","timeout","timeoutMs","status","retryAt","ok","json"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ProviderCapacitySnapshot } from "../types.js";
2
+ export type OpenAiUsageBucket = {
3
+ startMs: number;
4
+ tokens: number;
5
+ };
6
+ export type OpenAiPlatformCapacityOptions = {
7
+ checkedAt: string;
8
+ fetch?: typeof globalThis.fetch;
9
+ timeoutMs?: number;
10
+ };
11
+ export declare function parseOpenAiUsage(raw: unknown): OpenAiUsageBucket[];
12
+ export declare function buildOpenAiPlatformCapacity(buckets: OpenAiUsageBucket[], checkedAt: string): ProviderCapacitySnapshot;
13
+ export declare function fetchOpenAiPlatformCapacity(key: string, options: OpenAiPlatformCapacityOptions): Promise<ProviderCapacitySnapshot>;
14
+ //# sourceMappingURL=openai-platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-platform.d.ts","sourceRoot":"","sources":["../../../src/capacity/providers/openai-platform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAQ5E,MAAM,MAAM,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACpE,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAyBF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAUlE;AAWD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAwC1B;AAuBD,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,wBAAwB,CAAC,CAqBnC"}
@@ -0,0 +1,120 @@
1
+ const OPENAI_USAGE_BASE_URL = "https://api.openai.com/v1/organization/usage";
2
+ const OPENAI_MODELS_URL = "https://api.openai.com/v1/models";
3
+ const OPENAI_USAGE_ENDPOINTS = [
4
+ "completions",
5
+ "responses"
6
+ ];
7
+ const USAGE_WINDOW_DAYS = 7;
8
+ const DAY_MS = 24 * 60 * 60 * 1000;
9
+ function record(value) {
10
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
11
+ }
12
+ function finiteNumber(value) {
13
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
14
+ }
15
+ function bucketTokens(value) {
16
+ const results = Array.isArray(value) ? value : [];
17
+ return results.reduce((total, entry)=>total + (finiteNumber(record(entry)?.input_tokens) ?? 0) + (finiteNumber(record(entry)?.output_tokens) ?? 0), 0);
18
+ }
19
+ export function parseOpenAiUsage(raw) {
20
+ const data = record(raw)?.data;
21
+ if (!Array.isArray(data)) throw new Error("Invalid OpenAI usage response");
22
+ const buckets = [];
23
+ for (const entry of data){
24
+ const seconds = finiteNumber(record(entry)?.aggregation_timestamp);
25
+ if (seconds === null) continue;
26
+ buckets.push({
27
+ startMs: seconds * 1000,
28
+ tokens: bucketTokens(record(entry)?.results)
29
+ });
30
+ }
31
+ return buckets;
32
+ }
33
+ function emptySnapshot(authenticated) {
34
+ return {
35
+ authenticated,
36
+ available: "unknown",
37
+ windows: [],
38
+ creditsRemaining: null
39
+ };
40
+ }
41
+ export function buildOpenAiPlatformCapacity(buckets, checkedAt) {
42
+ const nowMs = Date.parse(checkedAt);
43
+ const todayStartMs = Math.floor(nowMs / DAY_MS) * DAY_MS;
44
+ const weekStartMs = todayStartMs - (USAGE_WINDOW_DAYS - 1) * DAY_MS;
45
+ let todayTokens = 0;
46
+ let weekTokens = 0;
47
+ for (const bucket of buckets){
48
+ if (bucket.startMs === todayStartMs) todayTokens += bucket.tokens;
49
+ if (bucket.startMs >= weekStartMs) weekTokens += bucket.tokens;
50
+ }
51
+ const windows = [
52
+ {
53
+ id: "openai:tokens:today",
54
+ label: "Tokens · today (UTC)",
55
+ limitType: "TOKENS_LIMIT",
56
+ durationMinutes: 24 * 60,
57
+ usedPercent: null,
58
+ resetsAt: new Date(todayStartMs + DAY_MS).toISOString(),
59
+ total: null,
60
+ current: todayTokens,
61
+ remaining: null
62
+ },
63
+ {
64
+ id: "openai:tokens:week",
65
+ label: "Tokens · 7 days",
66
+ limitType: "TOKENS_LIMIT",
67
+ durationMinutes: USAGE_WINDOW_DAYS * 24 * 60,
68
+ usedPercent: null,
69
+ resetsAt: null,
70
+ total: null,
71
+ current: weekTokens,
72
+ remaining: null
73
+ }
74
+ ];
75
+ return {
76
+ authenticated: true,
77
+ available: "yes",
78
+ windows,
79
+ creditsRemaining: null
80
+ };
81
+ }
82
+ async function request(url, key, options) {
83
+ try {
84
+ return await (options.fetch ?? globalThis.fetch)(url, {
85
+ method: "GET",
86
+ headers: {
87
+ Authorization: `Bearer ${key}`
88
+ },
89
+ signal: AbortSignal.timeout(options.timeoutMs ?? 5000)
90
+ });
91
+ } catch {
92
+ throw new Error("OpenAI usage request failed");
93
+ }
94
+ }
95
+ async function verifyKey(key, options) {
96
+ const response = await request(OPENAI_MODELS_URL, key, options);
97
+ return response.status !== 401;
98
+ }
99
+ export async function fetchOpenAiPlatformCapacity(key, options) {
100
+ const endSeconds = Math.floor(Date.parse(options.checkedAt) / 1000);
101
+ const startSeconds = endSeconds - USAGE_WINDOW_DAYS * 24 * 60 * 60;
102
+ const query = `start_time=${startSeconds}&end_time=${endSeconds}`;
103
+ const payloads = [];
104
+ for (const endpoint of OPENAI_USAGE_ENDPOINTS){
105
+ const response = await request(`${OPENAI_USAGE_BASE_URL}/${endpoint}?${query}`, key, options);
106
+ if (response.status === 401) return emptySnapshot(false);
107
+ if (response.status === 403) return emptySnapshot(await verifyKey(key, options));
108
+ if (response.status !== 200) {
109
+ throw new Error(`OpenAI usage request failed: HTTP ${response.status}`);
110
+ }
111
+ try {
112
+ payloads.push(await response.json());
113
+ } catch {
114
+ throw new Error("OpenAI usage response is not valid JSON");
115
+ }
116
+ }
117
+ return buildOpenAiPlatformCapacity(payloads.flatMap(parseOpenAiUsage), options.checkedAt);
118
+ }
119
+
120
+ //# sourceMappingURL=openai-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/capacity/providers/openai-platform.ts"],"sourcesContent":["import type { CapacityWindow, ProviderCapacitySnapshot } from \"../types.js\";\n\nconst OPENAI_USAGE_BASE_URL = \"https://api.openai.com/v1/organization/usage\";\nconst OPENAI_MODELS_URL = \"https://api.openai.com/v1/models\";\nconst OPENAI_USAGE_ENDPOINTS = [\"completions\", \"responses\"] as const;\nconst USAGE_WINDOW_DAYS = 7;\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nexport type OpenAiUsageBucket = { startMs: number; tokens: number };\nexport type OpenAiPlatformCapacityOptions = {\n checkedAt: string;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\ntype UnknownRecord = Record<string, unknown>;\n\nfunction record(value: unknown): UnknownRecord | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as UnknownRecord)\n : null;\n}\n\nfunction finiteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction bucketTokens(value: unknown): number {\n const results = Array.isArray(value) ? value : [];\n return results.reduce(\n (total, entry) =>\n total +\n (finiteNumber(record(entry)?.input_tokens) ?? 0) +\n (finiteNumber(record(entry)?.output_tokens) ?? 0),\n 0,\n );\n}\n\nexport function parseOpenAiUsage(raw: unknown): OpenAiUsageBucket[] {\n const data = record(raw)?.data;\n if (!Array.isArray(data)) throw new Error(\"Invalid OpenAI usage response\");\n const buckets: OpenAiUsageBucket[] = [];\n for (const entry of data) {\n const seconds = finiteNumber(record(entry)?.aggregation_timestamp);\n if (seconds === null) continue;\n buckets.push({ startMs: seconds * 1000, tokens: bucketTokens(record(entry)?.results) });\n }\n return buckets;\n}\n\nfunction emptySnapshot(authenticated: boolean): ProviderCapacitySnapshot {\n return {\n authenticated,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n };\n}\n\nexport function buildOpenAiPlatformCapacity(\n buckets: OpenAiUsageBucket[],\n checkedAt: string,\n): ProviderCapacitySnapshot {\n const nowMs = Date.parse(checkedAt);\n const todayStartMs = Math.floor(nowMs / DAY_MS) * DAY_MS;\n const weekStartMs = todayStartMs - (USAGE_WINDOW_DAYS - 1) * DAY_MS;\n let todayTokens = 0;\n let weekTokens = 0;\n for (const bucket of buckets) {\n if (bucket.startMs === todayStartMs) todayTokens += bucket.tokens;\n if (bucket.startMs >= weekStartMs) weekTokens += bucket.tokens;\n }\n const windows: CapacityWindow[] = [\n {\n id: \"openai:tokens:today\",\n label: \"Tokens · today (UTC)\",\n limitType: \"TOKENS_LIMIT\",\n durationMinutes: 24 * 60,\n usedPercent: null,\n resetsAt: new Date(todayStartMs + DAY_MS).toISOString(),\n total: null,\n current: todayTokens,\n remaining: null,\n },\n {\n id: \"openai:tokens:week\",\n label: \"Tokens · 7 days\",\n limitType: \"TOKENS_LIMIT\",\n durationMinutes: USAGE_WINDOW_DAYS * 24 * 60,\n usedPercent: null,\n resetsAt: null,\n total: null,\n current: weekTokens,\n remaining: null,\n },\n ];\n return {\n authenticated: true,\n available: \"yes\",\n windows,\n creditsRemaining: null,\n };\n}\n\nasync function request(\n url: string,\n key: string,\n options: OpenAiPlatformCapacityOptions,\n): Promise<Response> {\n try {\n return await (options.fetch ?? globalThis.fetch)(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${key}` },\n signal: AbortSignal.timeout(options.timeoutMs ?? 5000),\n });\n } catch {\n throw new Error(\"OpenAI usage request failed\");\n }\n}\n\nasync function verifyKey(key: string, options: OpenAiPlatformCapacityOptions): Promise<boolean> {\n const response = await request(OPENAI_MODELS_URL, key, options);\n return response.status !== 401;\n}\n\nexport async function fetchOpenAiPlatformCapacity(\n key: string,\n options: OpenAiPlatformCapacityOptions,\n): Promise<ProviderCapacitySnapshot> {\n const endSeconds = Math.floor(Date.parse(options.checkedAt) / 1000);\n const startSeconds = endSeconds - USAGE_WINDOW_DAYS * 24 * 60 * 60;\n const query = `start_time=${startSeconds}&end_time=${endSeconds}`;\n const payloads: unknown[] = [];\n\n for (const endpoint of OPENAI_USAGE_ENDPOINTS) {\n const response = await request(`${OPENAI_USAGE_BASE_URL}/${endpoint}?${query}`, key, options);\n if (response.status === 401) return emptySnapshot(false);\n if (response.status === 403) return emptySnapshot(await verifyKey(key, options));\n if (response.status !== 200) {\n throw new Error(`OpenAI usage request failed: HTTP ${response.status}`);\n }\n try {\n payloads.push(await response.json());\n } catch {\n throw new Error(\"OpenAI usage response is not valid JSON\");\n }\n }\n\n return buildOpenAiPlatformCapacity(payloads.flatMap(parseOpenAiUsage), options.checkedAt);\n}\n"],"names":["OPENAI_USAGE_BASE_URL","OPENAI_MODELS_URL","OPENAI_USAGE_ENDPOINTS","USAGE_WINDOW_DAYS","DAY_MS","record","value","Array","isArray","finiteNumber","Number","isFinite","bucketTokens","results","reduce","total","entry","input_tokens","output_tokens","parseOpenAiUsage","raw","data","Error","buckets","seconds","aggregation_timestamp","push","startMs","tokens","emptySnapshot","authenticated","available","windows","creditsRemaining","buildOpenAiPlatformCapacity","checkedAt","nowMs","Date","parse","todayStartMs","Math","floor","weekStartMs","todayTokens","weekTokens","bucket","id","label","limitType","durationMinutes","usedPercent","resetsAt","toISOString","current","remaining","request","url","key","options","fetch","globalThis","method","headers","Authorization","signal","AbortSignal","timeout","timeoutMs","verifyKey","response","status","fetchOpenAiPlatformCapacity","endSeconds","startSeconds","query","payloads","endpoint","json","flatMap"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { CapacityWindow, ProviderCapacitySnapshot } from "../types.js";
2
+ export type OpenAiSubscriptionCredential = {
3
+ access: string;
4
+ accountId: string;
5
+ };
6
+ export type OpenAiSubscriptionCapacityOptions = {
7
+ fetch?: typeof globalThis.fetch;
8
+ timeoutMs?: number;
9
+ };
10
+ export type WhamUsageSnapshot = {
11
+ windows: CapacityWindow[];
12
+ creditsRemaining: number | null;
13
+ };
14
+ export declare function resetTime(value: unknown): string | null;
15
+ export declare function safeIdentifier(value: unknown): string | null;
16
+ export declare function toRateWindow(value: unknown, id: string, label: string): CapacityWindow | null;
17
+ export declare function parseWhamUsage(raw: unknown): WhamUsageSnapshot;
18
+ export declare function fetchOpenAiSubscriptionCapacity(credential: OpenAiSubscriptionCredential, options?: OpenAiSubscriptionCapacityOptions): Promise<ProviderCapacitySnapshot>;
19
+ export declare function fetchOpenAiPatAccountId(token: string, options?: OpenAiSubscriptionCapacityOptions): Promise<string>;
20
+ //# sourceMappingURL=openai-subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-subscription.d.ts","sourceRoot":"","sources":["../../../src/capacity/providers/openai-subscription.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAK5E,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAkBF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAOvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAY7F;AAgBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAY9D;AAED,wBAAsB,+BAA+B,CACnD,UAAU,EAAE,4BAA4B,EACxC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,wBAAwB,CAAC,CAuCnC;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
@@ -0,0 +1,129 @@
1
+ const WHAM_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
2
+ const OPENAI_WHOAMI_URL = "https://auth.openai.com/api/accounts/v1/user-auth-credential/whoami";
3
+ function record(value) {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
5
+ }
6
+ function finiteNumber(value) {
7
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
8
+ }
9
+ function nonEmptyText(value) {
10
+ return typeof value === "string" && value.length > 0 ? value : null;
11
+ }
12
+ export function resetTime(value) {
13
+ const seconds = finiteNumber(value);
14
+ if (seconds !== null) return new Date(seconds * 1000).toISOString();
15
+ if (typeof value === "string" && !Number.isNaN(Date.parse(value))) {
16
+ return new Date(value).toISOString();
17
+ }
18
+ return null;
19
+ }
20
+ export function safeIdentifier(value) {
21
+ const candidate = nonEmptyText(value);
22
+ if (!candidate || !/^[a-z][a-z0-9_-]{0,63}$/i.test(candidate)) return null;
23
+ if (/(?:account|token|secret|key)[_-]?\d{6,}/i.test(candidate)) return null;
24
+ return candidate;
25
+ }
26
+ export function toRateWindow(value, id, label) {
27
+ const input = record(value);
28
+ if (!input) return null;
29
+ const used = finiteNumber(input.used_percent);
30
+ const seconds = finiteNumber(input.limit_window_seconds);
31
+ return {
32
+ id,
33
+ label,
34
+ durationMinutes: seconds === null ? null : seconds / 60,
35
+ usedPercent: used,
36
+ resetsAt: resetTime(input.reset_at)
37
+ };
38
+ }
39
+ function extraWindows(value) {
40
+ if (!Array.isArray(value)) return [];
41
+ return value.flatMap((entry, index)=>{
42
+ const limit = record(entry);
43
+ if (!limit) return [];
44
+ const scope = safeIdentifier(limit.limit_name) ?? `extra-${index + 1}`;
45
+ const windows = record(limit.rate_limit) ?? limit;
46
+ return [
47
+ toRateWindow(windows.primary_window, `${scope}:primary`, `${scope} primary`),
48
+ toRateWindow(windows.secondary_window, `${scope}:secondary`, `${scope} secondary`)
49
+ ].filter((window)=>window !== null);
50
+ });
51
+ }
52
+ export function parseWhamUsage(raw) {
53
+ const response = record(raw) ?? {};
54
+ const limits = record(response.rate_limit) ?? {};
55
+ const credits = record(response.credits) ?? {};
56
+ return {
57
+ windows: [
58
+ toRateWindow(limits.primary_window, "session", "Session"),
59
+ toRateWindow(limits.secondary_window, "weekly", "Weekly"),
60
+ ...extraWindows(response.additional_rate_limits)
61
+ ].filter((window)=>window !== null),
62
+ creditsRemaining: finiteNumber(credits.balance)
63
+ };
64
+ }
65
+ export async function fetchOpenAiSubscriptionCapacity(credential, options = {}) {
66
+ let response;
67
+ try {
68
+ response = await (options.fetch ?? globalThis.fetch)(WHAM_USAGE_URL, {
69
+ method: "GET",
70
+ headers: {
71
+ Authorization: `Bearer ${credential.access}`,
72
+ "ChatGPT-Account-Id": credential.accountId
73
+ },
74
+ signal: AbortSignal.timeout(options.timeoutMs ?? 5000)
75
+ });
76
+ } catch {
77
+ throw new Error("OpenAI usage request failed");
78
+ }
79
+ if (response.status === 401 || response.status === 403) {
80
+ return {
81
+ authenticated: false,
82
+ available: "unknown",
83
+ windows: [],
84
+ creditsRemaining: null
85
+ };
86
+ }
87
+ if (response.status !== 200) {
88
+ throw new Error(`OpenAI usage request failed: HTTP ${response.status}`);
89
+ }
90
+ let raw;
91
+ try {
92
+ raw = await response.json();
93
+ } catch {
94
+ throw new Error("OpenAI usage response is not valid JSON");
95
+ }
96
+ const snapshot = parseWhamUsage(raw);
97
+ const hasUsage = snapshot.windows.some((window)=>window.usedPercent !== null);
98
+ return {
99
+ authenticated: true,
100
+ available: hasUsage ? "yes" : "unknown",
101
+ windows: snapshot.windows,
102
+ creditsRemaining: snapshot.creditsRemaining
103
+ };
104
+ }
105
+ export async function fetchOpenAiPatAccountId(token, options = {}) {
106
+ let response;
107
+ try {
108
+ response = await (options.fetch ?? globalThis.fetch)(OPENAI_WHOAMI_URL, {
109
+ headers: {
110
+ Authorization: `Bearer ${token}`
111
+ },
112
+ signal: AbortSignal.timeout(options.timeoutMs ?? 5000)
113
+ });
114
+ } catch {
115
+ throw new Error("OpenAI account request failed");
116
+ }
117
+ if (!response.ok) throw new Error("OpenAI account request failed");
118
+ let raw;
119
+ try {
120
+ raw = await response.json();
121
+ } catch {
122
+ throw new Error("OpenAI account response is not valid JSON");
123
+ }
124
+ const accountId = nonEmptyText(record(raw)?.chatgpt_account_id);
125
+ if (!accountId) throw new Error("OpenAI account unavailable");
126
+ return accountId;
127
+ }
128
+
129
+ //# sourceMappingURL=openai-subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/capacity/providers/openai-subscription.ts"],"sourcesContent":["import type { CapacityWindow, ProviderCapacitySnapshot } from \"../types.js\";\n\nconst WHAM_USAGE_URL = \"https://chatgpt.com/backend-api/wham/usage\";\nconst OPENAI_WHOAMI_URL = \"https://auth.openai.com/api/accounts/v1/user-auth-credential/whoami\";\n\nexport type OpenAiSubscriptionCredential = {\n access: string;\n accountId: string;\n};\n\nexport type OpenAiSubscriptionCapacityOptions = {\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nexport type WhamUsageSnapshot = {\n windows: CapacityWindow[];\n creditsRemaining: number | null;\n};\n\ntype UnknownRecord = Record<string, unknown>;\n\nfunction record(value: unknown): UnknownRecord | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as UnknownRecord)\n : null;\n}\n\nfunction finiteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction nonEmptyText(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nexport function resetTime(value: unknown): string | null {\n const seconds = finiteNumber(value);\n if (seconds !== null) return new Date(seconds * 1000).toISOString();\n if (typeof value === \"string\" && !Number.isNaN(Date.parse(value))) {\n return new Date(value).toISOString();\n }\n return null;\n}\n\nexport function safeIdentifier(value: unknown): string | null {\n const candidate = nonEmptyText(value);\n if (!candidate || !/^[a-z][a-z0-9_-]{0,63}$/i.test(candidate)) return null;\n if (/(?:account|token|secret|key)[_-]?\\d{6,}/i.test(candidate)) return null;\n return candidate;\n}\n\nexport function toRateWindow(value: unknown, id: string, label: string): CapacityWindow | null {\n const input = record(value);\n if (!input) return null;\n const used = finiteNumber(input.used_percent);\n const seconds = finiteNumber(input.limit_window_seconds);\n return {\n id,\n label,\n durationMinutes: seconds === null ? null : seconds / 60,\n usedPercent: used,\n resetsAt: resetTime(input.reset_at),\n };\n}\n\nfunction extraWindows(value: unknown): CapacityWindow[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap((entry, index) => {\n const limit = record(entry);\n if (!limit) return [];\n const scope = safeIdentifier(limit.limit_name) ?? `extra-${index + 1}`;\n const windows = record(limit.rate_limit) ?? limit;\n return [\n toRateWindow(windows.primary_window, `${scope}:primary`, `${scope} primary`),\n toRateWindow(windows.secondary_window, `${scope}:secondary`, `${scope} secondary`),\n ].filter((window): window is CapacityWindow => window !== null);\n });\n}\n\nexport function parseWhamUsage(raw: unknown): WhamUsageSnapshot {\n const response = record(raw) ?? {};\n const limits = record(response.rate_limit) ?? {};\n const credits = record(response.credits) ?? {};\n return {\n windows: [\n toRateWindow(limits.primary_window, \"session\", \"Session\"),\n toRateWindow(limits.secondary_window, \"weekly\", \"Weekly\"),\n ...extraWindows(response.additional_rate_limits),\n ].filter((window): window is CapacityWindow => window !== null),\n creditsRemaining: finiteNumber(credits.balance),\n };\n}\n\nexport async function fetchOpenAiSubscriptionCapacity(\n credential: OpenAiSubscriptionCredential,\n options: OpenAiSubscriptionCapacityOptions = {},\n): Promise<ProviderCapacitySnapshot> {\n let response: Response;\n try {\n response = await (options.fetch ?? globalThis.fetch)(WHAM_USAGE_URL, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${credential.access}`,\n \"ChatGPT-Account-Id\": credential.accountId,\n },\n signal: AbortSignal.timeout(options.timeoutMs ?? 5000),\n });\n } catch {\n throw new Error(\"OpenAI usage request failed\");\n }\n if (response.status === 401 || response.status === 403) {\n return {\n authenticated: false,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n };\n }\n if (response.status !== 200) {\n throw new Error(`OpenAI usage request failed: HTTP ${response.status}`);\n }\n let raw: unknown;\n try {\n raw = await response.json();\n } catch {\n throw new Error(\"OpenAI usage response is not valid JSON\");\n }\n const snapshot = parseWhamUsage(raw);\n const hasUsage = snapshot.windows.some((window) => window.usedPercent !== null);\n return {\n authenticated: true,\n available: hasUsage ? \"yes\" : \"unknown\",\n windows: snapshot.windows,\n creditsRemaining: snapshot.creditsRemaining,\n };\n}\n\nexport async function fetchOpenAiPatAccountId(\n token: string,\n options: OpenAiSubscriptionCapacityOptions = {},\n): Promise<string> {\n let response: Response;\n try {\n response = await (options.fetch ?? globalThis.fetch)(OPENAI_WHOAMI_URL, {\n headers: { Authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(options.timeoutMs ?? 5000),\n });\n } catch {\n throw new Error(\"OpenAI account request failed\");\n }\n if (!response.ok) throw new Error(\"OpenAI account request failed\");\n let raw: unknown;\n try {\n raw = await response.json();\n } catch {\n throw new Error(\"OpenAI account response is not valid JSON\");\n }\n const accountId = nonEmptyText(record(raw)?.chatgpt_account_id);\n if (!accountId) throw new Error(\"OpenAI account unavailable\");\n return accountId;\n}\n"],"names":["WHAM_USAGE_URL","OPENAI_WHOAMI_URL","record","value","Array","isArray","finiteNumber","Number","isFinite","nonEmptyText","length","resetTime","seconds","Date","toISOString","isNaN","parse","safeIdentifier","candidate","test","toRateWindow","id","label","input","used","used_percent","limit_window_seconds","durationMinutes","usedPercent","resetsAt","reset_at","extraWindows","flatMap","entry","index","limit","scope","limit_name","windows","rate_limit","primary_window","secondary_window","filter","window","parseWhamUsage","raw","response","limits","credits","additional_rate_limits","creditsRemaining","balance","fetchOpenAiSubscriptionCapacity","credential","options","fetch","globalThis","method","headers","Authorization","access","accountId","signal","AbortSignal","timeout","timeoutMs","Error","status","authenticated","available","json","snapshot","hasUsage","some","fetchOpenAiPatAccountId","token","ok","chatgpt_account_id"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ProviderCapacitySnapshot } from "../types.js";
2
+ export type ZaiRequestOptions = {
3
+ fetch?: typeof globalThis.fetch;
4
+ timeoutMs?: number;
5
+ };
6
+ export declare function parseZaiCapacity(raw: unknown): ProviderCapacitySnapshot;
7
+ export declare function fetchZaiCapacity(key: string, options?: ZaiRequestOptions): Promise<ProviderCapacitySnapshot>;
8
+ //# sourceMappingURL=zai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../../src/capacity/providers/zai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAgBrB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwFF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,wBAAwB,CAevE;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,wBAAwB,CAAC,CAsBnC"}