@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +57744 -0
  2. package/native-manifest.json +1414 -0
  3. package/package.json +254 -0
  4. package/plugins/abacus-ai/icon.svg +18 -0
  5. package/plugins/abacus-ai/plugin.js +121 -0
  6. package/plugins/abacus-ai/plugin.json +17 -0
  7. package/plugins/alibaba/icon-color.svg +1 -0
  8. package/plugins/alibaba/icon.svg +1 -0
  9. package/plugins/alibaba/plugin.js +322 -0
  10. package/plugins/alibaba/plugin.json +20 -0
  11. package/plugins/alibaba-token-plan/icon-color.svg +1 -0
  12. package/plugins/alibaba-token-plan/icon.svg +1 -0
  13. package/plugins/alibaba-token-plan/plugin.js +544 -0
  14. package/plugins/alibaba-token-plan/plugin.json +19 -0
  15. package/plugins/amp/icon-color.svg +1 -0
  16. package/plugins/amp/icon-conflict.svg +6 -0
  17. package/plugins/amp/icon.svg +1 -0
  18. package/plugins/amp/plugin.js +175 -0
  19. package/plugins/amp/plugin.json +16 -0
  20. package/plugins/antigravity/icon-color.svg +1 -0
  21. package/plugins/antigravity/icon-conflict.svg +3 -0
  22. package/plugins/antigravity/icon.svg +1 -0
  23. package/plugins/antigravity/plugin.js +739 -0
  24. package/plugins/antigravity/plugin.json +31 -0
  25. package/plugins/antigravity-cli/icon.svg +3 -0
  26. package/plugins/antigravity-cli/plugin.js +570 -0
  27. package/plugins/antigravity-cli/plugin.json +16 -0
  28. package/plugins/antigravity-ide/icon.svg +3 -0
  29. package/plugins/antigravity-ide/plugin.js +257 -0
  30. package/plugins/antigravity-ide/plugin.json +29 -0
  31. package/plugins/augment/icon.svg +14 -0
  32. package/plugins/augment/plugin.js +59 -0
  33. package/plugins/augment/plugin.json +18 -0
  34. package/plugins/aws-bedrock/icon-color.svg +1 -0
  35. package/plugins/aws-bedrock/icon.svg +1 -0
  36. package/plugins/aws-bedrock/plugin.js +206 -0
  37. package/plugins/aws-bedrock/plugin.json +18 -0
  38. package/plugins/azure-openai/icon-color.svg +1 -0
  39. package/plugins/azure-openai/icon.svg +1 -0
  40. package/plugins/azure-openai/plugin.js +145 -0
  41. package/plugins/azure-openai/plugin.json +17 -0
  42. package/plugins/chutes/icon.svg +7 -0
  43. package/plugins/chutes/plugin.js +236 -0
  44. package/plugins/chutes/plugin.json +19 -0
  45. package/plugins/claude/icon-color.svg +1 -0
  46. package/plugins/claude/icon.svg +1 -0
  47. package/plugins/claude/plugin.js +1723 -0
  48. package/plugins/claude/plugin.json +30 -0
  49. package/plugins/codebuff/icon.svg +92 -0
  50. package/plugins/codebuff/plugin.js +113 -0
  51. package/plugins/codebuff/plugin.json +17 -0
  52. package/plugins/codex/icon-color.svg +5 -0
  53. package/plugins/codex/icon.svg +1 -0
  54. package/plugins/codex/plugin.js +1009 -0
  55. package/plugins/codex/plugin.json +82 -0
  56. package/plugins/command-code/icon.svg +3 -0
  57. package/plugins/command-code/plugin.js +184 -0
  58. package/plugins/command-code/plugin.json +20 -0
  59. package/plugins/copilot/icon-color.svg +1 -0
  60. package/plugins/copilot/icon.svg +1 -0
  61. package/plugins/copilot/plugin.js +684 -0
  62. package/plugins/copilot/plugin.json +21 -0
  63. package/plugins/crof/icon.svg +3 -0
  64. package/plugins/crof/plugin.js +53 -0
  65. package/plugins/crof/plugin.json +17 -0
  66. package/plugins/crofai/icon.svg +16 -0
  67. package/plugins/crofai/plugin.js +81 -0
  68. package/plugins/crofai/plugin.json +18 -0
  69. package/plugins/cursor/icon-conflict.svg +3 -0
  70. package/plugins/cursor/icon.svg +1 -0
  71. package/plugins/cursor/plugin.js +1379 -0
  72. package/plugins/cursor/plugin.json +23 -0
  73. package/plugins/cursor-nightly/icon.png +0 -0
  74. package/plugins/cursor-nightly/plugin.js +1379 -0
  75. package/plugins/cursor-nightly/plugin.json +23 -0
  76. package/plugins/deepgram/icon.svg +1 -0
  77. package/plugins/deepgram/plugin.js +20 -0
  78. package/plugins/deepgram/plugin.json +17 -0
  79. package/plugins/deepseek/icon-color.svg +1 -0
  80. package/plugins/deepseek/icon.svg +1 -0
  81. package/plugins/deepseek/plugin.js +71 -0
  82. package/plugins/deepseek/plugin.json +17 -0
  83. package/plugins/devin/icon.svg +5 -0
  84. package/plugins/devin/plugin.js +341 -0
  85. package/plugins/devin/plugin.json +16 -0
  86. package/plugins/doubao/icon-color.svg +1 -0
  87. package/plugins/doubao/icon.svg +1 -0
  88. package/plugins/doubao/plugin.js +92 -0
  89. package/plugins/doubao/plugin.json +17 -0
  90. package/plugins/droid/icon.svg +1 -0
  91. package/plugins/droid/plugin.json +20 -0
  92. package/plugins/elevenlabs/icon.svg +4 -0
  93. package/plugins/elevenlabs/plugin.js +48 -0
  94. package/plugins/elevenlabs/plugin.json +18 -0
  95. package/plugins/factory/icon-conflict.svg +1 -0
  96. package/plugins/factory/icon.svg +1 -0
  97. package/plugins/factory/plugin.js +487 -0
  98. package/plugins/factory/plugin.json +15 -0
  99. package/plugins/fireworks-ai/icon.svg +6 -0
  100. package/plugins/fireworks-ai/plugin.js +370 -0
  101. package/plugins/fireworks-ai/plugin.json +19 -0
  102. package/plugins/gemini/icon-color.svg +1 -0
  103. package/plugins/gemini/icon.svg +1 -0
  104. package/plugins/gemini/plugin.js +460 -0
  105. package/plugins/gemini/plugin.json +16 -0
  106. package/plugins/grok/icon.svg +1 -0
  107. package/plugins/grok/plugin.js +297 -0
  108. package/plugins/grok/plugin.json +19 -0
  109. package/plugins/groqcloud/icon.svg +1 -0
  110. package/plugins/groqcloud/plugin.js +117 -0
  111. package/plugins/groqcloud/plugin.json +19 -0
  112. package/plugins/jetbrains-ai-assistant/icon-color.svg +14 -0
  113. package/plugins/jetbrains-ai-assistant/icon-conflict.svg +3 -0
  114. package/plugins/jetbrains-ai-assistant/icon.svg +14 -0
  115. package/plugins/jetbrains-ai-assistant/plugin.js +313 -0
  116. package/plugins/jetbrains-ai-assistant/plugin.json +16 -0
  117. package/plugins/kilo/icon.svg +1 -0
  118. package/plugins/kilo/plugin.js +111 -0
  119. package/plugins/kilo/plugin.json +19 -0
  120. package/plugins/kimi/icon-color.svg +1 -0
  121. package/plugins/kimi/icon-conflict.svg +3 -0
  122. package/plugins/kimi/icon.svg +1 -0
  123. package/plugins/kimi/plugin.js +358 -0
  124. package/plugins/kimi/plugin.json +15 -0
  125. package/plugins/kimi-k2/icon-color.svg +1 -0
  126. package/plugins/kimi-k2/icon.svg +1 -0
  127. package/plugins/kimi-k2/plugin.js +58 -0
  128. package/plugins/kimi-k2/plugin.json +18 -0
  129. package/plugins/kiro/icon-color.svg +5 -0
  130. package/plugins/kiro/icon-conflict.svg +1 -0
  131. package/plugins/kiro/icon.svg +4 -0
  132. package/plugins/kiro/plugin.js +390 -0
  133. package/plugins/kiro/plugin.json +16 -0
  134. package/plugins/litellm/icon.svg +16 -0
  135. package/plugins/litellm/plugin.js +199 -0
  136. package/plugins/litellm/plugin.json +16 -0
  137. package/plugins/llm-proxy/icon.svg +1 -0
  138. package/plugins/llm-proxy/plugin.js +40 -0
  139. package/plugins/llm-proxy/plugin.json +17 -0
  140. package/plugins/manus/icon.svg +6 -0
  141. package/plugins/manus/plugin.js +50 -0
  142. package/plugins/manus/plugin.json +17 -0
  143. package/plugins/mimo/icon.svg +1 -0
  144. package/plugins/mimo/plugin.js +177 -0
  145. package/plugins/mimo/plugin.json +19 -0
  146. package/plugins/minimax/icon-color.svg +1 -0
  147. package/plugins/minimax/icon-conflict.svg +4 -0
  148. package/plugins/minimax/icon.svg +1 -0
  149. package/plugins/minimax/plugin.js +453 -0
  150. package/plugins/minimax/plugin.json +14 -0
  151. package/plugins/mistral/icon-color.svg +1 -0
  152. package/plugins/mistral/icon.svg +1 -0
  153. package/plugins/mistral/plugin.js +152 -0
  154. package/plugins/mistral/plugin.json +18 -0
  155. package/plugins/moonshot/icon.svg +1 -0
  156. package/plugins/moonshot/plugin.js +43 -0
  157. package/plugins/moonshot/plugin.json +16 -0
  158. package/plugins/nanogpt/icon-color.svg +17 -0
  159. package/plugins/nanogpt/icon.svg +10 -0
  160. package/plugins/nanogpt/plugin.js +80 -0
  161. package/plugins/nanogpt/plugin.json +17 -0
  162. package/plugins/neuralwatt/icon.svg +3 -0
  163. package/plugins/neuralwatt/plugin.js +152 -0
  164. package/plugins/neuralwatt/plugin.json +17 -0
  165. package/plugins/ollama/icon.svg +1 -0
  166. package/plugins/ollama/plugin.js +92 -0
  167. package/plugins/ollama/plugin.json +20 -0
  168. package/plugins/openai-api/icon.svg +1 -0
  169. package/plugins/openai-api/plugin.js +533 -0
  170. package/plugins/openai-api/plugin.json +26 -0
  171. package/plugins/opencode/icon.svg +1 -0
  172. package/plugins/opencode/plugin.js +342 -0
  173. package/plugins/opencode/plugin.json +19 -0
  174. package/plugins/opencode-go/icon-conflict.svg +1 -0
  175. package/plugins/opencode-go/icon.svg +1 -0
  176. package/plugins/opencode-go/plugin.js +617 -0
  177. package/plugins/opencode-go/plugin.json +50 -0
  178. package/plugins/openrouter/icon.svg +1 -0
  179. package/plugins/openrouter/plugin.js +156 -0
  180. package/plugins/openrouter/plugin.json +8 -0
  181. package/plugins/perplexity/icon-color.svg +1 -0
  182. package/plugins/perplexity/icon-conflict.svg +1 -0
  183. package/plugins/perplexity/icon.svg +1 -0
  184. package/plugins/perplexity/plugin.js +425 -0
  185. package/plugins/perplexity/plugin.json +19 -0
  186. package/plugins/poe/icon.svg +1 -0
  187. package/plugins/poe/plugin.js +190 -0
  188. package/plugins/poe/plugin.json +19 -0
  189. package/plugins/stepfun/icon-color.svg +1 -0
  190. package/plugins/stepfun/icon.svg +1 -0
  191. package/plugins/stepfun/plugin.js +155 -0
  192. package/plugins/stepfun/plugin.json +18 -0
  193. package/plugins/synthetic/icon-conflict.svg +12 -0
  194. package/plugins/synthetic/icon.svg +14 -0
  195. package/plugins/synthetic/plugin.js +331 -0
  196. package/plugins/synthetic/plugin.json +23 -0
  197. package/plugins/t3chat/icon-color.svg +28 -0
  198. package/plugins/t3chat/icon.svg +4 -0
  199. package/plugins/t3chat/plugin.js +243 -0
  200. package/plugins/t3chat/plugin.json +20 -0
  201. package/plugins/venice/icon-color.svg +1 -0
  202. package/plugins/venice/icon.svg +1 -0
  203. package/plugins/venice/plugin.js +69 -0
  204. package/plugins/venice/plugin.json +16 -0
  205. package/plugins/vertex-ai/icon-color.svg +1 -0
  206. package/plugins/vertex-ai/icon.svg +1 -0
  207. package/plugins/vertex-ai/plugin.js +172 -0
  208. package/plugins/vertex-ai/plugin.json +18 -0
  209. package/plugins/warp/icon-color.svg +10 -0
  210. package/plugins/warp/icon.svg +6 -0
  211. package/plugins/warp/plugin.js +108 -0
  212. package/plugins/warp/plugin.json +18 -0
  213. package/plugins/windsurf/icon-conflict.svg +3 -0
  214. package/plugins/windsurf/icon.svg +1 -0
  215. package/plugins/windsurf/plugin.js +208 -0
  216. package/plugins/windsurf/plugin.json +16 -0
  217. package/plugins/zai/icon-conflict.svg +5 -0
  218. package/plugins/zai/icon.svg +1 -0
  219. package/plugins/zai/plugin.js +188 -0
  220. package/plugins/zai/plugin.json +16 -0
  221. package/plugins/zed/icon.svg +4 -0
  222. package/plugins/zed/plugin.js +270 -0
  223. package/plugins/zed/plugin.json +19 -0
  224. package/usagestat +0 -0
  225. package/usagestatd +0 -0
@@ -0,0 +1,533 @@
1
+ (function () {
2
+ var CREDIT_GRANTS_URL = "https://api.openai.com/v1/dashboard/billing/credit_grants";
3
+ var ORG_COSTS_URL = "https://api.openai.com/v1/organization/costs";
4
+ var ORG_COMPLETIONS_URL = "https://api.openai.com/v1/organization/usage/completions";
5
+ var HISTORY_DAYS = 30;
6
+ var MAX_PAGES = 100;
7
+
8
+ function trim(value) {
9
+ return typeof value === "string" && value.trim() ? value.trim() : null;
10
+ }
11
+
12
+ function clean(value) {
13
+ var v = trim(value);
14
+ if (!v) return null;
15
+ if ((v[0] === '"' && v[v.length - 1] === '"') || (v[0] === "'" && v[v.length - 1] === "'")) {
16
+ v = v.slice(1, -1).trim();
17
+ }
18
+ return v || null;
19
+ }
20
+
21
+ function env(ctx, name) {
22
+ try {
23
+ return clean(ctx.host.env.get(name));
24
+ } catch (_) {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function setting(ctx, names) {
30
+ var settings = ctx.provider && ctx.provider.settings ? ctx.provider.settings : {};
31
+ for (var i = 0; i < names.length; i++) {
32
+ var value = clean(settings[names[i]]);
33
+ if (value) return value;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ function loadCredential(ctx) {
39
+ var configured = clean(ctx.provider && ctx.provider.apiKey);
40
+ if (configured) {
41
+ return {
42
+ apiKey: configured,
43
+ usesAdminKey: true,
44
+ projectId: loadProjectId(ctx),
45
+ };
46
+ }
47
+
48
+ var names = [
49
+ "OPENAI_ADMIN_KEY",
50
+ "OPENAI_ADMIN_API_KEY",
51
+ "OPENAI_API_KEY",
52
+ "OPENAI_PLATFORM_API_KEY",
53
+ ];
54
+ for (var i = 0; i < names.length; i++) {
55
+ var value = env(ctx, names[i]);
56
+ if (value) {
57
+ return {
58
+ apiKey: value,
59
+ usesAdminKey: names[i] === "OPENAI_ADMIN_KEY" || names[i] === "OPENAI_ADMIN_API_KEY",
60
+ projectId: loadProjectId(ctx),
61
+ };
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+
67
+ function loadProjectId(ctx) {
68
+ return setting(ctx, ["projectId", "projectID", "project", "openaiProjectId"]) ||
69
+ clean(ctx.provider && ctx.provider.workspaceId) ||
70
+ env(ctx, "OPENAI_PROJECT_ID");
71
+ }
72
+
73
+ function numberValue(value) {
74
+ if (typeof value === "number" && Number.isFinite(value)) return value;
75
+ if (typeof value === "string" && value.trim()) {
76
+ var parsed = Number(value.trim().replace(/,/g, ""));
77
+ if (Number.isFinite(parsed)) return parsed;
78
+ }
79
+ return null;
80
+ }
81
+
82
+ function intValue(value) {
83
+ var n = numberValue(value);
84
+ return n === null ? 0 : Math.max(0, Math.floor(n));
85
+ }
86
+
87
+ function formatUsd(value) {
88
+ var n = Number(value) || 0;
89
+ return "$" + n.toFixed(2);
90
+ }
91
+
92
+ function fmtCount(n, suffix) {
93
+ var value = Number(n) || 0;
94
+ var abs = Math.abs(value);
95
+ if (abs >= 1000000000) return (value / 1000000000).toFixed(1).replace(/\.0$/, "") + "B " + suffix;
96
+ if (abs >= 1000000) return (value / 1000000).toFixed(1).replace(/\.0$/, "") + "M " + suffix;
97
+ if (abs >= 1000) return Math.round(value).toLocaleString("en-US") + " " + suffix;
98
+ return String(Math.round(value)) + " " + suffix;
99
+ }
100
+
101
+ function dayKeyFromUtcMs(ms) {
102
+ return new Date(ms).toISOString().slice(0, 10);
103
+ }
104
+
105
+ function dayKeyFromSec(sec) {
106
+ return dayKeyFromUtcMs(sec * 1000);
107
+ }
108
+
109
+ function shortDayLabel(day) {
110
+ return Number(day.slice(5, 7)) + "/" + Number(day.slice(8, 10));
111
+ }
112
+
113
+ function adminRanges(nowMs) {
114
+ var date = new Date(nowMs);
115
+ var todayStartMs = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
116
+ var startMs = todayStartMs - (HISTORY_DAYS - 1) * 24 * 60 * 60 * 1000;
117
+ var endMs = todayStartMs + 24 * 60 * 60 * 1000;
118
+ return [{
119
+ startTime: Math.floor(startMs / 1000),
120
+ endTime: Math.floor(endMs / 1000),
121
+ limit: HISTORY_DAYS,
122
+ }];
123
+ }
124
+
125
+ function makeUrl(baseUrl, params) {
126
+ var query = [];
127
+ var keys = Object.keys(params);
128
+ for (var i = 0; i < keys.length; i++) {
129
+ var key = keys[i];
130
+ var value = params[key];
131
+ if (value === null || value === undefined || value === "") continue;
132
+ query.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(value)));
133
+ }
134
+ return baseUrl + (query.length ? "?" + query.join("&") : "");
135
+ }
136
+
137
+ function makeHttpError(message, status, endpoint) {
138
+ return {
139
+ message: message,
140
+ status: status,
141
+ endpoint: endpoint,
142
+ authRejected: status === 401 || status === 403,
143
+ };
144
+ }
145
+
146
+ function requestJson(ctx, url, apiKey, endpoint) {
147
+ var resp;
148
+ try {
149
+ resp = ctx.util.request({
150
+ method: "GET",
151
+ url: url,
152
+ headers: { Authorization: "Bearer " + apiKey, Accept: "application/json" },
153
+ timeoutMs: 20000,
154
+ });
155
+ } catch (e) {
156
+ throw makeHttpError("OpenAI API " + endpoint + " request failed: " + String(e), 0, endpoint);
157
+ }
158
+
159
+ if (ctx.util.isAuthStatus(resp.status)) {
160
+ throw makeHttpError("OpenAI API key invalid or missing required permissions.", resp.status, endpoint);
161
+ }
162
+ if (resp.status < 200 || resp.status >= 300) {
163
+ throw makeHttpError("OpenAI API " + endpoint + " error (HTTP " + resp.status + ").", resp.status, endpoint);
164
+ }
165
+
166
+ var json = ctx.util.tryParseJson(resp.bodyText);
167
+ if (!json) throw makeHttpError("Could not parse OpenAI API " + endpoint + " response.", resp.status, endpoint);
168
+ return json;
169
+ }
170
+
171
+ function fetchPagedBuckets(ctx, endpoint) {
172
+ var buckets = [];
173
+ var ranges = adminRanges(Date.now());
174
+ for (var r = 0; r < ranges.length; r++) {
175
+ var range = ranges[r];
176
+ var nextPage = null;
177
+ var seen = {};
178
+ var pageCount = 0;
179
+ do {
180
+ pageCount += 1;
181
+ if (pageCount > MAX_PAGES) {
182
+ throw makeHttpError("OpenAI API " + endpoint.name + " pagination exceeded " + MAX_PAGES + " pages.", 200, endpoint.name);
183
+ }
184
+
185
+ var params = {
186
+ start_time: range.startTime,
187
+ end_time: range.endTime,
188
+ bucket_width: "1d",
189
+ limit: range.limit,
190
+ group_by: endpoint.groupBy,
191
+ };
192
+ if (endpoint.projectId) params.project_ids = endpoint.projectId;
193
+ if (nextPage) params.page = nextPage;
194
+
195
+ var json = requestJson(ctx, makeUrl(endpoint.url, params), endpoint.apiKey, endpoint.name);
196
+ if (Array.isArray(json.data)) {
197
+ for (var i = 0; i < json.data.length; i++) buckets.push(json.data[i]);
198
+ }
199
+
200
+ if (!json.has_more) {
201
+ nextPage = null;
202
+ continue;
203
+ }
204
+ nextPage = clean(json.next_page);
205
+ if (!nextPage) {
206
+ throw makeHttpError("OpenAI API " + endpoint.name + " pagination cursor missing.", 200, endpoint.name);
207
+ }
208
+ if (seen[nextPage]) {
209
+ throw makeHttpError("OpenAI API " + endpoint.name + " pagination cursor repeated.", 200, endpoint.name);
210
+ }
211
+ seen[nextPage] = true;
212
+ } while (nextPage);
213
+ }
214
+ return buckets;
215
+ }
216
+
217
+ function displayName(raw, fallback) {
218
+ return clean(raw) || fallback;
219
+ }
220
+
221
+ function dailyBucket(map, startTime, endTime) {
222
+ var key = dayKeyFromSec(startTime);
223
+ if (!map[key]) {
224
+ map[key] = {
225
+ date: key,
226
+ startTime: startTime,
227
+ endTime: endTime,
228
+ costUSD: 0,
229
+ requests: 0,
230
+ inputTokens: 0,
231
+ cachedInputTokens: 0,
232
+ outputTokens: 0,
233
+ totalTokens: 0,
234
+ lineItems: {},
235
+ models: {},
236
+ };
237
+ }
238
+ if (endTime && (!map[key].endTime || endTime > map[key].endTime)) map[key].endTime = endTime;
239
+ return map[key];
240
+ }
241
+
242
+ function addModel(day, name, requests, input, cached, output, total) {
243
+ if (!day.models[name]) {
244
+ day.models[name] = {
245
+ modelName: name,
246
+ requestCount: 0,
247
+ inputTokens: 0,
248
+ cacheReadTokens: 0,
249
+ outputTokens: 0,
250
+ totalTokens: 0,
251
+ };
252
+ }
253
+ var model = day.models[name];
254
+ model.requestCount += requests;
255
+ model.inputTokens += input;
256
+ model.cacheReadTokens += cached;
257
+ model.outputTokens += output;
258
+ model.totalTokens += total;
259
+ }
260
+
261
+ function makeAdminDaily(costs, completions) {
262
+ var map = {};
263
+ for (var i = 0; i < costs.length; i++) {
264
+ var costBucket = costs[i] || {};
265
+ var start = intValue(costBucket.start_time);
266
+ if (!start) continue;
267
+ var day = dailyBucket(map, start, intValue(costBucket.end_time));
268
+ var results = Array.isArray(costBucket.results) ? costBucket.results : [];
269
+ for (var j = 0; j < results.length; j++) {
270
+ var result = results[j] || {};
271
+ var amount = result.amount && typeof result.amount === "object"
272
+ ? numberValue(result.amount.value)
273
+ : numberValue(result.amount);
274
+ amount = amount === null ? 0 : amount;
275
+ day.costUSD += amount;
276
+ var item = displayName(result.line_item, "API");
277
+ day.lineItems[item] = (day.lineItems[item] || 0) + amount;
278
+ }
279
+ }
280
+
281
+ for (var c = 0; c < completions.length; c++) {
282
+ var usageBucket = completions[c] || {};
283
+ var usageStart = intValue(usageBucket.start_time);
284
+ if (!usageStart) continue;
285
+ var usageDay = dailyBucket(map, usageStart, intValue(usageBucket.end_time));
286
+ var usageResults = Array.isArray(usageBucket.results) ? usageBucket.results : [];
287
+ for (var k = 0; k < usageResults.length; k++) {
288
+ var row = usageResults[k] || {};
289
+ var input = intValue(row.input_tokens) + intValue(row.input_audio_tokens);
290
+ var cached = intValue(row.input_cached_tokens);
291
+ var output = intValue(row.output_tokens) + intValue(row.output_audio_tokens);
292
+ var requests = intValue(row.num_model_requests);
293
+ var total = input + output;
294
+ usageDay.requests += requests;
295
+ usageDay.inputTokens += input;
296
+ usageDay.cachedInputTokens += cached;
297
+ usageDay.outputTokens += output;
298
+ usageDay.totalTokens += total;
299
+ addModel(
300
+ usageDay,
301
+ displayName(row.model, "Responses and Chat Completions"),
302
+ requests,
303
+ input,
304
+ cached,
305
+ output,
306
+ total
307
+ );
308
+ }
309
+ }
310
+
311
+ return Object.keys(map)
312
+ .sort()
313
+ .map(function (key) {
314
+ var day = map[key];
315
+ var modelBreakdowns = Object.keys(day.models)
316
+ .map(function (name) { return day.models[name]; })
317
+ .sort(function (a, b) {
318
+ return b.totalTokens - a.totalTokens || a.modelName.localeCompare(b.modelName);
319
+ });
320
+ var lineItems = Object.keys(day.lineItems)
321
+ .map(function (name) { return { name: name, costUSD: day.lineItems[name] }; })
322
+ .sort(function (a, b) {
323
+ return b.costUSD - a.costUSD || a.name.localeCompare(b.name);
324
+ });
325
+ return {
326
+ date: day.date,
327
+ inputTokens: day.inputTokens,
328
+ outputTokens: day.outputTokens,
329
+ cacheReadTokens: day.cachedInputTokens,
330
+ totalTokens: day.totalTokens,
331
+ requestCount: day.requests,
332
+ costUSD: day.costUSD,
333
+ totalCost: day.costUSD,
334
+ modelsUsed: modelBreakdowns.map(function (model) { return model.modelName; }),
335
+ modelBreakdowns: modelBreakdowns,
336
+ lineItems: lineItems,
337
+ };
338
+ });
339
+ }
340
+
341
+ function summarize(daily) {
342
+ var summary = { costUSD: 0, tokens: 0, requests: 0 };
343
+ for (var i = 0; i < daily.length; i++) {
344
+ summary.costUSD += Number(daily[i].costUSD) || 0;
345
+ summary.tokens += Number(daily[i].totalTokens) || 0;
346
+ summary.requests += Number(daily[i].requestCount) || 0;
347
+ }
348
+ return summary;
349
+ }
350
+
351
+ function findDay(daily, key) {
352
+ for (var i = 0; i < daily.length; i++) {
353
+ if (daily[i].date === key) return daily[i];
354
+ }
355
+ return null;
356
+ }
357
+
358
+ function usageLabel(summary) {
359
+ var parts = [formatUsd(summary.costUSD)];
360
+ parts.push(fmtCount(summary.tokens, "tokens"));
361
+ parts.push(fmtCount(summary.requests, "requests"));
362
+ return parts.join(" · ");
363
+ }
364
+
365
+ function pushDayLine(lines, ctx, label, day) {
366
+ lines.push(ctx.line.text({
367
+ label: label,
368
+ value: usageLabel(day ? summarize([day]) : { costUSD: 0, tokens: 0, requests: 0 }),
369
+ }));
370
+ }
371
+
372
+ function collectUsageChartPoints(daily) {
373
+ var hasCost = daily.some(function (day) { return Number(day.costUSD) > 0; });
374
+ var points = [];
375
+ for (var i = 0; i < daily.length; i++) {
376
+ var day = daily[i];
377
+ var cost = Number(day.costUSD) || 0;
378
+ var tokens = Number(day.totalTokens) || 0;
379
+ var requests = Number(day.requestCount) || 0;
380
+ var value = hasCost ? cost : tokens;
381
+ if (value < 0) continue;
382
+ points.push({
383
+ label: shortDayLabel(day.date),
384
+ value: value,
385
+ valueLabel: hasCost
386
+ ? formatUsd(cost) + " · " + fmtCount(tokens, "tokens")
387
+ : fmtCount(tokens, "tokens") + " · " + fmtCount(requests, "requests"),
388
+ });
389
+ }
390
+ return points;
391
+ }
392
+
393
+ function persistDaily(ctx, daily) {
394
+ if (!ctx.host.usageDaily || typeof ctx.host.usageDaily.ingest !== "function") return;
395
+ if (!daily || !daily.length) return;
396
+ try {
397
+ ctx.host.usageDaily.ingest({
398
+ displayName: "OpenAI API",
399
+ source: "admin_billing",
400
+ daily: daily,
401
+ });
402
+ } catch (_) {}
403
+ }
404
+
405
+ function topModelText(daily) {
406
+ var totals = {};
407
+ for (var i = 0; i < daily.length; i++) {
408
+ var models = Array.isArray(daily[i].modelBreakdowns) ? daily[i].modelBreakdowns : [];
409
+ for (var j = 0; j < models.length; j++) {
410
+ var model = models[j];
411
+ if (!model || !model.modelName) continue;
412
+ totals[model.modelName] = (totals[model.modelName] || 0) + (Number(model.totalTokens) || 0);
413
+ }
414
+ }
415
+ var ranked = Object.keys(totals)
416
+ .map(function (name) { return { name: name, tokens: totals[name] }; })
417
+ .sort(function (a, b) { return b.tokens - a.tokens || a.name.localeCompare(b.name); });
418
+ if (!ranked.length || ranked[0].tokens <= 0) return null;
419
+ return ranked[0].name + " · " + fmtCount(ranked[0].tokens, "tokens");
420
+ }
421
+
422
+ function fetchAdminUsage(ctx, credential) {
423
+ var costs = fetchPagedBuckets(ctx, {
424
+ name: "costs",
425
+ url: ORG_COSTS_URL,
426
+ groupBy: "line_item",
427
+ apiKey: credential.apiKey,
428
+ projectId: credential.projectId,
429
+ });
430
+ var completions = fetchPagedBuckets(ctx, {
431
+ name: "completions",
432
+ url: ORG_COMPLETIONS_URL,
433
+ groupBy: "model",
434
+ apiKey: credential.apiKey,
435
+ projectId: credential.projectId,
436
+ });
437
+ var daily = makeAdminDaily(costs, completions);
438
+ var total = summarize(daily);
439
+ var now = new Date();
440
+ var todayKey = dayKeyFromUtcMs(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
441
+ var yesterdayKey = dayKeyFromUtcMs(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()) - 24 * 60 * 60 * 1000);
442
+
443
+ var lines = [
444
+ ctx.line.text({ label: "Spend", value: formatUsd(total.costUSD) }),
445
+ ctx.line.text({ label: "Requests", value: fmtCount(total.requests, "requests") }),
446
+ ctx.line.text({ label: "Tokens", value: fmtCount(total.tokens, "tokens") }),
447
+ ];
448
+ pushDayLine(lines, ctx, "Today", findDay(daily, todayKey));
449
+ pushDayLine(lines, ctx, "Yesterday", findDay(daily, yesterdayKey));
450
+ lines.push(ctx.line.text({ label: "Last 30 Days", value: usageLabel(total) }));
451
+
452
+ var topModel = topModelText(daily);
453
+ if (topModel) lines.push(ctx.line.text({ label: "Top Model", value: topModel }));
454
+
455
+ var points = collectUsageChartPoints(daily);
456
+ if (points.length) {
457
+ lines.push(ctx.line.barChart({
458
+ label: "Usage Trend",
459
+ points: points,
460
+ note: credential.projectId
461
+ ? "OpenAI Admin API usage for project " + credential.projectId + "."
462
+ : "OpenAI Admin API organization usage.",
463
+ color: "#10a37f",
464
+ }));
465
+ }
466
+
467
+ persistDaily(ctx, daily);
468
+ return { lines: lines };
469
+ }
470
+
471
+ function fetchLegacyBalance(ctx, apiKey) {
472
+ var json = requestJson(ctx, CREDIT_GRANTS_URL, apiKey, "credit grants");
473
+ var granted = typeof json.total_granted === "number" ? json.total_granted : 0;
474
+ var used = typeof json.total_used === "number" ? json.total_used : 0;
475
+ var available = typeof json.total_available === "number" ? json.total_available : Math.max(0, granted - used);
476
+ var usedPct = granted > 0 ? Math.min(100, (used / granted) * 100) : (available > 0 ? 0 : 100);
477
+
478
+ var expiresAt = null;
479
+ var grants = json.grants && Array.isArray(json.grants.data) ? json.grants.data : [];
480
+ var now = Date.now();
481
+ for (var i = 0; i < grants.length; i++) {
482
+ var ts = grants[i].expires_at;
483
+ if (typeof ts === "number" && ts * 1000 > now) {
484
+ if (expiresAt === null || ts < expiresAt) expiresAt = ts;
485
+ }
486
+ }
487
+
488
+ var opts = {
489
+ label: "Credits",
490
+ used: usedPct,
491
+ limit: 100,
492
+ format: { kind: "percent" },
493
+ };
494
+ if (expiresAt !== null) opts.resetsAt = ctx.util.toIso(expiresAt * 1000);
495
+
496
+ return {
497
+ lines: [
498
+ ctx.line.progress(opts),
499
+ ctx.line.text({ label: "Available", value: formatUsd(available) }),
500
+ ],
501
+ };
502
+ }
503
+
504
+ function probe(ctx) {
505
+ var credential = loadCredential(ctx);
506
+ if (!credential) {
507
+ throw "OpenAI API key not found. Set OPENAI_ADMIN_KEY or OPENAI_API_KEY.";
508
+ }
509
+
510
+ var adminError = null;
511
+ try {
512
+ return fetchAdminUsage(ctx, credential);
513
+ } catch (e) {
514
+ adminError = e;
515
+ ctx.host.log.warn("OpenAI Admin API usage failed: " + (e && e.message ? e.message : String(e)));
516
+ }
517
+
518
+ if (credential.projectId && credential.usesAdminKey) {
519
+ throw adminError && adminError.message ? adminError.message : String(adminError);
520
+ }
521
+
522
+ try {
523
+ return fetchLegacyBalance(ctx, credential.apiKey);
524
+ } catch (legacyError) {
525
+ if (adminError && !adminError.authRejected) {
526
+ throw adminError.message || String(adminError);
527
+ }
528
+ throw legacyError && legacyError.message ? legacyError.message : String(legacyError);
529
+ }
530
+ }
531
+
532
+ globalThis.__openusage_plugin = { id: "openai-api", probe: probe };
533
+ })();
@@ -0,0 +1,26 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "openai-api",
4
+ "name": "OpenAI API",
5
+ "version": "0.0.1",
6
+ "entry": "plugin.js",
7
+ "brandColor": "#10a37f",
8
+ "links": [
9
+ { "label": "Usage", "url": "https://platform.openai.com/usage" },
10
+ { "label": "Status", "url": "https://status.openai.com" }
11
+ ],
12
+ "supportedModes": ["api"],
13
+ "autoMode": "api",
14
+ "lines": [
15
+ { "type": "text", "label": "Spend", "scope": "overview", "primaryOrder": 1 },
16
+ { "type": "text", "label": "Requests", "scope": "overview", "primaryOrder": 2 },
17
+ { "type": "text", "label": "Tokens", "scope": "overview", "primaryOrder": 3 },
18
+ { "type": "progress", "label": "Credits", "scope": "overview", "primaryOrder": 4 },
19
+ { "type": "text", "label": "Available", "scope": "detail" },
20
+ { "type": "text", "label": "Today", "scope": "detail" },
21
+ { "type": "text", "label": "Yesterday", "scope": "detail" },
22
+ { "type": "text", "label": "Last 30 Days", "scope": "detail" },
23
+ { "type": "text", "label": "Top Model", "scope": "detail" },
24
+ { "type": "barChart", "label": "Usage Trend", "scope": "detail" }
25
+ ]
26
+ }
@@ -0,0 +1 @@
1
+ <svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>opencode</title><path d="M16 6H8v12h8V6zm4 16H4V2h16v20z"></path></svg>