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

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 (273) hide show
  1. package/LICENSE +0 -0
  2. package/native-manifest.json +365 -77
  3. package/package.json +49 -1
  4. package/plugins/UPSTREAM-LICENSES.md +109 -0
  5. package/plugins/abacus-ai/icon.svg +0 -0
  6. package/plugins/abacus-ai/plugin.js +0 -0
  7. package/plugins/abacus-ai/plugin.json +0 -0
  8. package/plugins/aiand/icon.svg +6 -0
  9. package/plugins/aiand/plugin.js +108 -0
  10. package/plugins/aiand/plugin.json +22 -0
  11. package/plugins/alibaba/icon-color.svg +0 -0
  12. package/plugins/alibaba/icon.svg +0 -0
  13. package/plugins/alibaba/plugin.js +0 -0
  14. package/plugins/alibaba/plugin.json +0 -0
  15. package/plugins/alibaba-token-plan/icon-color.svg +0 -0
  16. package/plugins/alibaba-token-plan/icon.svg +0 -0
  17. package/plugins/alibaba-token-plan/plugin.js +0 -0
  18. package/plugins/alibaba-token-plan/plugin.json +0 -0
  19. package/plugins/amp/icon-color.svg +0 -0
  20. package/plugins/amp/icon-conflict.svg +0 -0
  21. package/plugins/amp/icon.svg +0 -0
  22. package/plugins/amp/plugin.js +0 -0
  23. package/plugins/amp/plugin.json +0 -0
  24. package/plugins/antigravity/icon-color.svg +0 -0
  25. package/plugins/antigravity/icon-conflict.svg +0 -0
  26. package/plugins/antigravity/icon.svg +0 -0
  27. package/plugins/antigravity/plugin.js +0 -0
  28. package/plugins/antigravity/plugin.json +0 -0
  29. package/plugins/antigravity-cli/icon.svg +0 -0
  30. package/plugins/antigravity-cli/plugin.js +0 -0
  31. package/plugins/antigravity-cli/plugin.json +0 -0
  32. package/plugins/antigravity-ide/icon.svg +0 -0
  33. package/plugins/antigravity-ide/plugin.js +0 -0
  34. package/plugins/antigravity-ide/plugin.json +0 -0
  35. package/plugins/augment/icon.svg +0 -0
  36. package/plugins/augment/plugin.js +0 -0
  37. package/plugins/augment/plugin.json +0 -0
  38. package/plugins/aws-bedrock/icon-color.svg +0 -0
  39. package/plugins/aws-bedrock/icon.svg +0 -0
  40. package/plugins/aws-bedrock/plugin.js +0 -0
  41. package/plugins/aws-bedrock/plugin.json +0 -0
  42. package/plugins/azure-openai/icon-color.svg +0 -0
  43. package/plugins/azure-openai/icon.svg +0 -0
  44. package/plugins/azure-openai/plugin.js +0 -0
  45. package/plugins/azure-openai/plugin.json +0 -0
  46. package/plugins/chutes/icon.svg +0 -0
  47. package/plugins/chutes/plugin.js +0 -0
  48. package/plugins/chutes/plugin.json +0 -0
  49. package/plugins/claude/icon-color.svg +0 -0
  50. package/plugins/claude/icon.svg +0 -0
  51. package/plugins/claude/plugin.js +70 -23
  52. package/plugins/claude/plugin.json +0 -0
  53. package/plugins/clawrouter/icon.svg +7 -0
  54. package/plugins/clawrouter/plugin.js +45 -0
  55. package/plugins/clawrouter/plugin.json +22 -0
  56. package/plugins/clinepass/icon.svg +4 -0
  57. package/plugins/clinepass/plugin.js +71 -0
  58. package/plugins/clinepass/plugin.json +23 -0
  59. package/plugins/codebuddy/plugin.js +331 -0
  60. package/plugins/codebuddy/plugin.json +30 -0
  61. package/plugins/codebuff/icon.svg +0 -0
  62. package/plugins/codebuff/plugin.js +0 -0
  63. package/plugins/codebuff/plugin.json +0 -0
  64. package/plugins/codex/icon-color.svg +0 -0
  65. package/plugins/codex/icon.svg +0 -0
  66. package/plugins/codex/plugin.js +7 -2
  67. package/plugins/codex/plugin.json +0 -0
  68. package/plugins/command-code/icon.svg +0 -0
  69. package/plugins/command-code/plugin.js +7 -0
  70. package/plugins/command-code/plugin.json +0 -0
  71. package/plugins/copilot/icon-color.svg +0 -0
  72. package/plugins/copilot/icon.svg +0 -0
  73. package/plugins/copilot/plugin.js +61 -9
  74. package/plugins/copilot/plugin.json +4 -1
  75. package/plugins/crof/icon.svg +0 -0
  76. package/plugins/crof/plugin.js +0 -0
  77. package/plugins/crof/plugin.json +0 -0
  78. package/plugins/crofai/icon.svg +0 -0
  79. package/plugins/crofai/plugin.js +0 -0
  80. package/plugins/crofai/plugin.json +0 -0
  81. package/plugins/cursor/icon-conflict.svg +0 -0
  82. package/plugins/cursor/icon.svg +0 -0
  83. package/plugins/cursor/plugin.js +46 -9
  84. package/plugins/cursor/plugin.json +3 -2
  85. package/plugins/cursor-nightly/icon.png +0 -0
  86. package/plugins/cursor-nightly/plugin.js +45 -8
  87. package/plugins/cursor-nightly/plugin.json +3 -2
  88. package/plugins/deepgram/icon.svg +0 -0
  89. package/plugins/deepgram/plugin.js +0 -0
  90. package/plugins/deepgram/plugin.json +0 -0
  91. package/plugins/deepinfra/icon.svg +4 -0
  92. package/plugins/deepinfra/plugin.js +97 -0
  93. package/plugins/deepinfra/plugin.json +28 -0
  94. package/plugins/deepseek/icon-color.svg +0 -0
  95. package/plugins/deepseek/icon.svg +0 -0
  96. package/plugins/deepseek/plugin.js +0 -0
  97. package/plugins/deepseek/plugin.json +0 -0
  98. package/plugins/devin/icon.svg +0 -0
  99. package/plugins/devin/plugin.js +111 -0
  100. package/plugins/devin/plugin.json +2 -1
  101. package/plugins/doubao/icon-color.svg +0 -0
  102. package/plugins/doubao/icon.svg +0 -0
  103. package/plugins/doubao/plugin.js +0 -0
  104. package/plugins/doubao/plugin.json +0 -0
  105. package/plugins/droid/icon.svg +0 -0
  106. package/plugins/droid/plugin.json +2 -1
  107. package/plugins/elevenlabs/icon.svg +0 -0
  108. package/plugins/elevenlabs/plugin.js +17 -2
  109. package/plugins/elevenlabs/plugin.json +0 -0
  110. package/plugins/factory/icon-conflict.svg +0 -0
  111. package/plugins/factory/icon.svg +0 -0
  112. package/plugins/factory/plugin.js +38 -14
  113. package/plugins/factory/plugin.json +2 -1
  114. package/plugins/fireworks-ai/icon.svg +0 -0
  115. package/plugins/fireworks-ai/plugin.js +0 -0
  116. package/plugins/fireworks-ai/plugin.json +0 -0
  117. package/plugins/gemini/icon-color.svg +0 -0
  118. package/plugins/gemini/icon.svg +0 -0
  119. package/plugins/gemini/plugin.js +0 -0
  120. package/plugins/gemini/plugin.json +0 -0
  121. package/plugins/grok/icon.svg +0 -0
  122. package/plugins/grok/plugin.js +30 -5
  123. package/plugins/grok/plugin.json +3 -1
  124. package/plugins/groqcloud/icon.svg +0 -0
  125. package/plugins/groqcloud/plugin.js +0 -0
  126. package/plugins/groqcloud/plugin.json +0 -0
  127. package/plugins/ibmbob/icon.svg +23 -0
  128. package/plugins/ibmbob/plugin.js +61 -0
  129. package/plugins/ibmbob/plugin.json +22 -0
  130. package/plugins/jetbrains-ai-assistant/icon-color.svg +0 -0
  131. package/plugins/jetbrains-ai-assistant/icon-conflict.svg +0 -0
  132. package/plugins/jetbrains-ai-assistant/icon.svg +0 -0
  133. package/plugins/jetbrains-ai-assistant/plugin.js +0 -0
  134. package/plugins/jetbrains-ai-assistant/plugin.json +0 -0
  135. package/plugins/kilo/icon.svg +0 -0
  136. package/plugins/kilo/plugin.js +0 -0
  137. package/plugins/kilo/plugin.json +0 -0
  138. package/plugins/kimi/icon-color.svg +0 -0
  139. package/plugins/kimi/icon-conflict.svg +0 -0
  140. package/plugins/kimi/icon.svg +0 -0
  141. package/plugins/kimi/plugin.js +122 -141
  142. package/plugins/kimi/plugin.json +5 -2
  143. package/plugins/kimi-k2/icon-color.svg +0 -0
  144. package/plugins/kimi-k2/icon.svg +0 -0
  145. package/plugins/kimi-k2/plugin.js +0 -0
  146. package/plugins/kimi-k2/plugin.json +0 -0
  147. package/plugins/kiro/icon-color.svg +0 -0
  148. package/plugins/kiro/icon-conflict.svg +0 -0
  149. package/plugins/kiro/icon.svg +0 -0
  150. package/plugins/kiro/plugin.js +49 -10
  151. package/plugins/kiro/plugin.json +3 -1
  152. package/plugins/litellm/icon.svg +0 -0
  153. package/plugins/litellm/plugin.js +0 -0
  154. package/plugins/litellm/plugin.json +0 -0
  155. package/plugins/llm-proxy/icon.svg +0 -0
  156. package/plugins/llm-proxy/plugin.js +0 -0
  157. package/plugins/llm-proxy/plugin.json +0 -0
  158. package/plugins/longcat/icon.svg +4 -0
  159. package/plugins/longcat/plugin.js +182 -0
  160. package/plugins/longcat/plugin.json +23 -0
  161. package/plugins/manus/icon.svg +0 -0
  162. package/plugins/manus/plugin.js +0 -0
  163. package/plugins/manus/plugin.json +0 -0
  164. package/plugins/mimo/icon.svg +0 -0
  165. package/plugins/mimo/plugin.js +0 -0
  166. package/plugins/mimo/plugin.json +0 -0
  167. package/plugins/minimax/icon-color.svg +0 -0
  168. package/plugins/minimax/icon-conflict.svg +0 -0
  169. package/plugins/minimax/icon.svg +0 -0
  170. package/plugins/minimax/plugin.js +25 -31
  171. package/plugins/minimax/plugin.json +0 -0
  172. package/plugins/mistral/icon-color.svg +0 -0
  173. package/plugins/mistral/icon.svg +0 -0
  174. package/plugins/mistral/plugin.js +0 -0
  175. package/plugins/mistral/plugin.json +0 -0
  176. package/plugins/moonshot/icon.svg +0 -0
  177. package/plugins/moonshot/plugin.js +8 -6
  178. package/plugins/moonshot/plugin.json +0 -0
  179. package/plugins/nanogpt/icon-color.svg +0 -0
  180. package/plugins/nanogpt/icon.svg +0 -0
  181. package/plugins/nanogpt/plugin.js +0 -0
  182. package/plugins/nanogpt/plugin.json +0 -0
  183. package/plugins/neuralwatt/icon.svg +0 -0
  184. package/plugins/neuralwatt/plugin.js +0 -0
  185. package/plugins/neuralwatt/plugin.json +0 -0
  186. package/plugins/notion/icon.svg +1 -0
  187. package/plugins/notion/plugin.js +282 -0
  188. package/plugins/notion/plugin.json +29 -0
  189. package/plugins/ollama/icon.svg +0 -0
  190. package/plugins/ollama/plugin.js +257 -56
  191. package/plugins/ollama/plugin.json +6 -3
  192. package/plugins/openai-api/icon.svg +0 -0
  193. package/plugins/openai-api/plugin.js +0 -0
  194. package/plugins/openai-api/plugin.json +0 -0
  195. package/plugins/opencode/icon.svg +0 -0
  196. package/plugins/opencode/plugin.js +0 -0
  197. package/plugins/opencode/plugin.json +0 -0
  198. package/plugins/opencode-go/icon-conflict.svg +0 -0
  199. package/plugins/opencode-go/icon.svg +0 -0
  200. package/plugins/opencode-go/plugin.js +78 -10
  201. package/plugins/opencode-go/plugin.json +5 -2
  202. package/plugins/openrouter/icon.svg +0 -0
  203. package/plugins/openrouter/plugin.js +371 -114
  204. package/plugins/openrouter/plugin.json +6 -0
  205. package/plugins/perplexity/icon-color.svg +0 -0
  206. package/plugins/perplexity/icon-conflict.svg +0 -0
  207. package/plugins/perplexity/icon.svg +0 -0
  208. package/plugins/perplexity/plugin.js +0 -0
  209. package/plugins/perplexity/plugin.json +0 -0
  210. package/plugins/poe/icon.svg +0 -0
  211. package/plugins/poe/plugin.js +24 -18
  212. package/plugins/poe/plugin.json +2 -0
  213. package/plugins/qoder/icon.svg +3 -0
  214. package/plugins/qoder/plugin.js +227 -0
  215. package/plugins/qoder/plugin.json +23 -0
  216. package/plugins/qwencloud/icon.svg +4 -0
  217. package/plugins/qwencloud/plugin.js +677 -0
  218. package/plugins/qwencloud/plugin.json +29 -0
  219. package/plugins/sakana/icon.svg +5 -0
  220. package/plugins/sakana/plugin.js +156 -0
  221. package/plugins/sakana/plugin.json +23 -0
  222. package/plugins/stepfun/icon-color.svg +0 -0
  223. package/plugins/stepfun/icon.svg +0 -0
  224. package/plugins/stepfun/plugin.js +0 -0
  225. package/plugins/stepfun/plugin.json +0 -0
  226. package/plugins/sub2api/icon.svg +4 -0
  227. package/plugins/sub2api/plugin.js +191 -0
  228. package/plugins/sub2api/plugin.json +16 -0
  229. package/plugins/synthetic/icon-conflict.svg +0 -0
  230. package/plugins/synthetic/icon.svg +0 -0
  231. package/plugins/synthetic/plugin.js +0 -0
  232. package/plugins/synthetic/plugin.json +0 -0
  233. package/plugins/t3chat/icon-color.svg +0 -0
  234. package/plugins/t3chat/icon.svg +0 -0
  235. package/plugins/t3chat/plugin.js +0 -0
  236. package/plugins/t3chat/plugin.json +0 -0
  237. package/plugins/venice/icon-color.svg +0 -0
  238. package/plugins/venice/icon.svg +0 -0
  239. package/plugins/venice/plugin.js +0 -0
  240. package/plugins/venice/plugin.json +0 -0
  241. package/plugins/vertex-ai/icon-color.svg +0 -0
  242. package/plugins/vertex-ai/icon.svg +0 -0
  243. package/plugins/vertex-ai/plugin.js +0 -0
  244. package/plugins/vertex-ai/plugin.json +0 -0
  245. package/plugins/warp/icon-color.svg +0 -0
  246. package/plugins/warp/icon.svg +0 -0
  247. package/plugins/warp/plugin.js +0 -0
  248. package/plugins/warp/plugin.json +0 -0
  249. package/plugins/wayfinder/icon.svg +5 -0
  250. package/plugins/wayfinder/plugin.js +47 -0
  251. package/plugins/wayfinder/plugin.json +16 -0
  252. package/plugins/windsurf/icon-conflict.svg +0 -0
  253. package/plugins/windsurf/icon.svg +0 -0
  254. package/plugins/windsurf/plugin.js +0 -0
  255. package/plugins/windsurf/plugin.json +0 -0
  256. package/plugins/xai/icon.svg +7 -0
  257. package/plugins/xai/plugin.js +162 -0
  258. package/plugins/xai/plugin.json +28 -0
  259. package/plugins/zai/icon-conflict.svg +0 -0
  260. package/plugins/zai/icon.svg +0 -0
  261. package/plugins/zai/plugin.js +111 -161
  262. package/plugins/zai/plugin.json +4 -1
  263. package/plugins/zed/icon.svg +0 -0
  264. package/plugins/zed/plugin.js +0 -0
  265. package/plugins/zed/plugin.json +0 -0
  266. package/plugins/zenmux/icon.svg +3 -0
  267. package/plugins/zenmux/plugin.js +102 -0
  268. package/plugins/zenmux/plugin.json +23 -0
  269. package/plugins/zoommate/icon.svg +1 -0
  270. package/plugins/zoommate/plugin.js +244 -0
  271. package/plugins/zoommate/plugin.json +29 -0
  272. package/usagestat +0 -0
  273. package/usagestatd +0 -0
@@ -313,16 +313,14 @@
313
313
  }
314
314
  }
315
315
 
316
- function fetchUsage(ctx, accessToken) {
316
+ function fetchUsage(ctx, accessToken, cookie) {
317
+ const headers = { Accept: "application/json", "Content-Type": "application/json", "User-Agent": "usagestat" }
318
+ if (accessToken) headers.Authorization = "Bearer " + accessToken
319
+ if (cookie) headers.Cookie = cookie
317
320
  var resp = ctx.util.request({
318
321
  method: "POST",
319
322
  url: USAGE_URL,
320
- headers: {
321
- Authorization: "Bearer " + accessToken,
322
- "Content-Type": "application/json",
323
- Accept: "application/json",
324
- "User-Agent": "OpenUsage",
325
- },
323
+ headers,
326
324
  bodyText: JSON.stringify({ useCache: true }),
327
325
  timeoutMs: 10000,
328
326
  })
@@ -331,18 +329,14 @@
331
329
  resp = ctx.util.request({
332
330
  method: "GET",
333
331
  url: USAGE_URL,
334
- headers: {
335
- Authorization: "Bearer " + accessToken,
336
- Accept: "application/json",
337
- "User-Agent": "OpenUsage",
338
- },
332
+ headers,
339
333
  timeoutMs: 10000,
340
334
  })
341
335
  }
342
336
  return resp
343
337
  }
344
338
 
345
- function probe(ctx) {
339
+ function fetchOAuth(ctx) {
346
340
  const authState = loadAuth(ctx)
347
341
  if (!authState) {
348
342
  ctx.host.log.error("probe failed: not logged in")
@@ -412,7 +406,10 @@
412
406
  }
413
407
 
414
408
  ctx.host.log.info("usage fetch succeeded")
409
+ return resp
410
+ }
415
411
 
412
+ function buildResult(ctx, resp, source) {
416
413
  const data = ctx.util.tryParseJson(resp.bodyText)
417
414
  if (data === null) {
418
415
  throw "Usage response invalid. Try again later."
@@ -480,7 +477,34 @@
480
477
  lines.push(ctx.line.badge({ label: "Status", text: "No usage data", color: "#a3a3a3" }))
481
478
  }
482
479
 
483
- return { plan: plan, lines: lines }
480
+ return { plan: plan, lines: lines, source }
481
+ }
482
+
483
+ function probe(ctx) {
484
+ const configured = ctx.provider || {}
485
+ const mode = ctx.sourceMode || "auto"
486
+ const key = String(configured.apiKey || ctx.host.env.get("FACTORY_API_KEY") || "").trim()
487
+ const cookie = String(configured.cookieHeader || ctx.host.env.get("FACTORY_COOKIE") || ctx.host.env.get("DROID_COOKIE") || "").trim().replace(/^cookie:\s*/i, "")
488
+ if (mode === "api" && !key) throw "Set FACTORY_API_KEY or provider apiKey."
489
+ if (mode === "web" && !cookie) throw "Set FACTORY_COOKIE or provider cookieHeader."
490
+ if (key && (mode === "auto" || mode === "api")) {
491
+ try {
492
+ const resp = fetchUsage(ctx, key)
493
+ if (ctx.util.isAuthStatus(resp.status)) throw "Factory API key was rejected."
494
+ if (resp.status < 200 || resp.status >= 300) throw "Factory API returned HTTP " + resp.status + "."
495
+ return buildResult(ctx, resp, "api")
496
+ } catch (error) {
497
+ if (mode === "api" || (!cookie && !loadAuth(ctx))) throw error
498
+ ctx.host.log.warn("Factory API unavailable; trying the configured session")
499
+ }
500
+ }
501
+ if (cookie && (mode === "auto" || mode === "web")) {
502
+ const resp = fetchUsage(ctx, null, cookie)
503
+ if (ctx.util.isAuthStatus(resp.status)) throw "Factory web session expired."
504
+ if (resp.status < 200 || resp.status >= 300) throw "Factory web usage returned HTTP " + resp.status + "."
505
+ return buildResult(ctx, resp, "web")
506
+ }
507
+ return buildResult(ctx, fetchOAuth(ctx), "oauth")
484
508
  }
485
509
 
486
510
  globalThis.__openusage_plugin = { id: "factory", probe }
@@ -6,7 +6,8 @@
6
6
  "entry": "plugin.js",
7
7
  "icon": "icon.svg",
8
8
  "brandColor": "#020202",
9
- "supportedModes": ["oauth", "local"],
9
+ "supportedModes": ["api", "oauth", "local", "web"],
10
+ "envVars": ["FACTORY_API_KEY", "FACTORY_COOKIE", "DROID_COOKIE"],
10
11
  "autoMode": "oauth",
11
12
  "lines": [
12
13
  { "type": "progress", "label": "Standard", "scope": "overview", "primaryOrder": 1 },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
  const AUTH_PATH = "~/.grok/auth.json";
3
- const BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing";
3
+ const BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
4
4
  const SETTINGS_URL = "https://cli-chat-proxy.grok.com/v1/settings";
5
5
  const REFRESH_URL = "https://auth.x.ai/oauth2/token";
6
6
  const DEFAULT_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
@@ -8,6 +8,11 @@
8
8
  const AUTH_REFRESH_BUFFER_MS = 5 * 60 * 1000;
9
9
  const LOGIN_HINT = "Grok auth expired. Run `grok login` again.";
10
10
 
11
+ function authPath(ctx) {
12
+ const home = ctx.host.env.get("GROK_HOME");
13
+ return typeof home === "string" && home.trim() ? home.trim().replace(/\/+$/, "") + "/auth.json" : AUTH_PATH;
14
+ }
15
+
11
16
  function readJson(ctx, path) {
12
17
  if (!ctx.host.fs.exists(path)) return null;
13
18
  try {
@@ -129,7 +134,7 @@
129
134
  entry.expires_at = new Date(expiresAtMs).toISOString();
130
135
 
131
136
  try {
132
- ctx.host.fs.writeText(AUTH_PATH, JSON.stringify(auth, null, 2));
137
+ ctx.host.fs.writeText(authPath(ctx), JSON.stringify(auth, null, 2));
133
138
  ctx.host.log.info("Grok auth refresh succeeded, token persisted");
134
139
  } catch (e) {
135
140
  ctx.host.log.warn("Grok auth refresh succeeded but failed to save auth: " + String(e));
@@ -144,7 +149,7 @@
144
149
  }
145
150
 
146
151
  function loadAuth(ctx) {
147
- const auth = readJson(ctx, AUTH_PATH);
152
+ const auth = readJson(ctx, authPath(ctx));
148
153
  if (!auth || typeof auth !== "object") {
149
154
  throw "Grok not logged in. Run `grok login`.";
150
155
  }
@@ -179,6 +184,7 @@
179
184
 
180
185
  function unitsValue(obj) {
181
186
  if (!obj || typeof obj !== "object") return null;
187
+ if ((typeof obj.val !== "number" && typeof obj.val !== "string") || String(obj.val).trim() === "") return null;
182
188
  const n = Number(obj.val);
183
189
  return Number.isFinite(n) ? n : null;
184
190
  }
@@ -235,7 +241,7 @@
235
241
  Accept: "application/json",
236
242
  "User-Agent": "OpenUsage",
237
243
  },
238
- timeoutMs: 10000,
244
+ timeoutMs: 1500,
239
245
  });
240
246
  if (resp.status < 200 || resp.status >= 300) return null;
241
247
  const data = ctx.util.tryParseJson(resp.bodyText);
@@ -262,6 +268,25 @@
262
268
  throw "Grok billing response changed.";
263
269
  }
264
270
 
271
+ if (config.currentPeriod) {
272
+ const period = config.currentPeriod;
273
+ const start = ctx.util.parseDateMs(period.start);
274
+ const end = ctx.util.parseDateMs(period.end);
275
+ const now = nowMs(ctx);
276
+ const knownType = ["USAGE_PERIOD_TYPE_WEEKLY", "USAGE_PERIOD_TYPE_MONTHLY"].includes(period.type);
277
+ if (!knownType || !Number.isFinite(start) || !Number.isFinite(end) || end <= start) throw "Grok billing period is invalid.";
278
+ const hasPercent = Object.prototype.hasOwnProperty.call(config, "creditUsagePercent");
279
+ let percent = hasPercent ? unitsValue({ val: config.creditUsagePercent }) : 0;
280
+ if (percent === null || percent < 0 || (!hasPercent && !(start <= now && now < end))) throw "Grok billing usage is unavailable for this period.";
281
+ const cap = config.onDemandCap === undefined ? 0 : config.onDemandCap && config.onDemandCap.val === undefined ? 0 : unitsValue(config.onDemandCap);
282
+ if (cap === null || cap < 0) throw "Grok on-demand cap is invalid.";
283
+ return { plan: fetchPlanName(ctx, auth.token), source: "api", lines: [
284
+ ctx.line.progress({ label: period.type === "USAGE_PERIOD_TYPE_WEEKLY" ? "Weekly limit" : "Monthly limit", used: clampPercent(percent), limit: 100,
285
+ format: { kind: "percent" }, resetsAt: ctx.util.toIso(end), periodDurationMs: end - start }),
286
+ ctx.line.badge({ label: "Pay as you go", text: cap > 0 ? String(cap) + " cap" : "Disabled", color: cap > 0 ? "#22c55e" : "#a3a3a3" }),
287
+ ] };
288
+ }
289
+
265
290
  const usedUnits = unitsValue(config.used);
266
291
  const limitUnits = unitsValue(config.monthlyLimit);
267
292
  const onDemandCapUnits = unitsValue(config.onDemandCap);
@@ -290,7 +315,7 @@
290
315
  }),
291
316
  ];
292
317
 
293
- return { plan: fetchPlanName(ctx, auth.token), lines };
318
+ return { plan: fetchPlanName(ctx, auth.token), source: "api", lines };
294
319
  }
295
320
 
296
321
  globalThis.__openusage_plugin = { id: "grok", probe };
@@ -13,7 +13,9 @@
13
13
  "autoMode": "cli",
14
14
  "webUrl": "https://grok.com",
15
15
  "lines": [
16
- { "type": "progress", "label": "Credits used", "scope": "overview", "primaryOrder": 1 },
16
+ { "type": "progress", "label": "Weekly limit", "scope": "overview", "primaryOrder": 1 },
17
+ { "type": "progress", "label": "Monthly limit", "scope": "overview" },
18
+ { "type": "progress", "label": "Credits used", "scope": "overview" },
17
19
  { "type": "badge", "label": "Pay as you go", "scope": "detail" }
18
20
  ]
19
21
  }
File without changes
File without changes
File without changes
@@ -0,0 +1,23 @@
1
+ <svg id="Bob" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="helmetShade" x1="0" y1="0" x2="1" y2="0">
4
+ <stop offset=".134" stop-opacity="0"/>
5
+ <stop offset=".873"/>
6
+ </linearGradient>
7
+ <linearGradient id="bobGradient" x1="0" y1="32" x2="32" y2="0" gradientUnits="userSpaceOnUse">
8
+ <stop offset=".1" stop-color="#a16efb"/>
9
+ <stop offset=".9" stop-color="#0e61fa"/>
10
+ </linearGradient>
11
+ <mask id="helmetMask" x="0" y="0" width="32" height="32" maskUnits="userSpaceOnUse">
12
+ <path d="M29.718 15.25a13.67 13.67 0 0 0-9.991-12.476A1.747 1.747 0 0 0 18 1.25h-4c-.887 0-1.615.667-1.727 1.524A13.67 13.67 0 0 0 2.282 15.25H0v1.5h32v-1.5h-2.282zM12.25 4.337V13h1.5V3a.25.25 0 0 1 .25-.25h4a.25.25 0 0 1 .25.25v10h1.5V4.337a12.183 12.183 0 0 1 8.468 10.913H3.782A12.183 12.183 0 0 1 12.25 4.337z" fill="#fff"/>
13
+ <path d="M21 7v7H11V7h10z" fill="url(#helmetShade)"/>
14
+ </mask>
15
+ </defs>
16
+ <g mask="url(#helmetMask)">
17
+ <path fill="url(#bobGradient)" d="M0 0h32v32H0z"/>
18
+ </g>
19
+ <path d="M24 30.75H8a4.756 4.756 0 0 1-4.69-4H3c-.965 0-1.75-.785-1.75-1.75v-6h1.5v6c0 .138.112.25.25.25h1.75V26A3.254 3.254 0 0 0 8 29.25h16A3.254 3.254 0 0 0 27.25 26v-.75H29a.25.25 0 0 0 .25-.25v-6h1.5v6c0 .965-.785 1.75-1.75 1.75h-.31a4.756 4.756 0 0 1-4.69 4z" fill="#001d6c"/>
20
+ <circle cx="22" cy="21" r="2" fill="#001d6c"/>
21
+ <circle cx="10" cy="21" r="2" fill="#001d6c"/>
22
+ <path d="M16 27.75a5.695 5.695 0 0 1-3.451-1.15.75.75 0 0 1 .903-1.199c1.476 1.111 3.617 1.111 5.099 0a.75.75 0 1 1 .901 1.199 5.696 5.696 0 0 1-3.45 1.15z" fill="#001d6c"/>
23
+ </svg>
@@ -0,0 +1,61 @@
1
+ (function () {
2
+ function clean(value) {
3
+ return typeof value === "string" && value.trim() ? value.trim() : null
4
+ }
5
+ function number(value) {
6
+ if (typeof value !== "number" && typeof value !== "string") return null
7
+ if (typeof value === "string" && !value.trim()) return null
8
+ const n = Number(value)
9
+ return Number.isFinite(n) && n >= 0 ? n : null
10
+ }
11
+ function regionalBase(domain) {
12
+ if (!clean(domain)) return "https://api.us-east.bob.ibm.com"
13
+ const host = domain.trim().toLowerCase()
14
+ if (!/^(?:[a-z0-9-]+\.)*bob\.ibm\.com$/.test(host)) throw "IBM Bob returned an untrusted regional host."
15
+ return "https://" + (host.startsWith("api.") ? host : "api." + host)
16
+ }
17
+ function request(ctx, url, headers) {
18
+ const result = ctx.util.requestJson({ method: "GET", url, headers, timeoutMs: 5000 })
19
+ if (ctx.util.isAuthStatus(result.resp.status)) throw "IBM Bob rejected the API key."
20
+ if (result.resp.status < 200 || result.resp.status >= 300) throw "IBM Bob API returned HTTP " + result.resp.status + "."
21
+ if (!result.json || typeof result.json !== "object") throw "Invalid IBM Bob response."
22
+ return result.json
23
+ }
24
+ function probe(ctx) {
25
+ const key = clean(ctx.provider && ctx.provider.apiKey) || clean(ctx.host.env.get("BOBSHELL_API_KEY"))
26
+ if (!key) throw "Set BOBSHELL_API_KEY or the IBM Bob provider API key."
27
+ const jwt = key.split(".").length === 3 && ctx.jwt.decodePayload(key)
28
+ const headers = { Authorization: (jwt ? "Bearer " : "Apikey ") + key, Accept: "application/json", "User-Agent": "UsageStat" }
29
+ const profile = request(ctx, "https://api.us-east.bob.ibm.com/admin/v1/profile", headers)
30
+ if (!Array.isArray(profile.instances)) throw "IBM Bob profile is missing subscriptions."
31
+ const lines = []
32
+ const plans = []
33
+ let count = 0
34
+ for (const instance of profile.instances) {
35
+ if (!instance || !clean(instance.user_id) || !Array.isArray(instance.teams)) continue
36
+ const base = regionalBase(instance.region_domain)
37
+ for (const team of instance.teams) {
38
+ if (!team || !clean(team.id)) continue
39
+ if (ctx.provider && ctx.provider.workspaceId && ctx.provider.workspaceId !== team.id) continue
40
+ if (++count > 20) throw "IBM Bob returned more than 20 teams; select a workspace."
41
+ const budget = request(ctx, base + "/admin/v1/teams/" + encodeURIComponent(team.id) + "/users/" + encodeURIComponent(instance.user_id),
42
+ Object.assign({}, headers, { "x-instance-id": instance.instance_id, "x-team-id": team.id }))
43
+ const used = number(budget.usage)
44
+ const limit = number(budget.budget_limit) === null ? number(team.budget_limit) : number(budget.budget_limit)
45
+ if (used === null) throw "IBM Bob did not return valid Bobcoin usage."
46
+ const label = clean(instance.instance_name) || clean(instance.name) || instance.instance_id
47
+ const teamName = clean(team.name) || team.id
48
+ const reset = typeof instance.refresh_at === "number" ? instance.refresh_at * 1000 : instance.refresh_at
49
+ if (limit !== null && limit > 0) {
50
+ lines.push(ctx.line.progress({ label: label + " / " + teamName, used, limit,
51
+ format: { kind: "count", suffix: "Bobcoins" }, resetsAt: ctx.util.toIso(reset),
52
+ periodDurationMs: 30 * 24 * 60 * 60 * 1000 }))
53
+ } else lines.push(ctx.line.text({ label: teamName, value: used + " Bobcoins used" }))
54
+ if (clean(instance.plan_name) && !plans.includes(instance.plan_name)) plans.push(instance.plan_name)
55
+ }
56
+ }
57
+ if (!lines.length) throw "No IBM Bob team subscription found."
58
+ return { displayName: "IBM Bob", source: "api", plan: plans.join(", ") || undefined, lines }
59
+ }
60
+ globalThis.__openusage_plugin = { id: "ibmbob", probe }
61
+ })()
@@ -0,0 +1,22 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "ibmbob",
4
+ "name": "IBM Bob",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "envVars": [
9
+ "BOBSHELL_API_KEY"
10
+ ],
11
+ "supportedModes": [
12
+ "api"
13
+ ],
14
+ "autoMode": "api",
15
+ "usageDashboardUrl": "https://bob.ibm.com",
16
+ "links": [
17
+ {
18
+ "label": "Dashboard",
19
+ "url": "https://bob.ibm.com"
20
+ }
21
+ ]
22
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes