@ai-devkit/agent-manager 0.34.0 → 0.36.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 (211) hide show
  1. package/dist/AgentManager.d.ts +2 -0
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +25 -0
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +74 -0
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/AntigravityCliAdapter.test.js +381 -0
  8. package/dist/__tests__/adapters/AntigravityCliAdapter.test.js.map +1 -0
  9. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +28 -0
  10. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  11. package/dist/__tests__/adapters/CodexAdapter.test.js +7 -0
  12. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  13. package/dist/__tests__/adapters/KiroAdapter.test.js +607 -0
  14. package/dist/__tests__/adapters/KiroAdapter.test.js.map +1 -0
  15. package/dist/__tests__/capacity/claude.test.js +485 -0
  16. package/dist/__tests__/capacity/claude.test.js.map +1 -0
  17. package/dist/__tests__/capacity/openai.test.js +672 -0
  18. package/dist/__tests__/capacity/openai.test.js.map +1 -0
  19. package/dist/__tests__/capacity/wham.test.js +144 -0
  20. package/dist/__tests__/capacity/wham.test.js.map +1 -0
  21. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js +18 -0
  22. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -1
  23. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js +36 -0
  24. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -1
  25. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js +56 -0
  26. package/dist/__tests__/providers/grok/GrokAgentMapper.test.js.map +1 -0
  27. package/dist/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.js +19 -6
  28. package/dist/__tests__/providers/grok/GrokCliAdapter.test.js.map +1 -0
  29. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js +133 -0
  30. package/dist/__tests__/providers/grok/GrokSessionLocator.test.js.map +1 -0
  31. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +24 -0
  32. package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -1
  33. package/dist/__tests__/providers/pi/PiAdapter.test.js +30 -0
  34. package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -1
  35. package/dist/__tests__/utils/agents.test.js +6 -0
  36. package/dist/__tests__/utils/agents.test.js.map +1 -1
  37. package/dist/__tests__/utils/session.test.js +18 -1
  38. package/dist/__tests__/utils/session.test.js.map +1 -1
  39. package/dist/adapters/AgentAdapter.d.ts +9 -1
  40. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  41. package/dist/adapters/AgentAdapter.js.map +1 -1
  42. package/dist/adapters/AntigravityCliAdapter.d.ts +57 -0
  43. package/dist/adapters/AntigravityCliAdapter.d.ts.map +1 -0
  44. package/dist/adapters/AntigravityCliAdapter.js +273 -0
  45. package/dist/adapters/AntigravityCliAdapter.js.map +1 -0
  46. package/dist/adapters/KiroAdapter.d.ts +46 -0
  47. package/dist/adapters/KiroAdapter.d.ts.map +1 -0
  48. package/dist/adapters/KiroAdapter.js +344 -0
  49. package/dist/adapters/KiroAdapter.js.map +1 -0
  50. package/dist/adapters/index.d.ts +3 -1
  51. package/dist/adapters/index.d.ts.map +1 -1
  52. package/dist/adapters/index.js +3 -1
  53. package/dist/adapters/index.js.map +1 -1
  54. package/dist/capacity/claude.d.ts +17 -0
  55. package/dist/capacity/claude.d.ts.map +1 -0
  56. package/dist/capacity/claude.js +271 -0
  57. package/dist/capacity/claude.js.map +1 -0
  58. package/dist/capacity/codex.d.ts +1 -2
  59. package/dist/capacity/codex.d.ts.map +1 -1
  60. package/dist/capacity/codex.js +5 -47
  61. package/dist/capacity/codex.js.map +1 -1
  62. package/dist/capacity/index.d.ts +11 -0
  63. package/dist/capacity/index.d.ts.map +1 -1
  64. package/dist/capacity/index.js +17 -0
  65. package/dist/capacity/index.js.map +1 -1
  66. package/dist/capacity/openai.d.ts +48 -0
  67. package/dist/capacity/openai.d.ts.map +1 -0
  68. package/dist/capacity/openai.js +312 -0
  69. package/dist/capacity/openai.js.map +1 -0
  70. package/dist/capacity/wham.d.ts +16 -0
  71. package/dist/capacity/wham.d.ts.map +1 -0
  72. package/dist/capacity/wham.js +66 -0
  73. package/dist/capacity/wham.js.map +1 -0
  74. package/dist/index.d.ts +5 -3
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +4 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +2 -0
  79. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
  80. package/dist/providers/claude/ClaudeCodeAdapter.js +22 -19
  81. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
  82. package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -0
  83. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
  84. package/dist/providers/claude/ClaudeSessionLocator.js +18 -1
  85. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
  86. package/dist/providers/codex/CodexAdapter.d.ts +1 -0
  87. package/dist/providers/codex/CodexAdapter.d.ts.map +1 -1
  88. package/dist/providers/codex/CodexAdapter.js +8 -0
  89. package/dist/providers/codex/CodexAdapter.js.map +1 -1
  90. package/dist/providers/copilot/CopilotAdapter.d.ts +2 -0
  91. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -1
  92. package/dist/providers/copilot/CopilotAdapter.js +24 -9
  93. package/dist/providers/copilot/CopilotAdapter.js.map +1 -1
  94. package/dist/providers/copilot/CopilotSessionLocator.d.ts +1 -0
  95. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -1
  96. package/dist/providers/copilot/CopilotSessionLocator.js +9 -1
  97. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -1
  98. package/dist/providers/gemini/GeminiCliAdapter.d.ts +1 -0
  99. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -1
  100. package/dist/providers/gemini/GeminiCliAdapter.js +9 -0
  101. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -1
  102. package/dist/providers/grok/GrokAgentMapper.d.ts +14 -0
  103. package/dist/providers/grok/GrokAgentMapper.d.ts.map +1 -0
  104. package/dist/providers/grok/GrokAgentMapper.js +36 -0
  105. package/dist/providers/grok/GrokAgentMapper.js.map +1 -0
  106. package/dist/providers/grok/GrokCliAdapter.d.ts +36 -0
  107. package/dist/providers/grok/GrokCliAdapter.d.ts.map +1 -0
  108. package/dist/providers/grok/GrokCliAdapter.js +98 -0
  109. package/dist/providers/grok/GrokCliAdapter.js.map +1 -0
  110. package/dist/providers/grok/GrokSessionLocator.d.ts +62 -0
  111. package/dist/providers/grok/GrokSessionLocator.d.ts.map +1 -0
  112. package/dist/providers/grok/GrokSessionLocator.js +140 -0
  113. package/dist/providers/grok/GrokSessionLocator.js.map +1 -0
  114. package/dist/providers/grok/GrokSessionParser.d.ts +54 -0
  115. package/dist/providers/grok/GrokSessionParser.d.ts.map +1 -0
  116. package/dist/providers/grok/GrokSessionParser.js +122 -0
  117. package/dist/providers/grok/GrokSessionParser.js.map +1 -0
  118. package/dist/providers/opencode/OpenCodeAdapter.d.ts +1 -0
  119. package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -1
  120. package/dist/providers/opencode/OpenCodeAdapter.js +3 -0
  121. package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -1
  122. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +2 -0
  123. package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -1
  124. package/dist/providers/opencode/OpenCodeSessionLocator.js +31 -12
  125. package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -1
  126. package/dist/providers/pi/PiAdapter.d.ts +1 -0
  127. package/dist/providers/pi/PiAdapter.d.ts.map +1 -1
  128. package/dist/providers/pi/PiAdapter.js +10 -0
  129. package/dist/providers/pi/PiAdapter.js.map +1 -1
  130. package/dist/providers/pi/PiSessionLocator.d.ts +1 -0
  131. package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -1
  132. package/dist/providers/pi/PiSessionLocator.js +8 -1
  133. package/dist/providers/pi/PiSessionLocator.js.map +1 -1
  134. package/dist/readiness/AgentReadiness.d.ts +1 -1
  135. package/dist/readiness/AgentReadiness.d.ts.map +1 -1
  136. package/dist/readiness/AgentReadiness.js.map +1 -1
  137. package/dist/runtime/ManagedAgentRuntime.js +3 -1
  138. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  139. package/dist/runtime/herdr/HerdrAgentDiscovery.js +10 -0
  140. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  141. package/dist/utils/agents.d.ts +1 -1
  142. package/dist/utils/agents.d.ts.map +1 -1
  143. package/dist/utils/agents.js +11 -0
  144. package/dist/utils/agents.js.map +1 -1
  145. package/dist/utils/session.d.ts +2 -0
  146. package/dist/utils/session.d.ts.map +1 -1
  147. package/dist/utils/session.js +3 -0
  148. package/dist/utils/session.js.map +1 -1
  149. package/package.json +1 -1
  150. package/src/AgentManager.ts +34 -0
  151. package/src/__tests__/AgentManager.test.ts +60 -0
  152. package/src/__tests__/adapters/AntigravityCliAdapter.test.ts +324 -0
  153. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +23 -0
  154. package/src/__tests__/adapters/CodexAdapter.test.ts +7 -0
  155. package/src/__tests__/adapters/KiroAdapter.test.ts +534 -0
  156. package/src/__tests__/capacity/claude.test.ts +467 -0
  157. package/src/__tests__/capacity/fixtures/claude-credentials.json +7 -0
  158. package/src/__tests__/capacity/fixtures/claude-usage-malformed.json +1 -0
  159. package/src/__tests__/capacity/fixtures/claude-usage-partial.json +15 -0
  160. package/src/__tests__/capacity/fixtures/claude-usage.json +63 -0
  161. package/src/__tests__/capacity/fixtures/openai-auth.json +6 -0
  162. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +9 -0
  163. package/src/__tests__/capacity/fixtures/openai-usage-completions.json +67 -0
  164. package/src/__tests__/capacity/fixtures/openai-usage-responses.json +20 -0
  165. package/src/__tests__/capacity/fixtures/wham-usage.json +32 -0
  166. package/src/__tests__/capacity/openai.test.ts +570 -0
  167. package/src/__tests__/capacity/wham.test.ts +104 -0
  168. package/src/__tests__/providers/copilot/CopilotAdapter.test.ts +12 -0
  169. package/src/__tests__/providers/gemini/GeminiCliAdapter.test.ts +21 -0
  170. package/src/__tests__/providers/grok/GrokAgentMapper.test.ts +57 -0
  171. package/src/__tests__/{adapters → providers/grok}/GrokCliAdapter.test.ts +18 -9
  172. package/src/__tests__/providers/grok/GrokSessionLocator.test.ts +106 -0
  173. package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +18 -0
  174. package/src/__tests__/providers/pi/PiAdapter.test.ts +15 -0
  175. package/src/__tests__/utils/agents.test.ts +7 -0
  176. package/src/__tests__/utils/session.test.ts +11 -1
  177. package/src/adapters/AgentAdapter.ts +11 -0
  178. package/src/adapters/AntigravityCliAdapter.ts +356 -0
  179. package/src/adapters/KiroAdapter.ts +456 -0
  180. package/src/adapters/index.ts +3 -1
  181. package/src/capacity/claude.ts +313 -0
  182. package/src/capacity/codex.ts +25 -134
  183. package/src/capacity/index.ts +33 -4
  184. package/src/capacity/openai.ts +361 -0
  185. package/src/capacity/wham.ts +84 -0
  186. package/src/index.ts +11 -2
  187. package/src/providers/claude/ClaudeCodeAdapter.ts +28 -23
  188. package/src/providers/claude/ClaudeSessionLocator.ts +20 -0
  189. package/src/providers/codex/CodexAdapter.ts +8 -0
  190. package/src/providers/copilot/CopilotAdapter.ts +26 -10
  191. package/src/providers/copilot/CopilotSessionLocator.ts +7 -1
  192. package/src/providers/gemini/GeminiCliAdapter.ts +8 -0
  193. package/src/providers/grok/GrokAgentMapper.ts +41 -0
  194. package/src/providers/grok/GrokCliAdapter.ts +125 -0
  195. package/src/providers/grok/GrokSessionLocator.ts +179 -0
  196. package/src/providers/grok/GrokSessionParser.ts +166 -0
  197. package/src/providers/opencode/OpenCodeAdapter.ts +4 -0
  198. package/src/providers/opencode/OpenCodeSessionLocator.ts +43 -15
  199. package/src/providers/pi/PiAdapter.ts +12 -0
  200. package/src/providers/pi/PiSessionLocator.ts +16 -1
  201. package/src/readiness/AgentReadiness.ts +4 -1
  202. package/src/runtime/ManagedAgentRuntime.ts +2 -0
  203. package/src/runtime/herdr/HerdrAgentDiscovery.ts +5 -1
  204. package/src/utils/agents.ts +11 -1
  205. package/src/utils/session.ts +11 -0
  206. package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +0 -1
  207. package/dist/adapters/GrokCliAdapter.d.ts +0 -80
  208. package/dist/adapters/GrokCliAdapter.d.ts.map +0 -1
  209. package/dist/adapters/GrokCliAdapter.js +0 -310
  210. package/dist/adapters/GrokCliAdapter.js.map +0 -1
  211. package/src/adapters/GrokCliAdapter.ts +0 -408
@@ -0,0 +1,271 @@
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
+ const SESSION_DURATION_MINUTES = 5 * 60;
10
+ const WEEK_DURATION_MINUTES = 7 * 24 * 60;
11
+ function record(value) {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
13
+ }
14
+ function percent(value) {
15
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100 ? value : null;
16
+ }
17
+ function nonEmptyText(value) {
18
+ return typeof value === "string" && value.trim() ? value.trim() : null;
19
+ }
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
+ function resetTime(value) {
118
+ if (typeof value !== "string") return null;
119
+ const milliseconds = Date.parse(value);
120
+ return Number.isNaN(milliseconds) ? null : new Date(milliseconds).toISOString();
121
+ }
122
+ function usageWindow(value, id, label, durationMinutes) {
123
+ const input = record(value);
124
+ if (!input) return null;
125
+ return {
126
+ id,
127
+ label,
128
+ durationMinutes,
129
+ usedPercent: percent(input.utilization),
130
+ resetsAt: resetTime(input.resets_at)
131
+ };
132
+ }
133
+ function slug(value) {
134
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
135
+ }
136
+ function scopedWindows(value) {
137
+ if (!Array.isArray(value)) return [];
138
+ const seen = new Set();
139
+ const windows = [];
140
+ for (const entry of value){
141
+ const limit = record(entry);
142
+ const model = record(record(limit?.scope)?.model);
143
+ const name = nonEmptyText(model?.display_name);
144
+ const identity = nonEmptyText(model?.id) ?? name;
145
+ if (limit?.kind !== "weekly_scoped" || limit.group !== "weekly" || !name || !identity) {
146
+ continue;
147
+ }
148
+ const modelSlug = slug(identity);
149
+ if (!modelSlug || modelSlug === "all-models" || modelSlug.endsWith("-all-models")) {
150
+ continue;
151
+ }
152
+ const id = `claude:weekly:${modelSlug}`;
153
+ if (seen.has(id)) continue;
154
+ seen.add(id);
155
+ windows.push({
156
+ id,
157
+ label: `${name} weekly`,
158
+ durationMinutes: WEEK_DURATION_MINUTES,
159
+ usedPercent: percent(limit.percent),
160
+ resetsAt: resetTime(limit.resets_at)
161
+ });
162
+ }
163
+ return windows;
164
+ }
165
+ function finiteNumber(value) {
166
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
167
+ }
168
+ function extraUsageWindow(value) {
169
+ const extra = record(value);
170
+ if (!extra || extra.is_enabled !== true) return null;
171
+ const limitMinor = finiteNumber(extra.monthly_limit);
172
+ const usedMinor = finiteNumber(extra.used_credits);
173
+ if (limitMinor === null || usedMinor === null) return null;
174
+ const total = limitMinor / 100;
175
+ const current = usedMinor / 100;
176
+ const currency = nonEmptyText(extra.currency)?.toUpperCase() ?? "USD";
177
+ return {
178
+ id: "claude:extra-usage",
179
+ label: `Extra usage · ${currency}`,
180
+ limitType: "CREDIT_LIMIT",
181
+ durationMinutes: null,
182
+ usedPercent: percent(extra.utilization) ?? (total > 0 ? Math.max(0, Math.min(100, current / total * 100)) : null),
183
+ resetsAt: null,
184
+ total,
185
+ current,
186
+ remaining: Math.max(0, total - current)
187
+ };
188
+ }
189
+ function availability(windows) {
190
+ const known = windows.flatMap((window)=>window.usedPercent === null ? [] : [
191
+ window.usedPercent
192
+ ]);
193
+ if (known.length === 0) return "unknown";
194
+ return known.some((used)=>used < 100) ? "yes" : "no";
195
+ }
196
+ function retryAfterTime(response, now) {
197
+ const raw = response.headers.get("Retry-After")?.trim();
198
+ if (!raw) return null;
199
+ const seconds = Number(raw);
200
+ if (Number.isFinite(seconds) && seconds >= 0) {
201
+ return new Date(now.getTime() + seconds * 1000).toISOString();
202
+ }
203
+ const milliseconds = Date.parse(raw);
204
+ return Number.isNaN(milliseconds) ? null : new Date(milliseconds).toISOString();
205
+ }
206
+ export function parseClaudeUsage(raw, checkedAt) {
207
+ const input = record(raw);
208
+ if (!input) throw new Error("Claude usage response is malformed");
209
+ const windows = [
210
+ usageWindow(input.five_hour, "session", "Session", SESSION_DURATION_MINUTES),
211
+ usageWindow(input.seven_day, "weekly", "Weekly", WEEK_DURATION_MINUTES),
212
+ usageWindow(input.seven_day_sonnet, "claude:sonnet:weekly", "Sonnet weekly", WEEK_DURATION_MINUTES),
213
+ usageWindow(input.seven_day_opus, "claude:opus:weekly", "Opus weekly", WEEK_DURATION_MINUTES),
214
+ ...scopedWindows(input.limits),
215
+ extraUsageWindow(input.extra_usage)
216
+ ].filter((window)=>window !== null);
217
+ return {
218
+ harness: "claude",
219
+ provider: "anthropic",
220
+ generatedAt: checkedAt,
221
+ authenticated: true,
222
+ available: availability(windows),
223
+ windows,
224
+ creditsRemaining: null
225
+ };
226
+ }
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);
231
+ let response;
232
+ try {
233
+ response = await (options.fetch ?? globalThis.fetch)(CLAUDE_USAGE_URL, {
234
+ method: "GET",
235
+ headers: {
236
+ Accept: "application/json",
237
+ Authorization: `Bearer ${token}`,
238
+ "Content-Type": "application/json",
239
+ "User-Agent": "claude-code/2.1.0",
240
+ "anthropic-beta": "oauth-2025-04-20"
241
+ },
242
+ signal: controller.signal
243
+ });
244
+ } catch {
245
+ 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
+ }
255
+ if (response.status === 429) {
256
+ const retryAt = retryAfterTime(response, options.now?.() ?? new Date());
257
+ throw new Error(retryAt ? `Claude OAuth usage rate limited until ${retryAt}` : "Claude OAuth usage rate limited (HTTP 429)");
258
+ }
259
+ if (!response.ok) {
260
+ throw new Error(`Claude OAuth usage request failed (HTTP ${response.status})`);
261
+ }
262
+ let raw;
263
+ try {
264
+ raw = await response.json();
265
+ } catch {
266
+ throw new Error("Claude usage response is malformed");
267
+ }
268
+ return parseClaudeUsage(raw, options.checkedAt);
269
+ }
270
+
271
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/capacity/claude.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { Availability, CapacityReport, CapacityWindow } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type ClaudeCapacityOptions = {\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n now?: () => Date;\n cwd?: string;\n platform?: NodeJS.Platform;\n keychainRead?: (service: string) => Promise<string | null>;\n};\n\nexport type ClaudeProbeOptions = ClaudeCapacityOptions & {\n checkedAt: string;\n};\n\nconst CLAUDE_USAGE_URL = \"https://api.anthropic.com/api/oauth/usage\";\nconst CLAUDE_KEYCHAIN_SERVICE = \"Claude Code-credentials\";\nconst SESSION_DURATION_MINUTES = 5 * 60;\nconst WEEK_DURATION_MINUTES = 7 * 24 * 60;\n\ntype Credential = { token: string | null; expired: boolean };\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 credentialsPath(options: ClaudeProbeOptions): string {\n const env = options.env ?? process.env;\n const configured = nonEmptyText(env.CLAUDE_CONFIG_DIR);\n if (configured) {\n const profileRoot = isAbsolute(configured)\n ? configured\n : resolve(options.cwd ?? process.cwd(), configured);\n return join(profileRoot, \".credentials.json\");\n }\n return join(env.HOME || homedir(), \".claude\", \".credentials.json\");\n}\n\nfunction credentialFromPayload(payload: string | null, now: Date): Credential {\n if (!payload) return { token: null, expired: false };\n try {\n return credentialFromRoot(record(JSON.parse(payload)), now);\n } catch {\n return { token: null, expired: false };\n }\n}\n\nasync function readProfileCredential(options: ClaudeProbeOptions, now: Date): Promise<Credential> {\n try {\n const payload = await (options.readFile ?? readFile)(credentialsPath(options), \"utf8\");\n return credentialFromPayload(payload, now);\n } catch {\n return { token: null, expired: false };\n }\n}\n\nasync function readKeychainCredential(options: ClaudeProbeOptions, now: Date): Promise<Credential> {\n const env = options.env ?? process.env;\n const usesDefaultProfile = !nonEmptyText(env.CLAUDE_CONFIG_DIR);\n if (!usesDefaultProfile || (options.platform ?? process.platform) !== \"darwin\") {\n return { token: null, expired: false };\n }\n try {\n const payload = await (options.keychainRead ?? readClaudeKeychain)(CLAUDE_KEYCHAIN_SERVICE);\n return credentialFromPayload(payload, now);\n } catch {\n return { token: null, expired: false };\n }\n}\n\nasync function resolveToken(options: ClaudeProbeOptions): Promise<string> {\n const env = options.env ?? process.env;\n const environmentToken = nonEmptyText(env.CLAUDE_CODE_OAUTH_TOKEN);\n if (environmentToken) return environmentToken;\n\n const now = options.now?.() ?? new Date();\n const profileCredential = await readProfileCredential(options, now);\n if (profileCredential.token) return profileCredential.token;\n\n const keychainCredential = await readKeychainCredential(options, now);\n if (keychainCredential.token) return keychainCredential.token;\n if (profileCredential.expired || keychainCredential.expired) {\n throw new Error(\"Claude OAuth credentials expired\");\n }\n throw new Error(\"Claude OAuth credentials not found or malformed\");\n}\n\nfunction credentialFromRoot(root: Record<string, unknown> | null, now: Date): Credential {\n const oauth = record(root?.claudeAiOauth);\n const token = nonEmptyText(oauth?.accessToken);\n if (!token) return { token: null, expired: false };\n const expired =\n typeof oauth?.expiresAt === \"number\" &&\n Number.isFinite(oauth.expiresAt) &&\n oauth.expiresAt <= now.getTime();\n return { token: expired ? null : token, expired };\n}\n\nasync function readClaudeKeychain(service: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync(\n \"/usr/bin/security\",\n [\"find-generic-password\", \"-s\", service, \"-w\"],\n { encoding: \"utf8\", timeout: 1500, maxBuffer: 1024 * 1024 },\n );\n return nonEmptyText(stdout);\n } catch {\n return null;\n }\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\")) {\n continue;\n }\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 parseClaudeUsage(raw: unknown, checkedAt: string): CapacityReport {\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 harness: \"claude\",\n provider: \"anthropic\",\n generatedAt: checkedAt,\n authenticated: true,\n available: availability(windows),\n windows,\n creditsRemaining: null,\n };\n}\n\nexport async function probeClaudeCapacity(options: ClaudeProbeOptions): Promise<CapacityReport> {\n const token = await resolveToken(options);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 5000);\n let response: Response;\n try {\n response = await (options.fetch ?? globalThis.fetch)(CLAUDE_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: controller.signal,\n });\n } catch {\n throw new Error(\"Claude OAuth usage request failed\");\n } finally {\n clearTimeout(timer);\n }\n if (response.status === 401) {\n throw new Error(\"Claude OAuth request unauthorized (HTTP 401)\");\n }\n if (response.status === 403) {\n throw new Error(\"Claude OAuth request forbidden (HTTP 403)\");\n }\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 parseClaudeUsage(raw, options.checkedAt);\n}\n"],"names":["execFile","readFile","homedir","isAbsolute","join","resolve","promisify","execFileAsync","CLAUDE_USAGE_URL","CLAUDE_KEYCHAIN_SERVICE","SESSION_DURATION_MINUTES","WEEK_DURATION_MINUTES","record","value","Array","isArray","percent","Number","isFinite","nonEmptyText","trim","credentialsPath","options","env","process","configured","CLAUDE_CONFIG_DIR","profileRoot","cwd","HOME","credentialFromPayload","payload","now","token","expired","credentialFromRoot","JSON","parse","readProfileCredential","readKeychainCredential","usesDefaultProfile","platform","keychainRead","readClaudeKeychain","resolveToken","environmentToken","CLAUDE_CODE_OAUTH_TOKEN","Date","profileCredential","keychainCredential","Error","root","oauth","claudeAiOauth","accessToken","expiresAt","getTime","service","stdout","encoding","timeout","maxBuffer","resetTime","milliseconds","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","raw","headers","get","seconds","parseClaudeUsage","checkedAt","five_hour","seven_day","seven_day_sonnet","seven_day_opus","limits","extra_usage","filter","harness","provider","generatedAt","authenticated","available","creditsRemaining","probeClaudeCapacity","controller","AbortController","timer","setTimeout","abort","timeoutMs","fetch","globalThis","method","Accept","Authorization","signal","clearTimeout","status","retryAt","ok","json"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { CapacityReport, CapacityWindow } from "./types.js";
2
+ export { toRateWindow } from "./wham.js";
2
3
  type CodexUsageSource = "pat" | "oauth" | "cli";
3
4
  type UsageSnapshot = {
4
5
  windows: CapacityWindow[];
@@ -28,10 +29,8 @@ type CodexProbeOptions = {
28
29
  now?: () => Date;
29
30
  };
30
31
  export declare function resolveCodexAuthPath(env?: NodeJS.ProcessEnv): string;
31
- export declare function toRateWindow(value: unknown, id: string, label: string): CapacityWindow | null;
32
32
  export declare function parseUsage(raw: unknown, source: "pat" | "oauth"): UsageSnapshot;
33
33
  export declare function parseCliUsage(raw: unknown): UsageSnapshot;
34
34
  export declare function codexUnavailableReport(checkedAt: string): CapacityReport;
35
35
  export declare function probeCodexCapacity(options: CodexProbeOptions): Promise<CapacityReport>;
36
- export {};
37
36
  //# sourceMappingURL=codex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/capacity/codex.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,KAAK,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAChD,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,KAAK,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC;AAC1E,KAAK,YAAY,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB,+DAMxB,CAAC;AAEX,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AA+BF,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAGR;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,cAAc,GAAG,IAAI,CAYvB;AAwBD,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,KAAK,GAAG,OAAO,GACtB,aAAa,CAaf;AAgCD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAazD;AAyJD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAUxE;AA+CD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,cAAc,CAAC,CAuDzB"}
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/capacity/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,KAAK,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAChD,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,KAAK,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC;AAC1E,KAAK,YAAY,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB,+DAAgE,CAAC;AAEnG,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAgBF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGjF;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,aAAa,CAG/E;AAwBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAWzD;AAuID,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAUxE;AA4CD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoD5F"}
@@ -1,6 +1,8 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
+ import { parseWhamUsage, resetTime, safeIdentifier } from "./wham.js";
5
+ export { toRateWindow } from "./wham.js";
4
6
  export const CODEX_APP_SERVER_ARGS = [
5
7
  "-s",
6
8
  "read-only",
@@ -17,59 +19,15 @@ function finiteNumber(value) {
17
19
  function nonEmptyText(value) {
18
20
  return typeof value === "string" && value.length > 0 ? value : null;
19
21
  }
20
- function resetTime(value) {
21
- const seconds = finiteNumber(value);
22
- if (seconds !== null) return new Date(seconds * 1000).toISOString();
23
- if (typeof value === "string" && !Number.isNaN(Date.parse(value))) return new Date(value).toISOString();
24
- return null;
25
- }
26
- function safeIdentifier(value) {
27
- const candidate = nonEmptyText(value);
28
- if (!candidate || !/^[a-z][a-z0-9_-]{0,63}$/i.test(candidate)) return null;
29
- if (/(?:account|token|secret|key)[_-]?\d{6,}/i.test(candidate)) return null;
30
- return candidate;
31
- }
32
22
  export function resolveCodexAuthPath(env = process.env) {
33
23
  const root = env.CODEX_HOME || join(env.HOME || "", ".codex");
34
24
  return join(root, "auth.json");
35
25
  }
36
- export function toRateWindow(value, id, label) {
37
- const input = record(value);
38
- if (!input) return null;
39
- const used = finiteNumber(input.used_percent);
40
- const seconds = finiteNumber(input.limit_window_seconds);
41
- return {
42
- id,
43
- label,
44
- durationMinutes: seconds === null ? null : seconds / 60,
45
- usedPercent: used,
46
- resetsAt: resetTime(input.reset_at)
47
- };
48
- }
49
- function extraWindows(value) {
50
- if (!Array.isArray(value)) return [];
51
- return value.flatMap((entry, index)=>{
52
- const limit = record(entry);
53
- if (!limit) return [];
54
- const scope = safeIdentifier(limit.limit_name) ?? `extra-${index + 1}`;
55
- const windows = record(limit.rate_limit) ?? limit;
56
- return [
57
- toRateWindow(windows.primary_window, `${scope}:primary`, `${scope} primary`),
58
- toRateWindow(windows.secondary_window, `${scope}:secondary`, `${scope} secondary`)
59
- ].filter((window)=>window !== null);
60
- });
61
- }
62
26
  export function parseUsage(raw, source) {
63
- const response = record(raw) ?? {};
64
- const limits = record(response.rate_limit) ?? {};
65
- const credits = record(response.credits) ?? {};
27
+ const { windows, creditsRemaining } = parseWhamUsage(raw);
66
28
  return {
67
- windows: [
68
- toRateWindow(limits.primary_window, "session", "Session"),
69
- toRateWindow(limits.secondary_window, "weekly", "Weekly"),
70
- ...extraWindows(response.additional_rate_limits)
71
- ].filter((window)=>window !== null),
72
- creditsRemaining: finiteNumber(credits.balance),
29
+ windows,
30
+ creditsRemaining,
73
31
  source
74
32
  };
75
33
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/capacity/codex.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { CapacityReport, CapacityWindow } from \"./types.js\";\n\ntype CodexUsageSource = \"pat\" | \"oauth\" | \"cli\";\ntype UsageSnapshot = {\n windows: CapacityWindow[];\n creditsRemaining: number | null;\n source: CodexUsageSource;\n};\n\ntype UnknownRecord = Record<string, unknown>;\ntype RpcMessage = { id?: number; method: string; params?: UnknownRecord };\ntype CliResponses = { rateLimits: unknown; account: unknown };\ntype CodexRpc = (messages: RpcMessage[]) => Promise<CliResponses>;\n\nexport const CODEX_APP_SERVER_ARGS = [\n \"-s\",\n \"read-only\",\n \"-a\",\n \"untrusted\",\n \"app-server\",\n] as const;\n\ntype CodexProbeOptions = {\n installed: boolean;\n checkedAt: string;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n rpc?: CodexRpc;\n timeoutMs?: number;\n env?: NodeJS.ProcessEnv;\n now?: () => Date;\n};\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\nfunction 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 return null;\n}\n\nfunction 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 resolveCodexAuthPath(\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const root = env.CODEX_HOME || join(env.HOME || \"\", \".codex\");\n return join(root, \"auth.json\");\n}\n\nexport function toRateWindow(\n value: unknown,\n id: string,\n label: string,\n): 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(\n windows.primary_window,\n `${scope}:primary`,\n `${scope} primary`,\n ),\n toRateWindow(\n windows.secondary_window,\n `${scope}:secondary`,\n `${scope} secondary`,\n ),\n ].filter((window): window is CapacityWindow => window !== null);\n });\n}\n\nexport function parseUsage(\n raw: unknown,\n source: \"pat\" | \"oauth\",\n): UsageSnapshot {\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 source,\n };\n}\n\nfunction cliWindow(\n value: unknown,\n id: string,\n label: string,\n): CapacityWindow | null {\n const input = record(value);\n if (!input) return null;\n return {\n id,\n label,\n durationMinutes: finiteNumber(input.windowDurationMins),\n usedPercent: finiteNumber(input.usedPercent),\n resetsAt: resetTime(input.resetsAt),\n };\n}\n\nfunction cliSnapshotWindows(\n value: unknown,\n fallbackId: string,\n): CapacityWindow[] {\n const snapshot = record(value);\n if (!snapshot) return [];\n const scope =\n safeIdentifier(snapshot.limitId) ?? safeIdentifier(fallbackId) ?? \"codex\";\n return [\n cliWindow(snapshot.primary, `${scope}:primary`, `${scope} primary`),\n cliWindow(snapshot.secondary, `${scope}:secondary`, `${scope} secondary`),\n ].filter((item): item is CapacityWindow => item !== null);\n}\n\nexport function parseCliUsage(raw: unknown): UsageSnapshot {\n const response = record(raw) ?? {};\n const primary = record(response.rateLimits);\n const windows = primary ? cliSnapshotWindows(primary, \"codex\") : [];\n const buckets = record(response.rateLimitsByLimitId);\n if (buckets) {\n for (const [id, snapshot] of Object.entries(buckets))\n windows.push(...cliSnapshotWindows(snapshot, id));\n }\n const unique = [\n ...new Map(windows.map((window) => [window.id, window])).values(),\n ];\n return { windows: unique, creditsRemaining: null, source: \"cli\" };\n}\n\nfunction capacityFromSnapshot(\n snapshot: UsageSnapshot,\n context: CodexProbeOptions,\n raw?: unknown,\n): CapacityReport {\n const hasUsage = snapshot.windows.some(\n (window) => window.usedPercent !== null,\n );\n const rateLimits = record(record(raw)?.rateLimits);\n const reached = nonEmptyText(rateLimits?.rateLimitReachedType);\n const resetCredits =\n record(record(raw)?.rateLimitResetCredits) ??\n record(record(raw)?.usageLimitResetCredits);\n return {\n harness: \"codex\",\n provider: \"openai\",\n generatedAt: context.checkedAt,\n authenticated: true,\n available: reached ? \"no\" : hasUsage ? \"yes\" : \"unknown\",\n windows: snapshot.windows,\n creditsRemaining:\n snapshot.creditsRemaining ?? finiteNumber(resetCredits?.availableCount),\n };\n}\n\nfunction jwtExpiry(token: string): number | null {\n const part = token.split(\".\")[1];\n if (!part) return null;\n try {\n return finiteNumber(\n record(JSON.parse(Buffer.from(part, \"base64url\").toString(\"utf8\")))?.exp,\n );\n } catch {\n return null;\n }\n}\n\nfunction staleOAuth(tokens: UnknownRecord, token: string, now: Date): boolean {\n const metadata = tokens.expires_at ?? tokens.expiresAt ?? tokens.expiry;\n let expiry: number | null = finiteNumber(metadata);\n if (typeof metadata === \"string\") {\n const parsed = Date.parse(metadata);\n expiry = Number.isNaN(parsed) ? null : parsed / 1000;\n }\n expiry ??= jwtExpiry(token);\n return expiry !== null && expiry <= now.getTime() / 1000;\n}\n\nasync function fetchJson(\n fetcher: typeof globalThis.fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<unknown> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetcher(url, { ...init, signal: controller.signal });\n if (!response.ok)\n throw new Error(\n response.status === 401 ? \"unauthorized\" : \"request failed\",\n );\n return await response.json();\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function apiSnapshot(\n token: string,\n accountId: string,\n source: \"pat\" | \"oauth\",\n options: CodexProbeOptions,\n): Promise<UsageSnapshot> {\n const fetcher = options.fetch ?? globalThis.fetch;\n const raw = await fetchJson(\n fetcher,\n \"https://chatgpt.com/backend-api/wham/usage\",\n {\n headers: {\n Authorization: `Bearer ${token}`,\n \"ChatGPT-Account-Id\": accountId,\n },\n },\n options.timeoutMs ?? 5000,\n );\n return parseUsage(raw, source);\n}\n\nfunction appServerRpc(\n messages: RpcMessage[],\n timeoutMs = 5000,\n): Promise<CliResponses> {\n return new Promise((resolve, reject) => {\n const child = spawn(\"codex\", CODEX_APP_SERVER_ARGS, {\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n const results: Partial<CliResponses> = {};\n let buffer = \"\";\n let settled = false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n child.kill();\n if (error) reject(error);\n else resolve(results as CliResponses);\n };\n const timer = setTimeout(\n () => finish(new Error(\"codex probe timed out\")),\n timeoutMs,\n );\n child.once(\"error\", () =>\n finish(new Error(\"codex app-server unavailable\")),\n );\n child.once(\"exit\", () => {\n if (!settled) finish(new Error(\"codex app-server exited\"));\n });\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n buffer += chunk;\n for (;;) {\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) break;\n const line = buffer.slice(0, newline).trim();\n buffer = buffer.slice(newline + 1);\n if (!line) continue;\n let message: UnknownRecord;\n try {\n message = JSON.parse(line) as UnknownRecord;\n } catch {\n continue;\n }\n if (message.id === 1) {\n for (const request of messages.slice(1))\n child.stdin.write(`${JSON.stringify(request)}\\n`);\n } else if (message.id === 2) {\n if (message.error)\n finish(new Error(\"codex rate-limit method failed\"));\n else results.rateLimits = message.result;\n } else if (message.id === 3) {\n if (message.error) finish(new Error(\"codex account method failed\"));\n else results.account = message.result;\n }\n if (\"rateLimits\" in results && \"account\" in results) finish();\n }\n });\n child.stdin.write(`${JSON.stringify(messages[0])}\\n`);\n });\n}\n\nexport function codexUnavailableReport(checkedAt: string): CapacityReport {\n return {\n harness: \"codex\",\n provider: \"openai\",\n generatedAt: checkedAt,\n authenticated: null,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n };\n}\n\nfunction unavailable(options: CodexProbeOptions): CapacityReport {\n return codexUnavailableReport(options.checkedAt);\n}\n\nasync function cliFallback(\n options: CodexProbeOptions,\n): Promise<CapacityReport> {\n if (!options.installed) return unavailable(options);\n const messages: RpcMessage[] = [\n {\n id: 1,\n method: \"initialize\",\n params: {\n clientInfo: { name: \"ai-devkit\", title: null, version: \"1\" },\n capabilities: null,\n },\n },\n { method: \"initialized\" },\n { id: 2, method: \"account/rateLimits/read\" },\n { id: 3, method: \"account/read\" },\n ];\n try {\n const rpc =\n options.rpc ?? ((requests) => appServerRpc(requests, options.timeoutMs));\n const response = await rpc(messages);\n const result = capacityFromSnapshot(\n parseCliUsage(response.rateLimits),\n options,\n response.rateLimits,\n );\n const accountEnvelope = record(response.account);\n if (\n accountEnvelope &&\n Object.hasOwn(accountEnvelope, \"account\") &&\n !record(accountEnvelope.account)\n ) {\n result.authenticated = false;\n result.available = \"unknown\";\n }\n return result;\n } catch {\n return unavailable(options);\n }\n}\n\nexport async function probeCodexCapacity(\n options: CodexProbeOptions,\n): Promise<CapacityReport> {\n let parsed: UnknownRecord | null = null;\n try {\n const contents = await (options.readFile ?? readFile)(\n resolveCodexAuthPath(options.env),\n \"utf8\",\n );\n parsed = record(JSON.parse(contents));\n } catch {\n return cliFallback(options);\n }\n\n const auth = parsed ?? {};\n const pat = nonEmptyText(auth.personal_access_token);\n if (pat) {\n try {\n const fetcher = options.fetch ?? globalThis.fetch;\n const whoami = record(\n await fetchJson(\n fetcher,\n \"https://auth.openai.com/api/accounts/v1/user-auth-credential/whoami\",\n { headers: { Authorization: `Bearer ${pat}` } },\n options.timeoutMs ?? 5000,\n ),\n );\n const accountId = nonEmptyText(whoami?.chatgpt_account_id);\n if (!accountId) throw new Error(\"account unavailable\");\n return capacityFromSnapshot(\n await apiSnapshot(pat, accountId, \"pat\", options),\n options,\n );\n } catch {\n // Continue to a separately available OAuth credential before using the CLI.\n }\n }\n\n const tokens = record(auth.tokens);\n const accessToken = nonEmptyText(tokens?.access_token);\n const accountId = nonEmptyText(tokens?.account_id);\n if (\n tokens &&\n accessToken &&\n accountId &&\n !staleOAuth(tokens, accessToken, (options.now ?? (() => new Date()))())\n ) {\n try {\n return capacityFromSnapshot(\n await apiSnapshot(accessToken, accountId, \"oauth\", options),\n options,\n );\n } catch {\n return cliFallback(options);\n }\n }\n return cliFallback(options);\n}\n"],"names":["spawn","readFile","join","CODEX_APP_SERVER_ARGS","record","value","Array","isArray","finiteNumber","Number","isFinite","nonEmptyText","length","resetTime","seconds","Date","toISOString","isNaN","parse","safeIdentifier","candidate","test","resolveCodexAuthPath","env","process","root","CODEX_HOME","HOME","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","parseUsage","raw","source","response","limits","credits","additional_rate_limits","creditsRemaining","balance","cliWindow","windowDurationMins","cliSnapshotWindows","fallbackId","snapshot","limitId","primary","secondary","item","parseCliUsage","rateLimits","buckets","rateLimitsByLimitId","Object","entries","push","unique","Map","map","values","capacityFromSnapshot","context","hasUsage","some","reached","rateLimitReachedType","resetCredits","rateLimitResetCredits","usageLimitResetCredits","harness","provider","generatedAt","checkedAt","authenticated","available","availableCount","jwtExpiry","token","part","split","JSON","Buffer","from","toString","exp","staleOAuth","tokens","now","metadata","expires_at","expiresAt","expiry","parsed","getTime","fetchJson","fetcher","url","init","timeoutMs","controller","AbortController","timer","setTimeout","abort","signal","ok","Error","status","json","clearTimeout","apiSnapshot","accountId","options","fetch","globalThis","headers","Authorization","appServerRpc","messages","Promise","resolve","reject","child","stdio","results","buffer","settled","finish","error","kill","once","stdout","setEncoding","on","chunk","newline","indexOf","line","slice","trim","message","request","stdin","write","stringify","result","account","codexUnavailableReport","unavailable","cliFallback","installed","method","params","clientInfo","name","title","version","capabilities","rpc","requests","accountEnvelope","hasOwn","probeCodexCapacity","contents","auth","pat","personal_access_token","whoami","chatgpt_account_id","accessToken","access_token","account_id"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/capacity/codex.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parseWhamUsage, resetTime, safeIdentifier } from \"./wham.js\";\nimport type { CapacityReport, CapacityWindow } from \"./types.js\";\n\nexport { toRateWindow } from \"./wham.js\";\n\ntype CodexUsageSource = \"pat\" | \"oauth\" | \"cli\";\ntype UsageSnapshot = {\n windows: CapacityWindow[];\n creditsRemaining: number | null;\n source: CodexUsageSource;\n};\n\ntype UnknownRecord = Record<string, unknown>;\ntype RpcMessage = { id?: number; method: string; params?: UnknownRecord };\ntype CliResponses = { rateLimits: unknown; account: unknown };\ntype CodexRpc = (messages: RpcMessage[]) => Promise<CliResponses>;\n\nexport const CODEX_APP_SERVER_ARGS = [\"-s\", \"read-only\", \"-a\", \"untrusted\", \"app-server\"] as const;\n\ntype CodexProbeOptions = {\n installed: boolean;\n checkedAt: string;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n rpc?: CodexRpc;\n timeoutMs?: number;\n env?: NodeJS.ProcessEnv;\n now?: () => Date;\n};\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 resolveCodexAuthPath(env: NodeJS.ProcessEnv = process.env): string {\n const root = env.CODEX_HOME || join(env.HOME || \"\", \".codex\");\n return join(root, \"auth.json\");\n}\n\nexport function parseUsage(raw: unknown, source: \"pat\" | \"oauth\"): UsageSnapshot {\n const { windows, creditsRemaining } = parseWhamUsage(raw);\n return { windows, creditsRemaining, source };\n}\n\nfunction cliWindow(value: unknown, id: string, label: string): CapacityWindow | null {\n const input = record(value);\n if (!input) return null;\n return {\n id,\n label,\n durationMinutes: finiteNumber(input.windowDurationMins),\n usedPercent: finiteNumber(input.usedPercent),\n resetsAt: resetTime(input.resetsAt),\n };\n}\n\nfunction cliSnapshotWindows(value: unknown, fallbackId: string): CapacityWindow[] {\n const snapshot = record(value);\n if (!snapshot) return [];\n const scope = safeIdentifier(snapshot.limitId) ?? safeIdentifier(fallbackId) ?? \"codex\";\n return [\n cliWindow(snapshot.primary, `${scope}:primary`, `${scope} primary`),\n cliWindow(snapshot.secondary, `${scope}:secondary`, `${scope} secondary`),\n ].filter((item): item is CapacityWindow => item !== null);\n}\n\nexport function parseCliUsage(raw: unknown): UsageSnapshot {\n const response = record(raw) ?? {};\n const primary = record(response.rateLimits);\n const windows = primary ? cliSnapshotWindows(primary, \"codex\") : [];\n const buckets = record(response.rateLimitsByLimitId);\n if (buckets) {\n for (const [id, snapshot] of Object.entries(buckets))\n windows.push(...cliSnapshotWindows(snapshot, id));\n }\n const unique = [...new Map(windows.map((window) => [window.id, window])).values()];\n return { windows: unique, creditsRemaining: null, source: \"cli\" };\n}\n\nfunction capacityFromSnapshot(\n snapshot: UsageSnapshot,\n context: CodexProbeOptions,\n raw?: unknown,\n): CapacityReport {\n const hasUsage = snapshot.windows.some((window) => window.usedPercent !== null);\n const rateLimits = record(record(raw)?.rateLimits);\n const reached = nonEmptyText(rateLimits?.rateLimitReachedType);\n const resetCredits =\n record(record(raw)?.rateLimitResetCredits) ?? record(record(raw)?.usageLimitResetCredits);\n return {\n harness: \"codex\",\n provider: \"openai\",\n generatedAt: context.checkedAt,\n authenticated: true,\n available: reached ? \"no\" : hasUsage ? \"yes\" : \"unknown\",\n windows: snapshot.windows,\n creditsRemaining: snapshot.creditsRemaining ?? finiteNumber(resetCredits?.availableCount),\n };\n}\n\nfunction jwtExpiry(token: string): number | null {\n const part = token.split(\".\")[1];\n if (!part) return null;\n try {\n return finiteNumber(record(JSON.parse(Buffer.from(part, \"base64url\").toString(\"utf8\")))?.exp);\n } catch {\n return null;\n }\n}\n\nfunction staleOAuth(tokens: UnknownRecord, token: string, now: Date): boolean {\n const metadata = tokens.expires_at ?? tokens.expiresAt ?? tokens.expiry;\n let expiry: number | null = finiteNumber(metadata);\n if (typeof metadata === \"string\") {\n const parsed = Date.parse(metadata);\n expiry = Number.isNaN(parsed) ? null : parsed / 1000;\n }\n expiry ??= jwtExpiry(token);\n return expiry !== null && expiry <= now.getTime() / 1000;\n}\n\nasync function fetchJson(\n fetcher: typeof globalThis.fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<unknown> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetcher(url, { ...init, signal: controller.signal });\n if (!response.ok) throw new Error(response.status === 401 ? \"unauthorized\" : \"request failed\");\n return await response.json();\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function apiSnapshot(\n token: string,\n accountId: string,\n source: \"pat\" | \"oauth\",\n options: CodexProbeOptions,\n): Promise<UsageSnapshot> {\n const fetcher = options.fetch ?? globalThis.fetch;\n const raw = await fetchJson(\n fetcher,\n \"https://chatgpt.com/backend-api/wham/usage\",\n {\n headers: {\n Authorization: `Bearer ${token}`,\n \"ChatGPT-Account-Id\": accountId,\n },\n },\n options.timeoutMs ?? 5000,\n );\n return parseUsage(raw, source);\n}\n\nfunction appServerRpc(messages: RpcMessage[], timeoutMs = 5000): Promise<CliResponses> {\n return new Promise((resolve, reject) => {\n const child = spawn(\"codex\", CODEX_APP_SERVER_ARGS, {\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n const results: Partial<CliResponses> = {};\n let buffer = \"\";\n let settled = false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n child.kill();\n if (error) reject(error);\n else resolve(results as CliResponses);\n };\n const timer = setTimeout(() => finish(new Error(\"codex probe timed out\")), timeoutMs);\n child.once(\"error\", () => finish(new Error(\"codex app-server unavailable\")));\n child.once(\"exit\", () => {\n if (!settled) finish(new Error(\"codex app-server exited\"));\n });\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n buffer += chunk;\n for (;;) {\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) break;\n const line = buffer.slice(0, newline).trim();\n buffer = buffer.slice(newline + 1);\n if (!line) continue;\n let message: UnknownRecord;\n try {\n message = JSON.parse(line) as UnknownRecord;\n } catch {\n continue;\n }\n if (message.id === 1) {\n for (const request of messages.slice(1))\n child.stdin.write(`${JSON.stringify(request)}\\n`);\n } else if (message.id === 2) {\n if (message.error) finish(new Error(\"codex rate-limit method failed\"));\n else results.rateLimits = message.result;\n } else if (message.id === 3) {\n if (message.error) finish(new Error(\"codex account method failed\"));\n else results.account = message.result;\n }\n if (\"rateLimits\" in results && \"account\" in results) finish();\n }\n });\n child.stdin.write(`${JSON.stringify(messages[0])}\\n`);\n });\n}\n\nexport function codexUnavailableReport(checkedAt: string): CapacityReport {\n return {\n harness: \"codex\",\n provider: \"openai\",\n generatedAt: checkedAt,\n authenticated: null,\n available: \"unknown\",\n windows: [],\n creditsRemaining: null,\n };\n}\n\nfunction unavailable(options: CodexProbeOptions): CapacityReport {\n return codexUnavailableReport(options.checkedAt);\n}\n\nasync function cliFallback(options: CodexProbeOptions): Promise<CapacityReport> {\n if (!options.installed) return unavailable(options);\n const messages: RpcMessage[] = [\n {\n id: 1,\n method: \"initialize\",\n params: {\n clientInfo: { name: \"ai-devkit\", title: null, version: \"1\" },\n capabilities: null,\n },\n },\n { method: \"initialized\" },\n { id: 2, method: \"account/rateLimits/read\" },\n { id: 3, method: \"account/read\" },\n ];\n try {\n const rpc = options.rpc ?? ((requests) => appServerRpc(requests, options.timeoutMs));\n const response = await rpc(messages);\n const result = capacityFromSnapshot(\n parseCliUsage(response.rateLimits),\n options,\n response.rateLimits,\n );\n const accountEnvelope = record(response.account);\n if (\n accountEnvelope &&\n Object.hasOwn(accountEnvelope, \"account\") &&\n !record(accountEnvelope.account)\n ) {\n result.authenticated = false;\n result.available = \"unknown\";\n }\n return result;\n } catch {\n return unavailable(options);\n }\n}\n\nexport async function probeCodexCapacity(options: CodexProbeOptions): Promise<CapacityReport> {\n let parsed: UnknownRecord | null = null;\n try {\n const contents = await (options.readFile ?? readFile)(\n resolveCodexAuthPath(options.env),\n \"utf8\",\n );\n parsed = record(JSON.parse(contents));\n } catch {\n return cliFallback(options);\n }\n\n const auth = parsed ?? {};\n const pat = nonEmptyText(auth.personal_access_token);\n if (pat) {\n try {\n const fetcher = options.fetch ?? globalThis.fetch;\n const whoami = record(\n await fetchJson(\n fetcher,\n \"https://auth.openai.com/api/accounts/v1/user-auth-credential/whoami\",\n { headers: { Authorization: `Bearer ${pat}` } },\n options.timeoutMs ?? 5000,\n ),\n );\n const accountId = nonEmptyText(whoami?.chatgpt_account_id);\n if (!accountId) throw new Error(\"account unavailable\");\n return capacityFromSnapshot(await apiSnapshot(pat, accountId, \"pat\", options), options);\n } catch {\n // Continue to a separately available OAuth credential before using the CLI.\n }\n }\n\n const tokens = record(auth.tokens);\n const accessToken = nonEmptyText(tokens?.access_token);\n const accountId = nonEmptyText(tokens?.account_id);\n if (\n tokens &&\n accessToken &&\n accountId &&\n !staleOAuth(tokens, accessToken, (options.now ?? (() => new Date()))())\n ) {\n try {\n return capacityFromSnapshot(\n await apiSnapshot(accessToken, accountId, \"oauth\", options),\n options,\n );\n } catch {\n return cliFallback(options);\n }\n }\n return cliFallback(options);\n}\n"],"names":["spawn","readFile","join","parseWhamUsage","resetTime","safeIdentifier","toRateWindow","CODEX_APP_SERVER_ARGS","record","value","Array","isArray","finiteNumber","Number","isFinite","nonEmptyText","length","resolveCodexAuthPath","env","process","root","CODEX_HOME","HOME","parseUsage","raw","source","windows","creditsRemaining","cliWindow","id","label","input","durationMinutes","windowDurationMins","usedPercent","resetsAt","cliSnapshotWindows","fallbackId","snapshot","scope","limitId","primary","secondary","filter","item","parseCliUsage","response","rateLimits","buckets","rateLimitsByLimitId","Object","entries","push","unique","Map","map","window","values","capacityFromSnapshot","context","hasUsage","some","reached","rateLimitReachedType","resetCredits","rateLimitResetCredits","usageLimitResetCredits","harness","provider","generatedAt","checkedAt","authenticated","available","availableCount","jwtExpiry","token","part","split","JSON","parse","Buffer","from","toString","exp","staleOAuth","tokens","now","metadata","expires_at","expiresAt","expiry","parsed","Date","isNaN","getTime","fetchJson","fetcher","url","init","timeoutMs","controller","AbortController","timer","setTimeout","abort","signal","ok","Error","status","json","clearTimeout","apiSnapshot","accountId","options","fetch","globalThis","headers","Authorization","appServerRpc","messages","Promise","resolve","reject","child","stdio","results","buffer","settled","finish","error","kill","once","stdout","setEncoding","on","chunk","newline","indexOf","line","slice","trim","message","request","stdin","write","stringify","result","account","codexUnavailableReport","unavailable","cliFallback","installed","method","params","clientInfo","name","title","version","capabilities","rpc","requests","accountEnvelope","hasOwn","probeCodexCapacity","contents","auth","pat","personal_access_token","whoami","chatgpt_account_id","accessToken","access_token","account_id"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { probeCodexCapacity } from "./codex.js";
2
+ import { type ClaudeCapacityOptions } from "./claude.js";
2
3
  import type { CapacityReport } from "./types.js";
3
4
  export type { CapacityReport, CapacityWindow } from "./types.js";
5
+ export type { ClaudeCapacityOptions } from "./claude.js";
4
6
  export type CapacityProbeOptions = {
5
7
  now?: () => Date;
6
8
  path?: string;
@@ -14,6 +16,15 @@ export type ZaiCapacityOptions = {
14
16
  fetch?: typeof globalThis.fetch;
15
17
  timeoutMs?: number;
16
18
  };
19
+ export type OpenAiCapacityOptions = {
20
+ now?: () => Date;
21
+ env?: NodeJS.ProcessEnv;
22
+ readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
23
+ fetch?: typeof globalThis.fetch;
24
+ timeoutMs?: number;
25
+ };
17
26
  export declare function getCodexCapacityReport(options?: CapacityProbeOptions): Promise<CapacityReport>;
18
27
  export declare function getZaiCapacityReport(options?: ZaiCapacityOptions): Promise<CapacityReport>;
28
+ export declare function getOpenAiCapacityReport(options?: OpenAiCapacityOptions): Promise<CapacityReport>;
29
+ export declare function getClaudeCapacityReport(options?: ClaudeCapacityOptions): Promise<CapacityReport>;
19
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capacity/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,OAAO,kBAAkB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+BF,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,CAAC,CAczB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAMzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capacity/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAG9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,OAAO,kBAAkB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+BF,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAMzB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAMzB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAOzB"}
@@ -2,6 +2,8 @@ import { constants } from "node:fs";
2
2
  import { access as fsAccess } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { codexUnavailableReport, probeCodexCapacity } from "./codex.js";
5
+ import { probeClaudeCapacity } from "./claude.js";
6
+ import { probeOpenAiCapacity } from "./openai.js";
5
7
  import { probeZaiCapacity } from "./zai.js";
6
8
  async function canAccess(target, mode) {
7
9
  try {
@@ -46,5 +48,20 @@ export async function getZaiCapacityReport(options = {}) {
46
48
  checkedAt: (now?.() ?? new Date()).toISOString()
47
49
  });
48
50
  }
51
+ export async function getOpenAiCapacityReport(options = {}) {
52
+ const { now, ...probeOptions } = options;
53
+ return probeOpenAiCapacity({
54
+ ...probeOptions,
55
+ checkedAt: (now?.() ?? new Date()).toISOString()
56
+ });
57
+ }
58
+ export async function getClaudeCapacityReport(options = {}) {
59
+ const { now, ...probeOptions } = options;
60
+ return probeClaudeCapacity({
61
+ ...probeOptions,
62
+ checkedAt: (now?.() ?? new Date()).toISOString(),
63
+ now
64
+ });
65
+ }
49
66
 
50
67
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./codex.js\";\nimport { probeZaiCapacity } from \"./zai.js\";\nimport type { CapacityReport } from \"./types.js\";\n\nexport type { CapacityReport, CapacityWindow } from \"./types.js\";\n\nexport type CapacityProbeOptions = {\n now?: () => Date;\n path?: string;\n access?: (target: string) => Promise<void>;\n probe?: typeof probeCodexCapacity;\n};\n\nexport type ZaiCapacityOptions = {\n now?: () => Date;\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nasync function canAccess(target: string, mode: number): Promise<boolean> {\n try {\n await fsAccess(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isCodexInstalled(\n pathValue: string,\n checkAccess?: (target: string) => Promise<void>,\n): Promise<boolean> {\n const directories = pathValue.split(path.delimiter).filter(Boolean);\n for (const directory of directories) {\n const executable = path.join(directory, \"codex\");\n if (checkAccess) {\n try {\n await checkAccess(executable);\n return true;\n } catch {\n continue;\n }\n }\n if (await canAccess(executable, constants.X_OK)) return true;\n }\n return false;\n}\n\nexport async function getCodexCapacityReport(\n options: CapacityProbeOptions = {},\n): Promise<CapacityReport> {\n const generatedAt = (options.now?.() ?? new Date()).toISOString();\n const installed = await isCodexInstalled(\n options.path ?? process.env.PATH ?? \"\",\n options.access,\n );\n try {\n return await (options.probe ?? probeCodexCapacity)({\n installed,\n checkedAt: generatedAt,\n });\n } catch {\n return codexUnavailableReport(generatedAt);\n }\n}\n\nexport async function getZaiCapacityReport(\n options: ZaiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeZaiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n"],"names":["constants","access","fsAccess","path","codexUnavailableReport","probeCodexCapacity","probeZaiCapacity","canAccess","target","mode","isCodexInstalled","pathValue","checkAccess","directories","split","delimiter","filter","Boolean","directory","executable","join","X_OK","getCodexCapacityReport","options","generatedAt","now","Date","toISOString","installed","process","env","PATH","probe","checkedAt","getZaiCapacityReport","probeOptions"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,UAAUC,QAAQ,QAAQ,mBAAmB;AACtD,OAAOC,UAAU,YAAY;AAC7B,SAASC,sBAAsB,EAAEC,kBAAkB,QAAQ,aAAa;AACxE,SAASC,gBAAgB,QAAQ,WAAW;AAoB5C,eAAeC,UAAUC,MAAc,EAAEC,IAAY;IACnD,IAAI;QACF,MAAMP,SAASM,QAAQC;QACvB,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeC,iBACbC,SAAiB,EACjBC,WAA+C;IAE/C,MAAMC,cAAcF,UAAUG,KAAK,CAACX,KAAKY,SAAS,EAAEC,MAAM,CAACC;IAC3D,KAAK,MAAMC,aAAaL,YAAa;QACnC,MAAMM,aAAahB,KAAKiB,IAAI,CAACF,WAAW;QACxC,IAAIN,aAAa;YACf,IAAI;gBACF,MAAMA,YAAYO;gBAClB,OAAO;YACT,EAAE,OAAM;gBACN;YACF;QACF;QACA,IAAI,MAAMZ,UAAUY,YAAYnB,UAAUqB,IAAI,GAAG,OAAO;IAC1D;IACA,OAAO;AACT;AAEA,OAAO,eAAeC,uBACpBC,UAAgC,CAAC,CAAC;IAElC,MAAMC,cAAc,AAACD,CAAAA,QAAQE,GAAG,QAAQ,IAAIC,MAAK,EAAGC,WAAW;IAC/D,MAAMC,YAAY,MAAMlB,iBACtBa,QAAQpB,IAAI,IAAI0B,QAAQC,GAAG,CAACC,IAAI,IAAI,IACpCR,QAAQtB,MAAM;IAEhB,IAAI;QACF,OAAO,MAAM,AAACsB,CAAAA,QAAQS,KAAK,IAAI3B,kBAAiB,EAAG;YACjDuB;YACAK,WAAWT;QACb;IACF,EAAE,OAAM;QACN,OAAOpB,uBAAuBoB;IAChC;AACF;AAEA,OAAO,eAAeU,qBACpBX,UAA8B,CAAC,CAAC;IAEhC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOjB,iBAAiB;QACtB,GAAG6B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF"}
1
+ {"version":3,"sources":["../../src/capacity/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { codexUnavailableReport, probeCodexCapacity } from \"./codex.js\";\nimport { probeClaudeCapacity, type ClaudeCapacityOptions } from \"./claude.js\";\nimport { probeOpenAiCapacity } from \"./openai.js\";\nimport { probeZaiCapacity } from \"./zai.js\";\nimport type { CapacityReport } from \"./types.js\";\n\nexport type { CapacityReport, CapacityWindow } from \"./types.js\";\nexport type { ClaudeCapacityOptions } from \"./claude.js\";\n\nexport type CapacityProbeOptions = {\n now?: () => Date;\n path?: string;\n access?: (target: string) => Promise<void>;\n probe?: typeof probeCodexCapacity;\n};\n\nexport type ZaiCapacityOptions = {\n now?: () => Date;\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nexport type OpenAiCapacityOptions = {\n now?: () => Date;\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n};\n\nasync function canAccess(target: string, mode: number): Promise<boolean> {\n try {\n await fsAccess(target, mode);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isCodexInstalled(\n pathValue: string,\n checkAccess?: (target: string) => Promise<void>,\n): Promise<boolean> {\n const directories = pathValue.split(path.delimiter).filter(Boolean);\n for (const directory of directories) {\n const executable = path.join(directory, \"codex\");\n if (checkAccess) {\n try {\n await checkAccess(executable);\n return true;\n } catch {\n continue;\n }\n }\n if (await canAccess(executable, constants.X_OK)) return true;\n }\n return false;\n}\n\nexport async function getCodexCapacityReport(\n options: CapacityProbeOptions = {},\n): Promise<CapacityReport> {\n const generatedAt = (options.now?.() ?? new Date()).toISOString();\n const installed = await isCodexInstalled(options.path ?? process.env.PATH ?? \"\", options.access);\n try {\n return await (options.probe ?? probeCodexCapacity)({\n installed,\n checkedAt: generatedAt,\n });\n } catch {\n return codexUnavailableReport(generatedAt);\n }\n}\n\nexport async function getZaiCapacityReport(\n options: ZaiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeZaiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getOpenAiCapacityReport(\n options: OpenAiCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeOpenAiCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n });\n}\n\nexport async function getClaudeCapacityReport(\n options: ClaudeCapacityOptions = {},\n): Promise<CapacityReport> {\n const { now, ...probeOptions } = options;\n return probeClaudeCapacity({\n ...probeOptions,\n checkedAt: (now?.() ?? new Date()).toISOString(),\n now,\n });\n}\n"],"names":["constants","access","fsAccess","path","codexUnavailableReport","probeCodexCapacity","probeClaudeCapacity","probeOpenAiCapacity","probeZaiCapacity","canAccess","target","mode","isCodexInstalled","pathValue","checkAccess","directories","split","delimiter","filter","Boolean","directory","executable","join","X_OK","getCodexCapacityReport","options","generatedAt","now","Date","toISOString","installed","process","env","PATH","probe","checkedAt","getZaiCapacityReport","probeOptions","getOpenAiCapacityReport","getClaudeCapacityReport"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,UAAUC,QAAQ,QAAQ,mBAAmB;AACtD,OAAOC,UAAU,YAAY;AAC7B,SAASC,sBAAsB,EAAEC,kBAAkB,QAAQ,aAAa;AACxE,SAASC,mBAAmB,QAAoC,cAAc;AAC9E,SAASC,mBAAmB,QAAQ,cAAc;AAClD,SAASC,gBAAgB,QAAQ,WAAW;AA6B5C,eAAeC,UAAUC,MAAc,EAAEC,IAAY;IACnD,IAAI;QACF,MAAMT,SAASQ,QAAQC;QACvB,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeC,iBACbC,SAAiB,EACjBC,WAA+C;IAE/C,MAAMC,cAAcF,UAAUG,KAAK,CAACb,KAAKc,SAAS,EAAEC,MAAM,CAACC;IAC3D,KAAK,MAAMC,aAAaL,YAAa;QACnC,MAAMM,aAAalB,KAAKmB,IAAI,CAACF,WAAW;QACxC,IAAIN,aAAa;YACf,IAAI;gBACF,MAAMA,YAAYO;gBAClB,OAAO;YACT,EAAE,OAAM;gBACN;YACF;QACF;QACA,IAAI,MAAMZ,UAAUY,YAAYrB,UAAUuB,IAAI,GAAG,OAAO;IAC1D;IACA,OAAO;AACT;AAEA,OAAO,eAAeC,uBACpBC,UAAgC,CAAC,CAAC;IAElC,MAAMC,cAAc,AAACD,CAAAA,QAAQE,GAAG,QAAQ,IAAIC,MAAK,EAAGC,WAAW;IAC/D,MAAMC,YAAY,MAAMlB,iBAAiBa,QAAQtB,IAAI,IAAI4B,QAAQC,GAAG,CAACC,IAAI,IAAI,IAAIR,QAAQxB,MAAM;IAC/F,IAAI;QACF,OAAO,MAAM,AAACwB,CAAAA,QAAQS,KAAK,IAAI7B,kBAAiB,EAAG;YACjDyB;YACAK,WAAWT;QACb;IACF,EAAE,OAAM;QACN,OAAOtB,uBAAuBsB;IAChC;AACF;AAEA,OAAO,eAAeU,qBACpBX,UAA8B,CAAC,CAAC;IAEhC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOjB,iBAAiB;QACtB,GAAG6B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeS,wBACpBb,UAAiC,CAAC,CAAC;IAEnC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOlB,oBAAoB;QACzB,GAAG8B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;IAChD;AACF;AAEA,OAAO,eAAeU,wBACpBd,UAAiC,CAAC,CAAC;IAEnC,MAAM,EAAEE,GAAG,EAAE,GAAGU,cAAc,GAAGZ;IACjC,OAAOnB,oBAAoB;QACzB,GAAG+B,YAAY;QACfF,WAAW,AAACR,CAAAA,WAAW,IAAIC,MAAK,EAAGC,WAAW;QAC9CF;IACF;AACF"}