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

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 (384) hide show
  1. package/LICENSE +0 -0
  2. package/native-manifest.json +1088 -134
  3. package/package.json +160 -1
  4. package/plugins/UPSTREAM-LICENSES.md +113 -0
  5. package/plugins/_provider-icons/LICENSE +21 -0
  6. package/plugins/_provider-icons/NOTICE +12 -0
  7. package/plugins/_provider-icons/licenses/CodexBar-MIT.txt +21 -0
  8. package/plugins/_provider-icons/licenses/LobeHub-MIT.txt +21 -0
  9. package/plugins/_provider-icons/licenses/UsageStat-Bar-MIT.txt +21 -0
  10. package/plugins/_provider-icons/manifest.json +1219 -0
  11. package/plugins/_provider-icons/provenance.json +269 -0
  12. package/plugins/_provider-icons/source.json +4 -0
  13. package/plugins/abacus-ai/icon-color.svg +18 -0
  14. package/plugins/abacus-ai/icon.svg +0 -0
  15. package/plugins/abacus-ai/plugin.js +0 -0
  16. package/plugins/abacus-ai/plugin.json +0 -0
  17. package/plugins/aiand/icon.svg +6 -0
  18. package/plugins/aiand/plugin.js +108 -0
  19. package/plugins/aiand/plugin.json +22 -0
  20. package/plugins/aixy/LICENSE +21 -0
  21. package/plugins/aixy/plugin.js +261 -0
  22. package/plugins/aixy/plugin.json +24 -0
  23. package/plugins/alibaba/icon-color.svg +0 -0
  24. package/plugins/alibaba/icon.svg +0 -0
  25. package/plugins/alibaba/plugin.js +0 -0
  26. package/plugins/alibaba/plugin.json +0 -0
  27. package/plugins/alibaba-token-plan/icon-color.svg +0 -0
  28. package/plugins/alibaba-token-plan/icon.svg +0 -0
  29. package/plugins/alibaba-token-plan/plugin.js +0 -0
  30. package/plugins/alibaba-token-plan/plugin.json +0 -0
  31. package/plugins/amp/icon-color.svg +0 -0
  32. package/plugins/amp/icon-conflict.svg +0 -0
  33. package/plugins/amp/icon.svg +0 -0
  34. package/plugins/amp/plugin.js +0 -0
  35. package/plugins/amp/plugin.json +0 -0
  36. package/plugins/antigravity/icon-color.svg +0 -0
  37. package/plugins/antigravity/icon-conflict.svg +0 -0
  38. package/plugins/antigravity/icon.svg +0 -0
  39. package/plugins/antigravity/plugin.js +136 -46
  40. package/plugins/antigravity/plugin.json +0 -0
  41. package/plugins/antigravity-cli/icon-color.svg +1 -0
  42. package/plugins/antigravity-cli/icon.svg +1 -3
  43. package/plugins/antigravity-cli/plugin.js +11 -15
  44. package/plugins/antigravity-cli/plugin.json +0 -0
  45. package/plugins/antigravity-ide/icon-color.svg +1 -0
  46. package/plugins/antigravity-ide/icon.svg +1 -3
  47. package/plugins/antigravity-ide/plugin.js +0 -0
  48. package/plugins/antigravity-ide/plugin.json +0 -0
  49. package/plugins/atlascloud/LICENSE +21 -0
  50. package/plugins/atlascloud/icon-color.svg +1 -0
  51. package/plugins/atlascloud/icon.svg +1 -0
  52. package/plugins/atlascloud/plugin.js +49 -0
  53. package/plugins/atlascloud/plugin.json +23 -0
  54. package/plugins/augment/icon-color.svg +14 -0
  55. package/plugins/augment/icon.svg +0 -0
  56. package/plugins/augment/plugin.js +0 -0
  57. package/plugins/augment/plugin.json +0 -0
  58. package/plugins/aws-bedrock/icon-color.svg +0 -0
  59. package/plugins/aws-bedrock/icon.svg +0 -0
  60. package/plugins/aws-bedrock/plugin.js +0 -0
  61. package/plugins/aws-bedrock/plugin.json +0 -0
  62. package/plugins/azure-openai/icon-color.svg +0 -0
  63. package/plugins/azure-openai/icon.svg +0 -0
  64. package/plugins/azure-openai/plugin.js +0 -0
  65. package/plugins/azure-openai/plugin.json +0 -0
  66. package/plugins/bifrost/LICENSE +21 -0
  67. package/plugins/bifrost/plugin.js +361 -0
  68. package/plugins/bifrost/plugin.json +23 -0
  69. package/plugins/chutes/icon.svg +0 -0
  70. package/plugins/chutes/plugin.js +0 -0
  71. package/plugins/chutes/plugin.json +0 -0
  72. package/plugins/claude/icon-color.svg +0 -0
  73. package/plugins/claude/icon.svg +0 -0
  74. package/plugins/claude/plugin.js +358 -161
  75. package/plugins/claude/plugin.json +1 -1
  76. package/plugins/clawrouter/icon.svg +7 -0
  77. package/plugins/clawrouter/plugin.js +45 -0
  78. package/plugins/clawrouter/plugin.json +22 -0
  79. package/plugins/clinepass/icon.svg +4 -0
  80. package/plugins/clinepass/plugin.js +87 -0
  81. package/plugins/clinepass/plugin.json +23 -0
  82. package/plugins/codebuddy/plugin.js +331 -0
  83. package/plugins/codebuddy/plugin.json +30 -0
  84. package/plugins/codebuff/icon-color.svg +92 -0
  85. package/plugins/codebuff/icon.svg +0 -0
  86. package/plugins/codebuff/plugin.js +0 -0
  87. package/plugins/codebuff/plugin.json +0 -0
  88. package/plugins/coderabbit/plugin.js +25 -0
  89. package/plugins/coderabbit/plugin.json +12 -0
  90. package/plugins/codex/icon-color.svg +0 -0
  91. package/plugins/codex/icon.svg +0 -0
  92. package/plugins/codex/plugin.js +8 -2
  93. package/plugins/codex/plugin.json +0 -0
  94. package/plugins/command-code/icon-color.svg +3 -0
  95. package/plugins/command-code/icon.svg +0 -0
  96. package/plugins/command-code/plugin.js +14 -3
  97. package/plugins/command-code/plugin.json +0 -0
  98. package/plugins/copilot/icon-color.svg +0 -0
  99. package/plugins/copilot/icon.svg +1 -1
  100. package/plugins/copilot/plugin.js +61 -9
  101. package/plugins/copilot/plugin.json +4 -1
  102. package/plugins/crof/icon-color.svg +3 -0
  103. package/plugins/crof/icon.svg +0 -0
  104. package/plugins/crof/plugin.js +0 -0
  105. package/plugins/crof/plugin.json +0 -0
  106. package/plugins/crofai/icon-color.svg +3 -0
  107. package/plugins/crofai/icon.svg +2 -15
  108. package/plugins/crofai/plugin.js +0 -0
  109. package/plugins/crofai/plugin.json +0 -0
  110. package/plugins/cursor/icon-color.svg +1 -0
  111. package/plugins/cursor/icon-conflict.svg +0 -0
  112. package/plugins/cursor/icon.svg +0 -0
  113. package/plugins/cursor/plugin.js +46 -9
  114. package/plugins/cursor/plugin.json +3 -2
  115. package/plugins/cursor-nightly/icon.png +0 -0
  116. package/plugins/cursor-nightly/plugin.js +45 -8
  117. package/plugins/cursor-nightly/plugin.json +3 -2
  118. package/plugins/deepgram/icon.svg +0 -0
  119. package/plugins/deepgram/plugin.js +0 -0
  120. package/plugins/deepgram/plugin.json +0 -0
  121. package/plugins/deepinfra/icon-color.svg +1 -0
  122. package/plugins/deepinfra/icon.svg +1 -0
  123. package/plugins/deepinfra/plugin.js +97 -0
  124. package/plugins/deepinfra/plugin.json +28 -0
  125. package/plugins/deepseek/icon-color.svg +0 -0
  126. package/plugins/deepseek/icon.svg +0 -0
  127. package/plugins/deepseek/plugin.js +150 -59
  128. package/plugins/deepseek/plugin.json +3 -1
  129. package/plugins/devin/icon.svg +0 -0
  130. package/plugins/devin/plugin.js +114 -0
  131. package/plugins/devin/plugin.json +2 -1
  132. package/plugins/devpass/LICENSE +21 -0
  133. package/plugins/devpass/plugin.js +115 -0
  134. package/plugins/devpass/plugin.json +23 -0
  135. package/plugins/doubao/icon-color.svg +0 -0
  136. package/plugins/doubao/icon.svg +0 -0
  137. package/plugins/doubao/plugin.js +0 -0
  138. package/plugins/doubao/plugin.json +0 -0
  139. package/plugins/droid/icon.svg +0 -0
  140. package/plugins/droid/plugin.json +2 -1
  141. package/plugins/elevenlabs/icon-color.svg +4 -0
  142. package/plugins/elevenlabs/icon.svg +0 -0
  143. package/plugins/elevenlabs/plugin.js +17 -2
  144. package/plugins/elevenlabs/plugin.json +0 -0
  145. package/plugins/factory/icon-color.svg +1 -0
  146. package/plugins/factory/icon-conflict.svg +0 -0
  147. package/plugins/factory/icon.svg +0 -0
  148. package/plugins/factory/plugin.js +38 -14
  149. package/plugins/factory/plugin.json +2 -1
  150. package/plugins/fireworks-ai/icon-color.svg +1 -0
  151. package/plugins/fireworks-ai/icon.svg +1 -6
  152. package/plugins/fireworks-ai/plugin.js +0 -0
  153. package/plugins/fireworks-ai/plugin.json +0 -0
  154. package/plugins/gemini/icon-color.svg +0 -0
  155. package/plugins/gemini/icon.svg +0 -0
  156. package/plugins/gemini/plugin.js +0 -0
  157. package/plugins/gemini/plugin.json +0 -0
  158. package/plugins/gemini-apps/LICENSE +22 -0
  159. package/plugins/gemini-apps/icon-color.svg +1 -0
  160. package/plugins/gemini-apps/icon.svg +1 -0
  161. package/plugins/gemini-apps/plugin.js +470 -0
  162. package/plugins/gemini-apps/plugin.json +19 -0
  163. package/plugins/gitkraken/LICENSE +21 -0
  164. package/plugins/gitkraken/plugin.js +153 -0
  165. package/plugins/gitkraken/plugin.json +24 -0
  166. package/plugins/grok/icon-color.svg +1 -0
  167. package/plugins/grok/icon.svg +0 -0
  168. package/plugins/grok/plugin.js +30 -5
  169. package/plugins/grok/plugin.json +3 -1
  170. package/plugins/groqcloud/icon-color.svg +1 -0
  171. package/plugins/groqcloud/icon.svg +0 -0
  172. package/plugins/groqcloud/plugin.js +0 -0
  173. package/plugins/groqcloud/plugin.json +0 -0
  174. package/plugins/helmcode/LICENSE +21 -0
  175. package/plugins/helmcode/plugin.js +188 -0
  176. package/plugins/helmcode/plugin.json +24 -0
  177. package/plugins/huggingface/LICENSE +21 -0
  178. package/plugins/huggingface/icon-color.svg +1 -0
  179. package/plugins/huggingface/icon.svg +1 -0
  180. package/plugins/huggingface/plugin.js +293 -0
  181. package/plugins/huggingface/plugin.json +25 -0
  182. package/plugins/hyper/LICENSE +21 -0
  183. package/plugins/hyper/plugin.js +131 -0
  184. package/plugins/hyper/plugin.json +26 -0
  185. package/plugins/ibmbob/icon.svg +23 -0
  186. package/plugins/ibmbob/plugin.js +61 -0
  187. package/plugins/ibmbob/plugin.json +22 -0
  188. package/plugins/jetbrains-ai-assistant/icon-color.svg +0 -0
  189. package/plugins/jetbrains-ai-assistant/icon-conflict.svg +0 -0
  190. package/plugins/jetbrains-ai-assistant/icon.svg +0 -0
  191. package/plugins/jetbrains-ai-assistant/plugin.js +0 -0
  192. package/plugins/jetbrains-ai-assistant/plugin.json +0 -0
  193. package/plugins/kilo/icon-color.svg +1 -0
  194. package/plugins/kilo/icon.svg +0 -0
  195. package/plugins/kilo/plugin.js +0 -0
  196. package/plugins/kilo/plugin.json +0 -0
  197. package/plugins/kimi/icon-color.svg +0 -0
  198. package/plugins/kimi/icon-conflict.svg +0 -0
  199. package/plugins/kimi/icon.svg +0 -0
  200. package/plugins/kimi/plugin.js +122 -141
  201. package/plugins/kimi/plugin.json +5 -2
  202. package/plugins/kimi-k2/icon-color.svg +0 -0
  203. package/plugins/kimi-k2/icon.svg +0 -0
  204. package/plugins/kimi-k2/plugin.js +0 -0
  205. package/plugins/kimi-k2/plugin.json +0 -0
  206. package/plugins/kiro/icon-color.svg +0 -0
  207. package/plugins/kiro/icon-conflict.svg +0 -0
  208. package/plugins/kiro/icon.svg +0 -0
  209. package/plugins/kiro/plugin.js +49 -10
  210. package/plugins/kiro/plugin.json +3 -1
  211. package/plugins/litellm/icon.svg +0 -0
  212. package/plugins/litellm/plugin.js +54 -3
  213. package/plugins/litellm/plugin.json +0 -0
  214. package/plugins/llm-proxy/icon.svg +0 -0
  215. package/plugins/llm-proxy/plugin.js +0 -0
  216. package/plugins/llm-proxy/plugin.json +0 -0
  217. package/plugins/llmman/LICENSE +21 -0
  218. package/plugins/llmman/plugin.js +133 -0
  219. package/plugins/llmman/plugin.json +23 -0
  220. package/plugins/longcat/icon-color.svg +1 -0
  221. package/plugins/longcat/icon.svg +1 -0
  222. package/plugins/longcat/plugin.js +182 -0
  223. package/plugins/longcat/plugin.json +23 -0
  224. package/plugins/manus/icon-color.svg +6 -0
  225. package/plugins/manus/icon.svg +0 -0
  226. package/plugins/manus/plugin.js +0 -0
  227. package/plugins/manus/plugin.json +0 -0
  228. package/plugins/mimo/icon-color.svg +4 -0
  229. package/plugins/mimo/icon.svg +4 -1
  230. package/plugins/mimo/plugin.js +0 -0
  231. package/plugins/mimo/plugin.json +0 -0
  232. package/plugins/minimax/icon-color.svg +0 -0
  233. package/plugins/minimax/icon-conflict.svg +0 -0
  234. package/plugins/minimax/icon.svg +0 -0
  235. package/plugins/minimax/plugin.js +25 -31
  236. package/plugins/minimax/plugin.json +0 -0
  237. package/plugins/mistral/icon-color.svg +0 -0
  238. package/plugins/mistral/icon.svg +0 -0
  239. package/plugins/mistral/plugin.js +110 -5
  240. package/plugins/mistral/plugin.json +0 -0
  241. package/plugins/moonshot/icon-color.svg +1 -0
  242. package/plugins/moonshot/icon.svg +0 -0
  243. package/plugins/moonshot/plugin.js +8 -6
  244. package/plugins/moonshot/plugin.json +0 -0
  245. package/plugins/muse/LICENSE +21 -0
  246. package/plugins/muse/plugin.js +319 -0
  247. package/plugins/muse/plugin.json +26 -0
  248. package/plugins/nanogpt/icon-color.svg +0 -0
  249. package/plugins/nanogpt/icon.svg +0 -0
  250. package/plugins/nanogpt/plugin.js +0 -0
  251. package/plugins/nanogpt/plugin.json +0 -0
  252. package/plugins/neuralwatt/icon.svg +0 -0
  253. package/plugins/neuralwatt/plugin.js +0 -0
  254. package/plugins/neuralwatt/plugin.json +0 -0
  255. package/plugins/notion/icon.svg +1 -0
  256. package/plugins/notion/plugin.js +282 -0
  257. package/plugins/notion/plugin.json +29 -0
  258. package/plugins/nous/LICENSE +21 -0
  259. package/plugins/nous/icon-color.svg +1 -0
  260. package/plugins/nous/icon.svg +1 -0
  261. package/plugins/nous/plugin.js +124 -0
  262. package/plugins/nous/plugin.json +24 -0
  263. package/plugins/ollama/icon-color.svg +1 -0
  264. package/plugins/ollama/icon.svg +0 -0
  265. package/plugins/ollama/plugin.js +257 -56
  266. package/plugins/ollama/plugin.json +6 -3
  267. package/plugins/openai-api/icon-color.svg +1 -0
  268. package/plugins/openai-api/icon.svg +0 -0
  269. package/plugins/openai-api/plugin.js +0 -0
  270. package/plugins/openai-api/plugin.json +0 -0
  271. package/plugins/opencode/icon-color.svg +1 -0
  272. package/plugins/opencode/icon.svg +0 -0
  273. package/plugins/opencode/plugin.js +0 -0
  274. package/plugins/opencode/plugin.json +0 -0
  275. package/plugins/opencode-go/icon-color.svg +1 -0
  276. package/plugins/opencode-go/icon-conflict.svg +0 -0
  277. package/plugins/opencode-go/icon.svg +0 -0
  278. package/plugins/opencode-go/plugin.js +103 -17
  279. package/plugins/opencode-go/plugin.json +5 -2
  280. package/plugins/openrouter/icon-color.svg +1 -0
  281. package/plugins/openrouter/icon.svg +0 -0
  282. package/plugins/openrouter/plugin.js +371 -114
  283. package/plugins/openrouter/plugin.json +6 -0
  284. package/plugins/perplexity/icon-color.svg +0 -0
  285. package/plugins/perplexity/icon-conflict.svg +0 -0
  286. package/plugins/perplexity/icon.svg +0 -0
  287. package/plugins/perplexity/plugin.js +0 -0
  288. package/plugins/perplexity/plugin.json +0 -0
  289. package/plugins/pi/plugin.js +113 -0
  290. package/plugins/pi/plugin.json +11 -0
  291. package/plugins/poe/icon.svg +0 -0
  292. package/plugins/poe/plugin.js +24 -18
  293. package/plugins/poe/plugin.json +2 -0
  294. package/plugins/qoder/icon.svg +3 -0
  295. package/plugins/qoder/plugin.js +227 -0
  296. package/plugins/qoder/plugin.json +23 -0
  297. package/plugins/qwencloud/icon-color.svg +1 -0
  298. package/plugins/qwencloud/icon.svg +1 -0
  299. package/plugins/qwencloud/plugin.js +677 -0
  300. package/plugins/qwencloud/plugin.json +29 -0
  301. package/plugins/raycast/LICENSE +21 -0
  302. package/plugins/raycast/plugin.js +174 -0
  303. package/plugins/raycast/plugin.json +24 -0
  304. package/plugins/replicate/LICENSE +21 -0
  305. package/plugins/replicate/icon-color.svg +1 -0
  306. package/plugins/replicate/icon.svg +1 -0
  307. package/plugins/replicate/plugin.js +165 -0
  308. package/plugins/replicate/plugin.json +24 -0
  309. package/plugins/sakana/icon.svg +5 -0
  310. package/plugins/sakana/plugin.js +156 -0
  311. package/plugins/sakana/plugin.json +23 -0
  312. package/plugins/stepfun/icon-color.svg +0 -0
  313. package/plugins/stepfun/icon.svg +0 -0
  314. package/plugins/stepfun/plugin.js +0 -0
  315. package/plugins/stepfun/plugin.json +0 -0
  316. package/plugins/sub2api/icon.svg +4 -0
  317. package/plugins/sub2api/plugin.js +191 -0
  318. package/plugins/sub2api/plugin.json +16 -0
  319. package/plugins/synthetic/icon-color.svg +14 -0
  320. package/plugins/synthetic/icon-conflict.svg +0 -0
  321. package/plugins/synthetic/icon.svg +0 -0
  322. package/plugins/synthetic/plugin.js +0 -0
  323. package/plugins/synthetic/plugin.json +0 -0
  324. package/plugins/t3chat/icon-color.svg +0 -0
  325. package/plugins/t3chat/icon.svg +0 -0
  326. package/plugins/t3chat/plugin.js +0 -0
  327. package/plugins/t3chat/plugin.json +0 -0
  328. package/plugins/typesafe/LICENSE +21 -0
  329. package/plugins/typesafe/plugin.js +225 -0
  330. package/plugins/typesafe/plugin.json +24 -0
  331. package/plugins/v0/LICENSE +21 -0
  332. package/plugins/v0/icon-color.svg +1 -0
  333. package/plugins/v0/icon.svg +1 -0
  334. package/plugins/v0/plugin.js +214 -0
  335. package/plugins/v0/plugin.json +24 -0
  336. package/plugins/venice/icon-color.svg +0 -0
  337. package/plugins/venice/icon.svg +0 -0
  338. package/plugins/venice/plugin.js +50 -1
  339. package/plugins/venice/plugin.json +2 -1
  340. package/plugins/vercel/LICENSE +21 -0
  341. package/plugins/vercel/icon-color.svg +1 -0
  342. package/plugins/vercel/icon.svg +1 -0
  343. package/plugins/vercel/plugin.js +55 -0
  344. package/plugins/vercel/plugin.json +23 -0
  345. package/plugins/vertex-ai/icon-color.svg +0 -0
  346. package/plugins/vertex-ai/icon.svg +0 -0
  347. package/plugins/vertex-ai/plugin.js +0 -0
  348. package/plugins/vertex-ai/plugin.json +0 -0
  349. package/plugins/warp/icon-color.svg +0 -0
  350. package/plugins/warp/icon.svg +0 -0
  351. package/plugins/warp/plugin.js +0 -0
  352. package/plugins/warp/plugin.json +0 -0
  353. package/plugins/wayfinder/icon.svg +5 -0
  354. package/plugins/wayfinder/plugin.js +47 -0
  355. package/plugins/wayfinder/plugin.json +16 -0
  356. package/plugins/windsurf/icon-color.svg +1 -0
  357. package/plugins/windsurf/icon-conflict.svg +0 -0
  358. package/plugins/windsurf/icon.svg +0 -0
  359. package/plugins/windsurf/plugin.js +0 -0
  360. package/plugins/windsurf/plugin.json +0 -0
  361. package/plugins/xai/icon-color.svg +1 -0
  362. package/plugins/xai/icon.svg +1 -0
  363. package/plugins/xai/plugin.js +162 -0
  364. package/plugins/xai/plugin.json +28 -0
  365. package/plugins/xkiro/LICENSE +21 -0
  366. package/plugins/xkiro/plugin.js +99 -0
  367. package/plugins/xkiro/plugin.json +23 -0
  368. package/plugins/zai/icon-color.svg +1 -0
  369. package/plugins/zai/icon-conflict.svg +0 -0
  370. package/plugins/zai/icon.svg +0 -0
  371. package/plugins/zai/plugin.js +111 -161
  372. package/plugins/zai/plugin.json +4 -1
  373. package/plugins/zed/icon.svg +0 -0
  374. package/plugins/zed/plugin.js +0 -0
  375. package/plugins/zed/plugin.json +0 -0
  376. package/plugins/zenmux/icon-color.svg +1 -0
  377. package/plugins/zenmux/icon.svg +1 -0
  378. package/plugins/zenmux/plugin.js +102 -0
  379. package/plugins/zenmux/plugin.json +23 -0
  380. package/plugins/zoommate/icon.svg +1 -0
  381. package/plugins/zoommate/plugin.js +244 -0
  382. package/plugins/zoommate/plugin.json +29 -0
  383. package/usagestat +0 -0
  384. package/usagestatd +0 -0
@@ -0,0 +1,470 @@
1
+ (function () {
2
+ const APP_URL = "https://gemini.google.com/app"
3
+ const USAGE_URL = "https://gemini.google.com/usage"
4
+ const BATCH_URL = "https://gemini.google.com/_/BardChatUi/data/batchexecute"
5
+ const ORIGIN = "https://gemini.google.com"
6
+ const RPC_USAGE = "jSf9Qc"
7
+ const RPC_PLAN = "sJBwce"
8
+ const CURRENT_MS = 5 * 60 * 60 * 1000
9
+ const WEEKLY_MS = 7 * 24 * 60 * 60 * 1000
10
+ const BRAND = "#3186FF"
11
+ const COOKIE_HOSTS = [".google.com", "google.com", ".gemini.google.com", "gemini.google.com"]
12
+ const COOKIE_NAMES = [
13
+ "SID",
14
+ "HSID",
15
+ "SSID",
16
+ "APISID",
17
+ "SAPISID",
18
+ "__Secure-1PSID",
19
+ "__Secure-1PSIDTS",
20
+ "__Secure-3PSID",
21
+ "__Secure-1PAPISID",
22
+ ]
23
+
24
+ function readString(value) {
25
+ if (typeof value !== "string") return null
26
+ const trimmed = value.trim()
27
+ return trimmed ? trimmed : null
28
+ }
29
+
30
+ function getEnv(ctx, name) {
31
+ try {
32
+ return readString(ctx.host.env.get(name))
33
+ } catch (e) {
34
+ ctx.host.log.warn("env read failed for " + name + ": " + String(e))
35
+ return null
36
+ }
37
+ }
38
+
39
+ function parseCookieHeader(raw) {
40
+ const text = readString(raw)
41
+ if (!text) return null
42
+ const header = text.replace(/^Cookie:\s*/i, "")
43
+ const map = {}
44
+ const parts = header.split(";")
45
+ for (let i = 0; i < parts.length; i += 1) {
46
+ const part = parts[i].trim()
47
+ const eq = part.indexOf("=")
48
+ if (eq === -1) continue
49
+ const name = part.slice(0, eq).trim()
50
+ const value = part.slice(eq + 1).trim()
51
+ if (name && value) map[name] = value
52
+ }
53
+ return Object.keys(map).length > 0 ? map : null
54
+ }
55
+
56
+ function hasGeminiSession(map) {
57
+ if (!map || typeof map !== "object") return false
58
+ const hasPsid = readString(map["__Secure-1PSID"])
59
+ const hasSapisid = readString(map.SAPISID) || readString(map["__Secure-1PAPISID"])
60
+ return !!(hasPsid && hasSapisid)
61
+ }
62
+
63
+ function readChromeCookies(ctx) {
64
+ if (!ctx.host.chromiumCookies || typeof ctx.host.chromiumCookies.read !== "function") {
65
+ return null
66
+ }
67
+ try {
68
+ const map = ctx.host.chromiumCookies.read({ hosts: COOKIE_HOSTS, names: COOKIE_NAMES })
69
+ if (!hasGeminiSession(map)) return null
70
+ return map
71
+ } catch (e) {
72
+ ctx.host.log.warn("chromium cookie read failed: " + String(e))
73
+ return null
74
+ }
75
+ }
76
+
77
+ function loadCookies(ctx) {
78
+ const provider = ctx.provider || {}
79
+ if (Object.prototype.hasOwnProperty.call(provider, "cookieHeader")) {
80
+ const configured = parseCookieHeader(provider.cookieHeader)
81
+ return configured ? { map: configured, source: "Configured cookie" } : null
82
+ }
83
+ if (provider.instanceId && provider.instanceId !== "gemini-apps") return null
84
+ const explicit = parseCookieHeader(getEnv(ctx, "GEMINI_COOKIE"))
85
+ if (explicit) return { map: explicit, source: "GEMINI_COOKIE" }
86
+ const chrome = readChromeCookies(ctx)
87
+ if (chrome) return { map: chrome, source: "Chrome" }
88
+
89
+ const envMap = parseCookieHeader(getEnv(ctx, "GEMINI_COOKIE"))
90
+ if (envMap) return { map: envMap, source: "GEMINI_COOKIE" }
91
+
92
+ return null
93
+ }
94
+
95
+ function headerSafe(value) {
96
+ const text = readString(value)
97
+ if (!text) return null
98
+ for (let i = 0; i < text.length; i += 1) {
99
+ const code = text.charCodeAt(i)
100
+ if (code < 32 || code > 126) return null
101
+ }
102
+ return text
103
+ }
104
+
105
+ function cookieHeader(map) {
106
+ const parts = []
107
+ for (let i = 0; i < COOKIE_NAMES.length; i += 1) {
108
+ const name = COOKIE_NAMES[i]
109
+ const value = headerSafe(map[name])
110
+ if (value) parts.push(name + "=" + value)
111
+ }
112
+ const keys = Object.keys(map)
113
+ for (let i = 0; i < keys.length; i += 1) {
114
+ const name = keys[i]
115
+ if (COOKIE_NAMES.indexOf(name) !== -1) continue
116
+ const value = headerSafe(map[name])
117
+ if (value) parts.push(name + "=" + value)
118
+ }
119
+ return parts.join("; ")
120
+ }
121
+
122
+ function sapisidHash(ctx, map) {
123
+ const sapisid = readString(map.SAPISID) || readString(map["__Secure-1PAPISID"])
124
+ if (!sapisid) return null
125
+ if (!ctx.host.crypto || typeof ctx.host.crypto.sha1Hex !== "function") {
126
+ ctx.host.log.warn("sha1Hex missing; SAPISIDHASH skipped")
127
+ return null
128
+ }
129
+ const ts = Math.floor(Date.parse(ctx.nowIso || "") / 1000)
130
+ const timestamp = Number.isFinite(ts) && ts > 0 ? ts : Math.floor(Date.now() / 1000)
131
+ const digest = ctx.host.crypto.sha1Hex(timestamp + " " + sapisid + " " + ORIGIN)
132
+ return "SAPISIDHASH " + timestamp + "_" + digest
133
+ }
134
+
135
+ function geminiHeaders(map, extra) {
136
+ const headers = {
137
+ Cookie: cookieHeader(map),
138
+ Origin: ORIGIN,
139
+ Referer: USAGE_URL,
140
+ "X-Same-Domain": "1",
141
+ "User-Agent":
142
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
143
+ }
144
+ if (extra) {
145
+ const keys = Object.keys(extra)
146
+ for (let i = 0; i < keys.length; i += 1) headers[keys[i]] = extra[keys[i]]
147
+ }
148
+ return headers
149
+ }
150
+
151
+ function extractMeta(html) {
152
+ function field(name) {
153
+ const re = new RegExp('"' + name + '"\\s*:\\s*"([^"]+)"')
154
+ const m = re.exec(html)
155
+ if (m && m[1]) return m[1]
156
+ const re2 = new RegExp("'" + name + "'\\s*:\\s*'([^']+)'")
157
+ const m2 = re2.exec(html)
158
+ return m2 && m2[1] ? m2[1] : null
159
+ }
160
+ return {
161
+ at: field("SNlM0e"),
162
+ bl: field("cfb2h"),
163
+ sid: field("FdrFJe"),
164
+ }
165
+ }
166
+
167
+ function looksLikeLogin(html, status) {
168
+ if (status === 302 || status === 303 || status === 307 || status === 308) return true
169
+ const text = String(html || "")
170
+ if (/accounts\.google\.com\/(?:ServiceLogin|v3\/signin)/i.test(text) && !/"SNlM0e"/.test(text)) {
171
+ return true
172
+ }
173
+ return false
174
+ }
175
+
176
+ function parsePlan(text) {
177
+ const s = String(text || "")
178
+ if (/\bUltra\b/i.test(s)) return "Ultra"
179
+ if (/\bPRO\b/.test(s) || /\bPro\b/.test(s)) return "Pro"
180
+ if (/\bFree\b/i.test(s)) return "Free"
181
+ return null
182
+ }
183
+
184
+ function tryParseJson(ctx, text) {
185
+ return ctx.util.tryParseJson(text)
186
+ }
187
+
188
+ function parseBatchexecuteFrames(ctx, text) {
189
+ let s = String(text || "").replace(/^\)\]\}'\s*/, "")
190
+ const frames = []
191
+ let pos = 0
192
+ while (pos < s.length) {
193
+ while (pos < s.length && /\s/.test(s.charAt(pos))) pos += 1
194
+ if (pos >= s.length) break
195
+ if (s.charAt(pos) === "[") {
196
+ const parsed = tryParseJson(ctx, s.slice(pos))
197
+ if (parsed) frames.push(parsed)
198
+ break
199
+ }
200
+ const nl = s.indexOf("\n", pos)
201
+ if (nl < 0) break
202
+ const n = Number(s.slice(pos, nl).trim())
203
+ if (!Number.isFinite(n) || n <= 0) {
204
+ pos = nl + 1
205
+ continue
206
+ }
207
+ const frame = s.slice(nl + 1, nl + 1 + n)
208
+ const parsed = tryParseJson(ctx, frame)
209
+ if (parsed) frames.push(parsed)
210
+ pos = nl + 1 + n
211
+ }
212
+ return frames
213
+ }
214
+
215
+ function extractRpcPayload(ctx, text, rpcId) {
216
+ const marker = '"' + rpcId + '",'
217
+ const idx = String(text || "").indexOf(marker)
218
+ if (idx === -1) return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
219
+ let p = idx + marker.length
220
+ while (p < text.length && /\s/.test(text.charAt(p))) p += 1
221
+ if (text.charAt(p) !== '"') {
222
+ return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
223
+ }
224
+ let i = p + 1
225
+ while (i < text.length) {
226
+ const ch = text.charAt(i)
227
+ if (ch === "\\") {
228
+ i += 2
229
+ continue
230
+ }
231
+ if (ch === '"') {
232
+ const sliced = text.slice(p, i + 1)
233
+ const inner = tryParseJson(ctx, sliced)
234
+ if (typeof inner === "string") return tryParseJson(ctx, inner) || inner
235
+ return inner
236
+ }
237
+ i += 1
238
+ }
239
+ return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
240
+ }
241
+
242
+ function findWrbPayload(ctx, frames, rpcId) {
243
+ function walk(node) {
244
+ if (!node) return null
245
+ if (Array.isArray(node)) {
246
+ if (node[0] === "wrb.fr" && node[1] === rpcId) {
247
+ const payload = node[2]
248
+ if (typeof payload === "string") return tryParseJson(ctx, payload) || payload
249
+ return payload
250
+ }
251
+ for (let i = 0; i < node.length; i += 1) {
252
+ const found = walk(node[i])
253
+ if (found != null) return found
254
+ }
255
+ }
256
+ return null
257
+ }
258
+ for (let i = 0; i < frames.length; i += 1) {
259
+ const found = walk(frames[i])
260
+ if (found != null) return found
261
+ }
262
+ return null
263
+ }
264
+
265
+ function resetUnix(row) {
266
+ if (!row || !Array.isArray(row[3])) return null
267
+ const first = row[3][0]
268
+ if (Array.isArray(first)) {
269
+ const n = Number(first[0])
270
+ return Number.isFinite(n) && n > 0 ? n : null
271
+ }
272
+ const n = Number(first)
273
+ return Number.isFinite(n) && n > 0 ? n : null
274
+ }
275
+
276
+ function parseUsagePayload(payload) {
277
+ let rows = null
278
+ if (Array.isArray(payload) && Array.isArray(payload[1])) rows = payload[1]
279
+ else if (Array.isArray(payload) && payload.length && Array.isArray(payload[0])) rows = payload
280
+ if (!rows) return null
281
+
282
+ let current = null
283
+ let weekly = null
284
+ for (let i = 0; i < rows.length; i += 1) {
285
+ const row = rows[i]
286
+ if (!Array.isArray(row) || row.length < 3) continue
287
+ if (typeof row[1] !== "number" || row[1] < 0 || row[1] > 1) continue
288
+ const frac = row[1]
289
+ const type = Number(row[2])
290
+ if (!Number.isFinite(frac) || !Number.isFinite(type)) continue
291
+ const used = Math.max(0, Math.min(100, Math.round(frac * 100)))
292
+ const bucket = { used: used, resetsAtUnix: resetUnix(row) }
293
+ if (type === 1) current = bucket
294
+ else if (type === 2) weekly = bucket
295
+ }
296
+ if (!current || !weekly) return null
297
+ return { current: current, weekly: weekly }
298
+ }
299
+
300
+ function encodeForm(fields) {
301
+ const parts = []
302
+ const keys = Object.keys(fields)
303
+ for (let i = 0; i < keys.length; i += 1) {
304
+ const key = keys[i]
305
+ if (fields[key] == null) continue
306
+ parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(fields[key])))
307
+ }
308
+ return parts.join("&")
309
+ }
310
+
311
+ function batchExecute(ctx, cookies, meta, rpcId, innerPayload) {
312
+ const params = [
313
+ "rpcids=" + encodeURIComponent(rpcId),
314
+ "rt=c",
315
+ "source-path=" + encodeURIComponent("/usage"),
316
+ ]
317
+ if (meta.bl) params.push("bl=" + encodeURIComponent(meta.bl))
318
+ if (meta.sid) params.push("f.sid=" + encodeURIComponent(meta.sid))
319
+ params.push("_reqid=" + String(100000 + Math.floor(Math.random() * 900000)))
320
+
321
+ const headers = geminiHeaders(cookies.map, {
322
+ "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
323
+ })
324
+ const auth = sapisidHash(ctx, cookies.map)
325
+ if (auth) headers.Authorization = auth
326
+
327
+ const form = encodeForm({
328
+ "f.req": JSON.stringify([[[rpcId, innerPayload, null, "generic"]]]),
329
+ at: meta.at,
330
+ })
331
+
332
+ let resp
333
+ try {
334
+ resp = ctx.util.request({
335
+ method: "POST",
336
+ url: BATCH_URL + "?" + params.join("&"),
337
+ headers: headers,
338
+ bodyText: form,
339
+ timeoutMs: 15000,
340
+ http1Only: true,
341
+ })
342
+ } catch (e) {
343
+ ctx.host.log.warn("batchexecute failed (" + rpcId + "): " + String(e))
344
+ return null
345
+ }
346
+ if (looksLikeLogin(resp.bodyText, resp.status)) {
347
+ throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
348
+ }
349
+ if (resp.status < 200 || resp.status >= 300) {
350
+ ctx.host.log.warn("batchexecute HTTP " + resp.status + " for " + rpcId)
351
+ return null
352
+ }
353
+ return extractRpcPayload(ctx, resp.bodyText, rpcId)
354
+ }
355
+
356
+ function fetchHtml(ctx, cookies, url) {
357
+ const headers = geminiHeaders(cookies.map, { Accept: "text/html" })
358
+ const auth = sapisidHash(ctx, cookies.map)
359
+ if (auth) headers.Authorization = auth
360
+ let resp
361
+ try {
362
+ resp = ctx.util.request({
363
+ method: "GET",
364
+ url: url,
365
+ headers: headers,
366
+ timeoutMs: 15000,
367
+ http1Only: true,
368
+ })
369
+ } catch (e) {
370
+ ctx.host.log.warn("html request failed (" + url + "): " + String(e))
371
+ throw "Could not reach gemini.google.com. Check your connection."
372
+ }
373
+ if (looksLikeLogin(resp.bodyText, resp.status)) {
374
+ throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
375
+ }
376
+ if (ctx.util.isAuthStatus(resp.status)) {
377
+ throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
378
+ }
379
+ if (resp.status < 200 || resp.status >= 300) {
380
+ throw "Gemini Apps page failed (HTTP " + resp.status + ")."
381
+ }
382
+ return resp.bodyText
383
+ }
384
+
385
+ function fetchUsagePage(ctx, cookies) {
386
+ let html = fetchHtml(ctx, cookies, APP_URL)
387
+ let meta = extractMeta(html)
388
+ if (!meta.at) {
389
+ ctx.host.log.info("SNlM0e missing on /app; trying /usage")
390
+ html = fetchHtml(ctx, cookies, USAGE_URL)
391
+ meta = extractMeta(html)
392
+ }
393
+ ctx.host.log.info(
394
+ "htmlChars=" +
395
+ String(html.length) +
396
+ " hasAt=" +
397
+ (meta.at ? "yes" : "no") +
398
+ " hasBl=" +
399
+ (meta.bl ? "yes" : "no"),
400
+ )
401
+ if (!meta.at) {
402
+ throw "Could not read Gemini Apps session token. Log into gemini.google.com in Chrome."
403
+ }
404
+ return { html: html, meta: meta }
405
+ }
406
+
407
+ function probe(ctx) {
408
+ const cookies = loadCookies(ctx)
409
+ if (!cookies) {
410
+ throw "Gemini Apps auth missing. Log into gemini.google.com in Chrome."
411
+ }
412
+ ctx.host.log.info("cookie names=" + Object.keys(cookies.map).sort().join(","))
413
+ if (!hasGeminiSession(cookies.map)) {
414
+ throw "Gemini Apps auth missing. Log into gemini.google.com in Chrome."
415
+ }
416
+
417
+ const page = fetchUsagePage(ctx, cookies)
418
+ const payload = batchExecute(ctx, cookies, page.meta, RPC_USAGE, "[]")
419
+ const usage = parseUsagePayload(payload)
420
+ if (!usage) throw "Could not parse Gemini Apps usage."
421
+
422
+ let plan = null
423
+ try {
424
+ const planPayload = batchExecute(ctx, cookies, page.meta, RPC_PLAN, "[]")
425
+ if (planPayload != null) {
426
+ plan = parsePlan(JSON.stringify(planPayload))
427
+ }
428
+ } catch (e) {
429
+ ctx.host.log.warn("plan RPC skipped: " + String(e))
430
+ }
431
+
432
+ const currentResetsAt = usage.current.resetsAtUnix
433
+ ? ctx.util.toIso(usage.current.resetsAtUnix)
434
+ : undefined
435
+ const weeklyResetsAt = usage.weekly.resetsAtUnix
436
+ ? ctx.util.toIso(usage.weekly.resetsAtUnix)
437
+ : undefined
438
+
439
+ const lines = [
440
+ ctx.line.progress({
441
+ label: "Current",
442
+ used: usage.current.used,
443
+ limit: 100,
444
+ format: { kind: "percent" },
445
+ resetsAt: currentResetsAt,
446
+ periodDurationMs: CURRENT_MS,
447
+ color: BRAND,
448
+ }),
449
+ ctx.line.progress({
450
+ label: "Weekly",
451
+ used: usage.weekly.used,
452
+ limit: 100,
453
+ format: { kind: "percent" },
454
+ resetsAt: weeklyResetsAt,
455
+ periodDurationMs: WEEKLY_MS,
456
+ color: BRAND,
457
+ }),
458
+ ctx.line.text({ label: "Source", value: cookies.source }),
459
+ ]
460
+
461
+ const result = { lines: lines, source: "web" }
462
+ if (plan) result.plan = ctx.fmt.planLabel(plan)
463
+ return result
464
+ }
465
+
466
+ globalThis.__openusage_plugin = {
467
+ id: "gemini-apps",
468
+ probe: probe,
469
+ }
470
+ })()
@@ -0,0 +1,19 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "gemini-apps",
4
+ "name": "Gemini Apps",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": ["web"],
9
+ "autoMode": "web",
10
+ "webUrl": "https://gemini.google.com",
11
+ "usageDashboardUrl": "https://gemini.google.com/usage",
12
+ "envVars": ["GEMINI_COOKIE"],
13
+ "upstream": {
14
+ "repository": "https://github.com/barramee27/crossusage",
15
+ "revision": "2b90b00be84e06acbf71a7c5c8db22d1c4e83966",
16
+ "path": "plugins/gemini-apps/plugin.js",
17
+ "license": "MIT"
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter Steinberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,153 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Personal","Shared pool","Additional"],"defaults":{},"autoMode":"api"};
3
+ function _nullishCoalesce(lhs, rhsFn) {
4
+ if (lhs != null) {
5
+ return lhs;
6
+ } else {
7
+ return rhsFn();
8
+ }
9
+ }
10
+ function _optionalChain(ops) {
11
+ let lastAccessLHS = undefined;
12
+ let value = ops[0];
13
+ let i = 1;
14
+ while (i < ops.length) {
15
+ const op = ops[i];
16
+ const fn = ops[i + 1];
17
+ i += 2;
18
+ if ((op === "optionalAccess" || op === "optionalCall") && value == null) {
19
+ return undefined;
20
+ }
21
+ if (op === "access" || op === "optionalAccess") {
22
+ lastAccessLHS = value;
23
+ value = fn(value);
24
+ } else if (op === "call" || op === "optionalCall") {
25
+ value = fn((...args) => value.call(lastAccessLHS, ...args));
26
+ lastAccessLHS = undefined;
27
+ }
28
+ }
29
+ return value;
30
+ }
31
+
32
+ defineProvider({
33
+ id: "gitkraken",
34
+ name: "GitKraken AI",
35
+ endpoints: ["https://api.gitkraken.dev"],
36
+ auth: { type: "bearer", secret: "GITKRAKEN_API_TOKEN" },
37
+ settings: [
38
+ { key: "GITKRAKEN_API_TOKEN", title: "GitKraken access token", type: "secure" },
39
+ { key: "GITKRAKEN_ORG_ID", title: "API organization ID", type: "plain" },
40
+ { key: "CLIENT_VERSION", title: "Client version", type: "plain" },
41
+ ],
42
+ capabilities: ["http-status"],
43
+ async fetchUsage(ctx) {
44
+ const token = ctx.settings.getSecret("GITKRAKEN_API_TOKEN");
45
+ if (!token) throw ctx.fail.missingCredential("Set a GitKraken access token in Settings or GITKRAKEN_API_TOKEN.");
46
+ if (!/^[\x21-\x7e]{1,16384}$/.test(token)) {
47
+ throw ctx.fail.authenticationExpired("Invalid GitKraken token. Paste only the value after Bearer.");
48
+ }
49
+ const organization = _optionalChain([
50
+ ctx,
51
+ "access",
52
+ (_) => _.settings,
53
+ "access",
54
+ (_2) => _2.get,
55
+ "call",
56
+ (_3) => _3("GITKRAKEN_ORG_ID"),
57
+ "optionalAccess",
58
+ (_4) => _4.trim,
59
+ "call",
60
+ (_5) => _5(),
61
+ ]);
62
+ if (organization && !/^[\x21-\x7e]{1,256}$/.test(organization)) {
63
+ throw ctx.fail.permissionDenied("Invalid GitKraken organization ID. Enter a single ID without whitespace.");
64
+ }
65
+ const headers = {
66
+ "Client-Name": "CodexBar",
67
+ "Client-Version": ctx.settings.get("CLIENT_VERSION") || "0.0.0",
68
+ "User-Agent": "CodexBar",
69
+ };
70
+ if (organization) headers["gk-org-id"] = organization;
71
+ const response = await ctx.http.get("https://api.gitkraken.dev/v1/ai-tasks/usage", { headers });
72
+ if (response.status === 401)
73
+ throw ctx.fail.authenticationExpired("GitKraken access token expired or was rejected.");
74
+ if (response.status === 403)
75
+ throw ctx.fail.permissionDenied("GitKraken denied access to this account or organization.");
76
+ if (response.status === 429) {
77
+ const delay = Number(_nullishCoalesce(response.headers["retry-after"], () => 1));
78
+ throw ctx.fail.rateLimited("GitKraken rate limited usage requests.", {
79
+ retryAfterSeconds: Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 10) : 1,
80
+ });
81
+ }
82
+ if (response.status >= 500) throw ctx.fail.providerUnavailable("GitKraken usage is temporarily unavailable.");
83
+ if (response.status !== 200) throw ctx.fail.apiFailure(`GitKraken returned HTTP ${response.status}.`);
84
+
85
+ function fail() {
86
+ throw ctx.fail.parseFailure("GitKraken returned an unrecognized usage response.");
87
+ }
88
+ function quota(value) {
89
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
90
+ const { used, limit } = value;
91
+ if (
92
+ typeof used !== "number" ||
93
+ !Number.isFinite(used) ||
94
+ used < 0 ||
95
+ typeof limit !== "number" ||
96
+ !Number.isFinite(limit) ||
97
+ (limit < 0 && limit !== -1) ||
98
+ (limit > 0 && !Number.isFinite((used / limit) * 100))
99
+ )
100
+ return null;
101
+ return { used, limit };
102
+ }
103
+ let body;
104
+ try {
105
+ body = JSON.parse(response.bodyText);
106
+ } catch (error) {
107
+ void error;
108
+ return fail();
109
+ }
110
+ const data = _optionalChain([body, "optionalAccess", (_6) => _6.data]);
111
+ const personal = quota(data);
112
+ if (_optionalChain([body, "optionalAccess", (_7) => _7.error]) != null || !personal) return fail();
113
+ if (
114
+ typeof data.resetsOn !== "string" ||
115
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(data.resetsOn)
116
+ )
117
+ return fail();
118
+ const resetsAt = new Date(data.resetsOn);
119
+ if (!Number.isFinite(resetsAt.getTime())) return fail();
120
+ const pool = quota(data.organization);
121
+ const number = (value) => ctx.format.number(value, { maximumFractionDigits: 2 });
122
+ function description(value) {
123
+ if (value.limit <= 0)
124
+ return `${number(value.used)} credits used · ${value.limit === -1 ? "Unlimited" : "No allowance"}`;
125
+ return `${number(value.used)} / ${number(value.limit)} credits used`;
126
+ }
127
+ const rows = [{ label: "Personal", value: description(personal) }];
128
+ if (pool) {
129
+ rows.push({ label: "Shared pool", value: description(pool) });
130
+ // sharedUsed is a slice of the pool, never additional personal usage.
131
+ const shared = data.sharedUsed;
132
+ if (typeof shared === "number" && Number.isFinite(shared) && shared >= 0 && shared <= pool.used) {
133
+ rows.push({ label: "Your shared usage", value: `${number(shared)} credits` });
134
+ rows.push({ label: "Rest of organization", value: `${number(pool.used - shared)} credits` });
135
+ }
136
+ }
137
+ if (personal.limit <= 0 && (!pool || pool.limit <= 0)) {
138
+ rows.push({ label: "Reset", value: resetsAt.toISOString() });
139
+ }
140
+ function window(value) {
141
+ return value && value.limit > 0
142
+ ? { usedPercent: ctx.pct(value.used, value.limit), windowMinutes: 10080, resetsAt }
143
+ : null;
144
+ }
145
+ return {
146
+ primary: window(personal),
147
+ secondary: window(pool),
148
+ details: [{ title: "Weekly usage", rows }],
149
+ identity: { loginMethod: "API" },
150
+ dataConfidence: "exact",
151
+ };
152
+ },
153
+ });
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "gitkraken",
4
+ "name": "GitKraken AI",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "api"
10
+ ],
11
+ "autoMode": "api",
12
+ "usageDashboardUrl": "https://gitkraken.dev/account#ai-usage",
13
+ "envVars": [
14
+ "GITKRAKEN_API_TOKEN",
15
+ "GITKRAKEN_ORG_ID"
16
+ ],
17
+ "upstream": {
18
+ "repository": "https://github.com/steipete/CodexBar",
19
+ "revision": "78ba5a1e65e6859da59a21b799df95ab91bc0b89",
20
+ "path": "Sources/CodexBarCore/Resources/Plugins/gitkraken.js",
21
+ "sha256": "c583890b74fc4c054cd818e560b30a17dd716c7611b554bd5b559f533a824ca2",
22
+ "license": "MIT"
23
+ }
24
+ }
@@ -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>Grok</title><path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z"></path></svg>
File without changes