@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
@@ -1,9 +1,10 @@
1
- globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1
+ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
2
2
  (function () {
3
3
  const KEYCHAIN_ACCESS_TOKEN_SERVICE = "cursor-access-token"
4
4
  const KEYCHAIN_REFRESH_TOKEN_SERVICE = "cursor-refresh-token"
5
5
  const BASE_URL = "https://api2.cursor.sh"
6
6
  const USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCurrentPeriodUsage"
7
+ const GROK_BOT_USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetSandUsageStatus"
7
8
  const PLAN_URL = BASE_URL + "/aiserver.v1.DashboardService/GetPlanInfo"
8
9
  const REFRESH_URL = BASE_URL + "/oauth/token"
9
10
  const CREDITS_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCreditGrantsBalance"
@@ -282,13 +283,13 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
282
283
  format: { kind: "count", suffix: " req" },
283
284
  }),
284
285
  ctx.line.progress({
285
- label: "Auto usage",
286
+ label: "Cursor Models",
286
287
  used: 18,
287
288
  limit: 100,
288
289
  format: { kind: "percent" },
289
290
  }),
290
291
  ctx.line.progress({
291
- label: "API usage",
292
+ label: "Other Models",
292
293
  used: 7,
293
294
  limit: 50,
294
295
  format: { kind: "percent" },
@@ -414,7 +415,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
414
415
  }
415
416
  }
416
417
 
417
- function connectPost(ctx, url, token) {
418
+ function connectPost(ctx, url, token, timeoutMs) {
418
419
  return ctx.util.request({
419
420
  method: "POST",
420
421
  url: url,
@@ -426,7 +427,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
426
427
  "User-Agent": CONNECT_CLIENT_USER_AGENT,
427
428
  },
428
429
  bodyText: "{}",
429
- timeoutMs: 10000,
430
+ timeoutMs: timeoutMs || 10000,
430
431
  })
431
432
  }
432
433
 
@@ -762,7 +763,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
762
763
 
763
764
  if (typeof pu.autoPercentUsed === "number" && Number.isFinite(pu.autoPercentUsed)) {
764
765
  lines.push(ctx.line.progress({
765
- label: "Auto usage",
766
+ label: "Cursor Models",
766
767
  used: pu.autoPercentUsed,
767
768
  limit: 100,
768
769
  format: { kind: "percent" },
@@ -773,7 +774,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
773
774
 
774
775
  if (typeof pu.apiPercentUsed === "number" && Number.isFinite(pu.apiPercentUsed)) {
775
776
  lines.push(ctx.line.progress({
776
- label: "API usage",
777
+ label: "Other Models",
777
778
  used: pu.apiPercentUsed,
778
779
  limit: 100,
779
780
  format: { kind: "percent" },
@@ -1106,7 +1107,40 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1106
1107
  return result
1107
1108
  }
1108
1109
 
1109
- function probeImpl(ctx) {
1110
+ function attachGrokBotUsage(ctx, result, accessToken) {
1111
+ if (!result || !Array.isArray(result.lines) || !accessToken) return result
1112
+ try {
1113
+ const resp = connectPost(ctx, GROK_BOT_USAGE_URL, accessToken, 3000)
1114
+ if (resp.status < 200 || resp.status >= 300) return result
1115
+ const usage = ctx.util.tryParseJson(resp.bodyText)
1116
+ if (!usage || usage.usesPooledEnterpriseAllowance === true ||
1117
+ usage.hasNonZeroIncludedLimit === false || usage.includedLimitZero === true) return result
1118
+ const percent = readFiniteNumber(usage.usagePercent)
1119
+ if (!Number.isFinite(percent) || percent < 0) return result
1120
+ const resetMs = ctx.util.parseDateMs(usage.nextResetTimestampUtc)
1121
+ const startMs = ctx.util.parseDateMs(usage.currentPeriodStart)
1122
+ const line = ctx.line.progress({
1123
+ label: "Grok Bot usage",
1124
+ used: Math.min(100, percent),
1125
+ limit: 100,
1126
+ format: { kind: "percent" },
1127
+ resetsAt: Number.isFinite(resetMs) ? ctx.util.toIso(resetMs) : undefined,
1128
+ periodDurationMs: Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
1129
+ ? resetMs - startMs : 7 * 24 * 60 * 60 * 1000,
1130
+ })
1131
+ let insertAt = result.lines.length
1132
+ for (const label of ["Other Models", "Cursor Models", "Total usage"]) {
1133
+ const index = result.lines.findIndex((item) => item.label === label)
1134
+ if (index >= 0) { insertAt = index + 1; break }
1135
+ }
1136
+ result.lines.splice(insertAt, 0, line)
1137
+ } catch (_) {
1138
+ ctx.host.log.warn("Cursor Grok Bot quota unavailable")
1139
+ }
1140
+ return result
1141
+ }
1142
+
1143
+ function probeImpl(ctx, state) {
1110
1144
  const authState = loadAuthState(ctx)
1111
1145
  let accessToken = authState.accessToken
1112
1146
  const refreshTokenValue = authState.refreshToken
@@ -1176,6 +1210,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1176
1210
  throw "Usage request failed. Check your connection."
1177
1211
  }
1178
1212
 
1213
+ state.accessToken = accessToken
1179
1214
  if (ctx.util.isAuthStatus(usageResp.status)) {
1180
1215
  ctx.host.log.error("usage returned auth error after all retries: status=" + usageResp.status)
1181
1216
  throw "Token expired. " + LOGIN_HINT
@@ -1368,7 +1403,9 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1368
1403
  }
1369
1404
 
1370
1405
  function probe(ctx) {
1371
- var result = probeImpl(ctx)
1406
+ var state = {}
1407
+ var result = probeImpl(ctx, state)
1408
+ result = attachGrokBotUsage(ctx, result, state.accessToken)
1372
1409
  result = attachCursorTranscriptActivity(ctx, result)
1373
1410
  result = attachCursorBillingDaily(ctx, result)
1374
1411
  return attachCursorMtdUsage(ctx, result)
@@ -16,8 +16,9 @@
16
16
  { "type": "progress", "label": "Credits", "scope": "overview", "primaryOrder": 2 },
17
17
  { "type": "progress", "label": "Total usage", "scope": "overview", "primaryOrder": 1 },
18
18
  { "type": "progress", "label": "Requests", "scope": "overview", "primaryOrder": 3 },
19
- { "type": "progress", "label": "Auto usage", "scope": "detail" },
20
- { "type": "progress", "label": "API usage", "scope": "detail" },
19
+ { "type": "progress", "label": "Cursor Models", "scope": "detail" },
20
+ { "type": "progress", "label": "Other Models", "scope": "detail" },
21
+ { "type": "progress", "label": "Grok Bot usage", "scope": "detail" },
21
22
  { "type": "progress", "label": "On-demand", "scope": "detail" }
22
23
  ]
23
24
  }
File without changes
@@ -4,6 +4,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
4
4
  const KEYCHAIN_REFRESH_TOKEN_SERVICE = "cursor-refresh-token"
5
5
  const BASE_URL = "https://api2.cursor.sh"
6
6
  const USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCurrentPeriodUsage"
7
+ const GROK_BOT_USAGE_URL = BASE_URL + "/aiserver.v1.DashboardService/GetSandUsageStatus"
7
8
  const PLAN_URL = BASE_URL + "/aiserver.v1.DashboardService/GetPlanInfo"
8
9
  const REFRESH_URL = BASE_URL + "/oauth/token"
9
10
  const CREDITS_URL = BASE_URL + "/aiserver.v1.DashboardService/GetCreditGrantsBalance"
@@ -282,13 +283,13 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
282
283
  format: { kind: "count", suffix: " req" },
283
284
  }),
284
285
  ctx.line.progress({
285
- label: "Auto usage",
286
+ label: "Cursor Models",
286
287
  used: 18,
287
288
  limit: 100,
288
289
  format: { kind: "percent" },
289
290
  }),
290
291
  ctx.line.progress({
291
- label: "API usage",
292
+ label: "Other Models",
292
293
  used: 7,
293
294
  limit: 50,
294
295
  format: { kind: "percent" },
@@ -414,7 +415,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
414
415
  }
415
416
  }
416
417
 
417
- function connectPost(ctx, url, token) {
418
+ function connectPost(ctx, url, token, timeoutMs) {
418
419
  return ctx.util.request({
419
420
  method: "POST",
420
421
  url: url,
@@ -426,7 +427,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
426
427
  "User-Agent": CONNECT_CLIENT_USER_AGENT,
427
428
  },
428
429
  bodyText: "{}",
429
- timeoutMs: 10000,
430
+ timeoutMs: timeoutMs || 10000,
430
431
  })
431
432
  }
432
433
 
@@ -762,7 +763,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
762
763
 
763
764
  if (typeof pu.autoPercentUsed === "number" && Number.isFinite(pu.autoPercentUsed)) {
764
765
  lines.push(ctx.line.progress({
765
- label: "Auto usage",
766
+ label: "Cursor Models",
766
767
  used: pu.autoPercentUsed,
767
768
  limit: 100,
768
769
  format: { kind: "percent" },
@@ -773,7 +774,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
773
774
 
774
775
  if (typeof pu.apiPercentUsed === "number" && Number.isFinite(pu.apiPercentUsed)) {
775
776
  lines.push(ctx.line.progress({
776
- label: "API usage",
777
+ label: "Other Models",
777
778
  used: pu.apiPercentUsed,
778
779
  limit: 100,
779
780
  format: { kind: "percent" },
@@ -1106,7 +1107,40 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1106
1107
  return result
1107
1108
  }
1108
1109
 
1109
- function probeImpl(ctx) {
1110
+ function attachGrokBotUsage(ctx, result, accessToken) {
1111
+ if (!result || !Array.isArray(result.lines) || !accessToken) return result
1112
+ try {
1113
+ const resp = connectPost(ctx, GROK_BOT_USAGE_URL, accessToken, 3000)
1114
+ if (resp.status < 200 || resp.status >= 300) return result
1115
+ const usage = ctx.util.tryParseJson(resp.bodyText)
1116
+ if (!usage || usage.usesPooledEnterpriseAllowance === true ||
1117
+ usage.hasNonZeroIncludedLimit === false || usage.includedLimitZero === true) return result
1118
+ const percent = readFiniteNumber(usage.usagePercent)
1119
+ if (!Number.isFinite(percent) || percent < 0) return result
1120
+ const resetMs = ctx.util.parseDateMs(usage.nextResetTimestampUtc)
1121
+ const startMs = ctx.util.parseDateMs(usage.currentPeriodStart)
1122
+ const line = ctx.line.progress({
1123
+ label: "Grok Bot usage",
1124
+ used: Math.min(100, percent),
1125
+ limit: 100,
1126
+ format: { kind: "percent" },
1127
+ resetsAt: Number.isFinite(resetMs) ? ctx.util.toIso(resetMs) : undefined,
1128
+ periodDurationMs: Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
1129
+ ? resetMs - startMs : 7 * 24 * 60 * 60 * 1000,
1130
+ })
1131
+ let insertAt = result.lines.length
1132
+ for (const label of ["Other Models", "Cursor Models", "Total usage"]) {
1133
+ const index = result.lines.findIndex((item) => item.label === label)
1134
+ if (index >= 0) { insertAt = index + 1; break }
1135
+ }
1136
+ result.lines.splice(insertAt, 0, line)
1137
+ } catch (_) {
1138
+ ctx.host.log.warn("Cursor Grok Bot quota unavailable")
1139
+ }
1140
+ return result
1141
+ }
1142
+
1143
+ function probeImpl(ctx, state) {
1110
1144
  const authState = loadAuthState(ctx)
1111
1145
  let accessToken = authState.accessToken
1112
1146
  const refreshTokenValue = authState.refreshToken
@@ -1176,6 +1210,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1176
1210
  throw "Usage request failed. Check your connection."
1177
1211
  }
1178
1212
 
1213
+ state.accessToken = accessToken
1179
1214
  if (ctx.util.isAuthStatus(usageResp.status)) {
1180
1215
  ctx.host.log.error("usage returned auth error after all retries: status=" + usageResp.status)
1181
1216
  throw "Token expired. " + LOGIN_HINT
@@ -1368,7 +1403,9 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor-nightly";
1368
1403
  }
1369
1404
 
1370
1405
  function probe(ctx) {
1371
- var result = probeImpl(ctx)
1406
+ var state = {}
1407
+ var result = probeImpl(ctx, state)
1408
+ result = attachGrokBotUsage(ctx, result, state.accessToken)
1372
1409
  result = attachCursorTranscriptActivity(ctx, result)
1373
1410
  result = attachCursorBillingDaily(ctx, result)
1374
1411
  return attachCursorMtdUsage(ctx, result)
@@ -16,8 +16,9 @@
16
16
  { "type": "progress", "label": "Credits", "scope": "overview", "primaryOrder": 2 },
17
17
  { "type": "progress", "label": "Total usage", "scope": "overview", "primaryOrder": 1 },
18
18
  { "type": "progress", "label": "Requests", "scope": "overview", "primaryOrder": 3 },
19
- { "type": "progress", "label": "Auto usage", "scope": "detail" },
20
- { "type": "progress", "label": "API usage", "scope": "detail" },
19
+ { "type": "progress", "label": "Cursor Models", "scope": "detail" },
20
+ { "type": "progress", "label": "Other Models", "scope": "detail" },
21
+ { "type": "progress", "label": "Grok Bot usage", "scope": "detail" },
21
22
  { "type": "progress", "label": "On-demand", "scope": "detail" }
22
23
  ]
23
24
  }
File without changes
File without changes
File without changes
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 32">
2
+ <title>DeepInfra</title>
3
+ <path fill="currentColor" d="M2.833 10.66A2.837 2.837 0 0 1 0 7.825a2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.555a1.723 1.723 0 0 0 0 3.445 1.724 1.724 0 0 0 0-3.445m0 12.552A2.837 2.837 0 0 1 0 15.822a2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.554a1.722 1.722 0 0 0 0 3.443 1.722 1.722 0 1 0 0-3.443m0 12.539A2.837 2.837 0 0 1 0 23.806a2.837 2.837 0 0 1 2.833-2.833 2.837 2.837 0 0 1 2.834 2.833 2.837 2.837 0 0 1-2.834 2.834m0-4.555a1.723 1.723 0 0 0 0 3.445c.95 0 1.723-.772 1.723-1.722s-.772-1.723-1.723-1.723M24.376 10.66a2.837 2.837 0 0 1-2.833-2.834 2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.555c-.948 0-1.722.772-1.722 1.722a1.724 1.724 0 0 0 3.445 0c0-.95-.772-1.722-1.723-1.722m0 12.551a2.837 2.837 0 0 1-2.833-2.833 2.837 2.837 0 0 1 2.833-2.834 2.837 2.837 0 0 1 2.834 2.834 2.837 2.837 0 0 1-2.834 2.833m0-4.554c-.948 0-1.722.772-1.722 1.72 0 .95.772 1.723 1.722 1.723s1.723-.772 1.723-1.722-.772-1.721-1.723-1.721m0 12.539a2.837 2.837 0 0 1-2.833-2.834 2.837 2.837 0 0 1 2.833-2.833 2.837 2.837 0 0 1 2.834 2.833 2.837 2.837 0 0 1-2.834 2.834m0-4.555a1.724 1.724 0 1 0 1.723 1.723c0-.95-.772-1.723-1.723-1.723m-10.771-7.43a2.837 2.837 0 0 1-2.834-2.833 2.837 2.837 0 0 1 2.834-2.834 2.837 2.837 0 0 1 2.833 2.834 2.837 2.837 0 0 1-2.833 2.833m0-4.554c-.949 0-1.723.772-1.723 1.722s.773 1.723 1.723 1.723 1.723-.772 1.723-1.723c0-.95-.773-1.722-1.723-1.722m0-3.434a2.837 2.837 0 0 1-2.834-2.834A2.837 2.837 0 0 1 13.606 1a2.837 2.837 0 0 1 2.833 2.833 2.837 2.837 0 0 1-2.833 2.834m0-4.554c-.949 0-1.723.772-1.723 1.722a1.724 1.724 0 0 0 3.446 0c0-.95-.773-1.722-1.723-1.722m-.001 20.534a2.837 2.837 0 0 1-2.834-2.833 2.837 2.837 0 0 1 2.834-2.834 2.837 2.837 0 0 1 2.833 2.834 2.837 2.837 0 0 1-2.833 2.833m0-4.554a1.724 1.724 0 0 0 0 3.445 1.724 1.724 0 0 0 0-3.445m0 12.547a2.837 2.837 0 0 1-2.834-2.834 2.837 2.837 0 0 1 2.834-2.833 2.837 2.837 0 0 1 2.833 2.833 2.837 2.837 0 0 1-2.833 2.834m0-4.555a1.724 1.724 0 1 0 1.723 1.723c0-.95-.773-1.723-1.723-1.723M9.094 11.154a.9.9 0 0 1-.342-.066l-1.845-.735a.926.926 0 1 1 .687-1.72l1.845.735a.926.926 0 0 1-.344 1.786zm0 7.835a.9.9 0 0 1-.342-.066l-1.845-.735a.925.925 0 1 1 .687-1.72l1.845.736a.925.925 0 0 1-.344 1.785zm10.82-3.914a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.736a.926.926 0 0 1-.344 1.785m0-7.835a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.735a.926.926 0 0 1-.344 1.786m0 15.668a.9.9 0 0 1-.342-.066l-1.844-.735a.926.926 0 0 1 .685-1.72l1.845.735a.926.926 0 0 1-.344 1.785m-12.616.281a.926.926 0 0 1-.344-1.785l1.845-.735a.925.925 0 1 1 .687 1.72l-1.845.734a.9.9 0 0 1-.342.066zm0-7.793a.926.926 0 0 1-.344-1.785l1.845-.735a.925.925 0 1 1 .687 1.72l-1.845.734a.9.9 0 0 1-.342.066zm0-8.18a.926.926 0 0 1-.344-1.785L8.8 4.695a.925.925 0 1 1 .687 1.72L7.64 7.15a.9.9 0 0 1-.342.065zm1.796 20.169a.9.9 0 0 1-.342-.066l-1.845-.735a.925.925 0 0 1 .686-1.72l1.844.736a.926.926 0 0 1-.343 1.785m8.976-16.229a.926.926 0 0 1-.344-1.787l1.844-.735a.927.927 0 0 1 .687 1.72l-1.844.736a.9.9 0 0 1-.342.066zm0 7.835a.926.926 0 0 1-.344-1.786l1.844-.736a.927.927 0 0 1 .687 1.721l-1.844.736a.9.9 0 0 1-.342.066zm0 8.393a.925.925 0 0 1-.343-1.785l1.843-.735a.925.925 0 1 1 .686 1.72l-1.845.735a.9.9 0 0 1-.342.065"/>
4
+ </svg>
@@ -0,0 +1,97 @@
1
+ (function () {
2
+ var CHECKLIST_URL = "https://api.deepinfra.com/payment/checklist?compute_owed=true";
3
+ var USAGE_URL = "https://api.deepinfra.com/payment/usage?from=current";
4
+
5
+ function clean(value) {
6
+ if (typeof value !== "string") return null;
7
+ var v = value.trim();
8
+ if (!v) return null;
9
+ if ((v[0] === "\"" && v[v.length - 1] === "\"") || (v[0] === "'" && v[v.length - 1] === "'")) v = v.slice(1, -1).trim();
10
+ if (v.slice(0, 7).toLowerCase() === "bearer ") v = v.slice(7).trim();
11
+ return v || null;
12
+ }
13
+
14
+ function env(ctx, name) {
15
+ try {
16
+ return clean(ctx.host.env.get(name));
17
+ } catch (_) {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ function apiKey(ctx) {
23
+ return clean(ctx.provider && ctx.provider.apiKey) || env(ctx, "DEEPINFRA_API_KEY") || env(ctx, "DEEPINFRA_TOKEN");
24
+ }
25
+
26
+ function requestJson(ctx, url, key, label, timeoutMs) {
27
+ var result = ctx.util.requestJson({
28
+ method: "GET",
29
+ url: url,
30
+ headers: { Authorization: "Bearer " + key, Accept: "application/json" },
31
+ timeoutMs: timeoutMs || 15000,
32
+ });
33
+ if (result.resp.status === 401) throw "DeepInfra API key rejected.";
34
+ if (result.resp.status === 403) throw "DeepInfra API key cannot access billing data.";
35
+ if (result.resp.status < 200 || result.resp.status >= 300) throw "DeepInfra " + label + " API error: HTTP " + result.resp.status + ".";
36
+ if (!result.json || typeof result.json !== "object") throw "Failed to parse DeepInfra " + label + " response.";
37
+ return result.json;
38
+ }
39
+
40
+ function number(value) {
41
+ if (typeof value === "number" && Number.isFinite(value)) return value;
42
+ if (typeof value === "string" && value.trim()) {
43
+ var parsed = Number(value.replace(/,/g, ""));
44
+ if (Number.isFinite(parsed)) return parsed;
45
+ }
46
+ return null;
47
+ }
48
+
49
+ function probe(ctx) {
50
+ var key = apiKey(ctx);
51
+ if (!key) throw "DeepInfra API key not found. Set DEEPINFRA_API_KEY, DEEPINFRA_TOKEN, or provider apiKey.";
52
+
53
+ var checklist = requestJson(ctx, CHECKLIST_URL, key, "billing");
54
+ var recent = number(checklist.recent);
55
+ var stripeBalance = number(checklist.stripe_balance);
56
+ if (recent === null || recent < 0 || stripeBalance === null) throw "DeepInfra returned invalid billing amounts.";
57
+ var currentMonth = null;
58
+ try {
59
+ var usage = requestJson(ctx, USAGE_URL, key, "usage", 3000);
60
+ if (Array.isArray(usage.months) && usage.months.length) {
61
+ var cents = number(usage.months[usage.months.length - 1].total_cost);
62
+ if (cents !== null && cents >= 0) currentMonth = cents / 100;
63
+ }
64
+ } catch (error) {
65
+ ctx.host.log.warn("DeepInfra monthly usage unavailable: " + String(error));
66
+ }
67
+ var netBalance = stripeBalance + recent;
68
+ var available = Math.max(0, -netBalance);
69
+ var owed = Math.max(0, netBalance);
70
+ var limit = number(checklist.limit);
71
+ var suspended = checklist.suspended === true;
72
+ var balanceText = owed > 0 ? "$" + owed.toFixed(2) + " owed" : "$" + available.toFixed(2) + " available";
73
+ var detail = balanceText + (currentMonth === null ? "" : " · $" + currentMonth.toFixed(2) + " spent this month");
74
+ if (suspended) detail = "Suspended" + (checklist.suspend_reason ? ": " + checklist.suspend_reason : "") + " · " + detail;
75
+
76
+ var metrics = [];
77
+ if (limit > 0 && Number.isFinite(limit)) {
78
+ metrics.push(ctx.line.progress({
79
+ label: "Billing Cycle",
80
+ used: Math.max(0, recent),
81
+ limit: limit,
82
+ format: { kind: "dollars" },
83
+ detail: detail,
84
+ }));
85
+ } else {
86
+ metrics.push(ctx.line.text({ label: "Balance", value: balanceText }));
87
+ }
88
+ if (currentMonth !== null) metrics.push(ctx.line.text({ label: "Month Cost", value: "$" + currentMonth.toFixed(2) }));
89
+ if (suspended || owed > 0 || available <= 0) {
90
+ metrics.push(ctx.line.badge({ label: "Status", text: suspended ? "Suspended" : "No prepaid balance", color: "#ef4444" }));
91
+ }
92
+
93
+ return { displayName: "DeepInfra", source: "api", lines: metrics };
94
+ }
95
+
96
+ globalThis.__openusage_plugin = { id: "deepinfra", probe: probe };
97
+ })();
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "deepinfra",
4
+ "name": "DeepInfra",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "api"
10
+ ],
11
+ "autoMode": "api",
12
+ "usageDashboardUrl": "https://deepinfra.com/dash",
13
+ "statusPageUrl": "https://status.deepinfra.com",
14
+ "links": [
15
+ {
16
+ "label": "Dashboard",
17
+ "url": "https://deepinfra.com/dash"
18
+ },
19
+ {
20
+ "label": "Status",
21
+ "url": "https://status.deepinfra.com"
22
+ }
23
+ ],
24
+ "envVars": [
25
+ "DEEPINFRA_API_KEY",
26
+ "DEEPINFRA_TOKEN"
27
+ ]
28
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -305,8 +305,119 @@
305
305
  return auth.apiKey + "\n" + effectiveApiServerUrl(auth)
306
306
  }
307
307
 
308
+ function firstPresent(values) {
309
+ for (var i = 0; i < values.length; i++) if (values[i] !== null && values[i] !== undefined) return values[i]
310
+ return null
311
+ }
312
+
313
+ function manualOrganization(raw) {
314
+ if (typeof raw !== "string") throw {code: "missing-auth", message: "Set Devin workspace_id or DEVIN_ORGANIZATION to the intended organization."}
315
+ var value = raw.trim().replace(/^\/+|\/+$/g, "")
316
+ if (/^https:\/\//i.test(value)) {
317
+ var url = /^https:\/\/(?:[a-z0-9-]+\.)*devin\.ai\/(org|organizations)\/([^/?#]+)(?:[/?#].*)?$/i.exec(value)
318
+ if (!url) throw {code: "failed", message: "Use an app.devin.ai organization URL, organization ID, or slug."}
319
+ value = url[1].toLowerCase() + "/" + url[2]
320
+ }
321
+ var parts = value.split("/")
322
+ var id = parts.length === 1 ? parts[0] : parts[1]
323
+ if (parts.length > 2 || (parts.length === 2 && !/^(org|organizations)$/.test(parts[0])) || !/^[a-z0-9][a-z0-9_.-]*$/i.test(id)) {
324
+ throw {code: "failed", message: "Invalid Devin organization; use an organization ID, slug, or app.devin.ai organization URL."}
325
+ }
326
+ var internal = parts.length === 2 ? parts[0] === "organizations" : /^org[-_]/.test(id)
327
+ return { id: id, internal: internal, path: (internal ? "organizations/" : "org/") + id }
328
+ }
329
+
330
+ function manualQuotaPercent(value) {
331
+ var number = readFiniteNumber(value)
332
+ if (number !== null) return number <= 1 ? number * 100 : number
333
+ if (!value || typeof value !== "object") return null
334
+ var usedPercent = firstPresent([value.used_percent, value.usedPercent, value.usage_percent, value.usagePercent, value.percent_used, value.percentUsed, value.percent])
335
+ if (usedPercent !== null) return manualQuotaPercent(usedPercent)
336
+ var remainingPercent = firstPresent([value.remaining_percent, value.remainingPercent, value.percent_remaining, value.percentRemaining])
337
+ if (remainingPercent !== null) {
338
+ var remaining = manualQuotaPercent(remainingPercent)
339
+ return remaining === null ? null : 100 - remaining
340
+ }
341
+ var limit = readFiniteNumber(firstPresent([value.limit, value.quota, value.total, value.max]))
342
+ var used = readFiniteNumber(firstPresent([value.used, value.usage, value.used_count, value.usedCount, value.consumed]))
343
+ if (limit !== null && limit > 0 && used !== null) return used / limit * 100
344
+ remaining = readFiniteNumber(firstPresent([value.remaining, value.left]))
345
+ return limit !== null && limit > 0 && remaining !== null ? (limit - remaining) / limit * 100 : null
346
+ }
347
+
348
+ function manualWindow(ctx, data, cadence, depth) {
349
+ if (!data || typeof data !== "object" || depth > 8) return null
350
+ var current = readFiniteNumber(data[cadence + "_percentage"])
351
+ if (current !== null) {
352
+ var currentPercent = current < 1 ? current * 100 : current
353
+ if (Number.isFinite(currentPercent)) return {used: currentPercent, reset: ctx.util.toIso(data[cadence + "_reset_at"])}
354
+ }
355
+ var singular = cadence === "daily" ? "day" : "week"
356
+ var window = firstPresent([data[cadence], data[singular], data[cadence + "_quota"], data[cadence + "Quota"], data[cadence + "_usage"], data[cadence + "Usage"]])
357
+ var percent = manualQuotaPercent(window)
358
+ if (percent !== null && Number.isFinite(percent)) {
359
+ return {used: percent, reset: ctx.util.toIso(window && typeof window === "object" ? firstPresent([window.resetsAt, window.reset_at, window.resetAt, window.reset_time]) : null)}
360
+ }
361
+ var keys = Object.keys(data)
362
+ for (var i = 0; i < keys.length; i++) {
363
+ var nested = manualWindow(ctx, data[keys[i]], cadence, depth + 1)
364
+ if (nested) return nested
365
+ }
366
+ return null
367
+ }
368
+
369
+ function manualOutput(ctx, data) {
370
+ var daily = manualWindow(ctx, data, "daily", 0)
371
+ var weekly = manualWindow(ctx, data, "weekly", 0)
372
+ var lines = []
373
+ if (daily) lines.push(buildUsedQuotaLine(ctx, "Daily quota", daily.used, daily.reset, DAY_MS))
374
+ if (weekly) lines.push(buildUsedQuotaLine(ctx, "Weekly quota", weekly.used, weekly.reset, WEEK_MS))
375
+ if (!lines.length) throw {code: "no-data", message: "Devin web response did not contain measurable daily or weekly quotas."}
376
+ var balance = readFiniteNumber(data.overage_balance)
377
+ if (balance === null) {
378
+ var cents = readFiniteNumber(data.overage_balance_cents)
379
+ if (cents !== null) balance = cents / 100
380
+ }
381
+ if (balance !== null && balance >= 0) lines.push(ctx.line.text({label: "Extra usage balance", value: "$" + balance.toFixed(2)}))
382
+ var plan = firstPresent([data.plan_name, data.planName, data.plan, data.tier, data.subscription_tier])
383
+ return {source: "web", plan: typeof plan === "string" && plan.trim() ? plan.trim() : null, lines: lines}
384
+ }
385
+
386
+ function probeManual(ctx, opts) {
387
+ if (opts.cookieSource === "off") throw {code: "missing-auth", message: "Devin web authentication is disabled in settings.cookieSource."}
388
+ if (opts.credentialsPath || opts.ideVariant || opts.userDataDir || (opts.authSource && ["auto", "manual"].indexOf(opts.authSource) < 0)) {
389
+ throw {code: "failed", message: "Select either Devin manual web authentication or a CLI/IDE credential source."}
390
+ }
391
+ var raw = firstPresent([ctx.host.env.get("DEVIN_BEARER_TOKEN"), ctx.host.env.get("DEVIN_AUTHORIZATION"), ctx.provider.cookieHeader, opts.bearerToken])
392
+ var token = typeof raw === "string" ? raw.trim().replace(/^Authorization:\s*/i, "").replace(/^Bearer(?:\s+|$)/i, "").trim() : ""
393
+ if (!token || /[^\x21-\x7e]/.test(token)) throw {code: "missing-auth", message: "Set a valid Devin bearer token in cookie_header or DEVIN_BEARER_TOKEN."}
394
+ var org = manualOrganization(firstPresent([ctx.host.env.get("DEVIN_ORGANIZATION"), ctx.host.env.get("DEVIN_ORG"), ctx.provider.workspaceId, opts.organization]))
395
+ var paths = org.internal ? [org.id, org.path] : [org.path, org.id]
396
+ for (var i = 0; i < paths.length; i++) {
397
+ var headers = {Authorization: "Bearer " + token, Accept: "application/json", "Accept-Language": "en-US,en;q=0.9", "User-Agent": "usagestat/" + ctx.app.version}
398
+ if (org.internal) headers["x-cog-org-id"] = org.id
399
+ var response = ctx.util.request({method: "GET", url: "https://app.devin.ai/api/" + paths[i] + "/billing/quota/usage", headers: headers, timeoutMs: 10000})
400
+ if (response.status === 401 || response.status === 403) throw {code: "credential-denied", message: "Devin web token was rejected. Reauthenticate the selected organization."}
401
+ // Alternate routes refer to the same organization; only missing routes
402
+ // permit retry. A rejected token never selects a CLI or IDE account.
403
+ if (response.status === 404 || response.status === 405) continue
404
+ if (response.status !== 200) throw {code: "failed", message: "Devin quota request failed (HTTP " + response.status + ")."}
405
+ var data = ctx.util.tryParseJson(response.bodyText)
406
+ if (!data || typeof data !== "object") throw {code: "no-data", message: "Invalid Devin quota response."}
407
+ return manualOutput(ctx, data)
408
+ }
409
+ throw {code: "no-data", message: "Devin quota endpoint was not found for the selected organization."}
410
+ }
411
+
308
412
  function probe(ctx) {
309
413
  var opts = settings(ctx)
414
+ var mode = ctx.sourceMode || "auto"
415
+ var manual = mode === "web" || opts.authSource === "manual" || opts.cookieSource === "manual" ||
416
+ (mode === "auto" && !opts.authSource && opts.cookieSource == null && (ctx.provider.cookieHeader != null || opts.bearerToken != null))
417
+ if (manual) {
418
+ if (mode === "local") throw {code: "failed", message: "Select web or auto mode to use Devin manual web authentication."}
419
+ return probeManual(ctx, opts)
420
+ }
310
421
  var source = opts.authSource || (opts.credentialsPath ? "cli" : opts.ideVariant || opts.userDataDir ? "ide" : "auto")
311
422
  if (["auto", "cli", "ide"].indexOf(source) < 0) throw {code: "failed", message: "Set Devin settings.authSource to auto, cli or ide."}
312
423
  if ((opts.credentialsPath && source !== "cli") || ((opts.ideVariant || opts.userDataDir) && source !== "ide")) {
@@ -6,8 +6,9 @@
6
6
  "entry": "plugin.js",
7
7
  "icon": "icon.svg",
8
8
  "brandColor": "#000000",
9
- "supportedModes": ["local"],
9
+ "supportedModes": ["local", "web"],
10
10
  "autoMode": "local",
11
+ "envVars": ["DEVIN_BEARER_TOKEN", "DEVIN_AUTHORIZATION", "DEVIN_ORGANIZATION", "DEVIN_ORG"],
11
12
  "lines": [
12
13
  { "type": "progress", "label": "Weekly quota", "scope": "overview", "primaryOrder": 1 },
13
14
  { "type": "progress", "label": "Daily quota", "scope": "overview" },
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -10,7 +10,8 @@
10
10
  { "label": "Factory", "url": "https://app.factory.ai" },
11
11
  { "label": "Status", "url": "https://status.factory.ai" }
12
12
  ],
13
- "supportedModes": ["oauth", "local"],
13
+ "supportedModes": ["api", "oauth", "local", "web"],
14
+ "envVars": ["FACTORY_API_KEY", "FACTORY_COOKIE", "DROID_COOKIE"],
14
15
  "autoMode": "oauth",
15
16
  "webUrl": "https://app.factory.ai",
16
17
  "lines": [
File without changes
@@ -1,12 +1,27 @@
1
1
  (function () {
2
2
  function apiKey(ctx) {
3
- return ctx.host.env.get("ELEVENLABS_API_KEY") || ctx.host.env.get("XI_API_KEY");
3
+ return (ctx.provider && ctx.provider.apiKey) || ctx.host.env.get("ELEVENLABS_API_KEY") || ctx.host.env.get("XI_API_KEY");
4
4
  }
5
5
 
6
6
  function apiBase(ctx) {
7
7
  return ctx.host.env.get("ELEVENLABS_API_URL") || "https://api.elevenlabs.io";
8
8
  }
9
9
 
10
+ function authError(status, json) {
11
+ var detail = json && json.detail;
12
+ var codes = detail && typeof detail === "object" ? [detail.code, detail.status] : [];
13
+ for (var i = 0; i < codes.length; i++) {
14
+ var code = typeof codes[i] === "string" ? codes[i].trim().toLowerCase() : "";
15
+ if (code === "invalid_api_key") return "ElevenLabs rejected the API key. Check whether it is valid or revoked.";
16
+ if (code === "missing_permissions" || code === "insufficient_permissions") {
17
+ return "ElevenLabs API key needs the user_read permission to fetch subscription usage.";
18
+ }
19
+ }
20
+ return status === 403
21
+ ? "ElevenLabs denied access. Check the API key's endpoint permissions and IP allowlist."
22
+ : "ElevenLabs could not authenticate the API key. Check the key and its permissions.";
23
+ }
24
+
10
25
  function probe(ctx) {
11
26
  var key = apiKey(ctx);
12
27
  if (!key) throw "ElevenLabs API key not found. Set ELEVENLABS_API_KEY or XI_API_KEY.";
@@ -17,7 +32,7 @@
17
32
  headers: { "xi-api-key": key, Accept: "application/json" },
18
33
  timeoutMs: 15000,
19
34
  });
20
- if (ctx.util.isAuthStatus(result.resp.status)) throw "ElevenLabs API key invalid or expired.";
35
+ if (ctx.util.isAuthStatus(result.resp.status)) throw authError(result.resp.status, result.json);
21
36
  if (result.resp.status < 200 || result.resp.status >= 300) throw "ElevenLabs API error (HTTP " + result.resp.status + ").";
22
37
  if (!result.json) throw "Could not parse ElevenLabs subscription response.";
23
38
 
File without changes
File without changes
File without changes