@hashimkarim/usagestat-linux-x64-gnu 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/native-manifest.json +685 -133
  2. package/package.json +94 -2
  3. package/plugins/abliteration/LICENSE +21 -0
  4. package/plugins/abliteration/icon.svg +5 -0
  5. package/plugins/abliteration/plugin.js +89 -0
  6. package/plugins/abliteration/plugin.json +24 -0
  7. package/plugins/aerostack/LICENSE +21 -0
  8. package/plugins/aerostack/icon.svg +1 -0
  9. package/plugins/aerostack/plugin.js +94 -0
  10. package/plugins/aerostack/plugin.json +24 -0
  11. package/plugins/aixy/icon.svg +1 -0
  12. package/plugins/aixy/plugin.js +5 -0
  13. package/plugins/aixy/plugin.json +9 -1
  14. package/plugins/antigravity/plugin.js +19 -10
  15. package/plugins/antigravity-cli/plugin.js +8 -3
  16. package/plugins/antigravity-ide/plugin.js +8 -5
  17. package/plugins/atlascloud/plugin.js +5 -0
  18. package/plugins/atlascloud/plugin.json +2 -1
  19. package/plugins/bifrost/icon.svg +3 -0
  20. package/plugins/bifrost/plugin.js +5 -0
  21. package/plugins/bifrost/plugin.json +9 -1
  22. package/plugins/chatbox/LICENSE +21 -0
  23. package/plugins/chatbox/icon.svg +4 -0
  24. package/plugins/chatbox/plugin.js +208 -0
  25. package/plugins/chatbox/plugin.json +24 -0
  26. package/plugins/claude/plugin.js +39 -2
  27. package/plugins/claudeplatform/LICENSE +21 -0
  28. package/plugins/claudeplatform/icon.svg +3 -0
  29. package/plugins/claudeplatform/plugin.js +244 -0
  30. package/plugins/claudeplatform/plugin.json +25 -0
  31. package/plugins/codex/plugin.js +3 -2
  32. package/plugins/codex/plugin.json +1 -1
  33. package/plugins/cosmic/LICENSE +21 -0
  34. package/plugins/cosmic/icon.svg +1 -0
  35. package/plugins/cosmic/plugin.js +134 -0
  36. package/plugins/cosmic/plugin.json +32 -0
  37. package/plugins/cursor/plugin.js +9 -2
  38. package/plugins/cursor-nightly/plugin.json +1 -1
  39. package/plugins/devpass/icon.svg +3 -0
  40. package/plugins/devpass/plugin.js +5 -0
  41. package/plugins/devpass/plugin.json +2 -1
  42. package/plugins/exa/LICENSE +21 -0
  43. package/plugins/exa/icon-color.svg +1 -0
  44. package/plugins/exa/icon.svg +1 -0
  45. package/plugins/exa/plugin.js +75 -0
  46. package/plugins/exa/plugin.json +32 -0
  47. package/plugins/fal/LICENSE +21 -0
  48. package/plugins/fal/icon-color.svg +1 -0
  49. package/plugins/fal/icon.svg +1 -0
  50. package/plugins/fal/plugin.js +74 -0
  51. package/plugins/fal/plugin.json +24 -0
  52. package/plugins/flora/LICENSE +21 -0
  53. package/plugins/flora/icon.svg +4 -0
  54. package/plugins/flora/plugin.js +93 -0
  55. package/plugins/flora/plugin.json +32 -0
  56. package/plugins/futureagi/LICENSE +21 -0
  57. package/plugins/futureagi/icon.svg +24 -0
  58. package/plugins/futureagi/plugin.js +139 -0
  59. package/plugins/futureagi/plugin.json +32 -0
  60. package/plugins/gemini-apps/LICENSE +1 -2
  61. package/plugins/gemini-apps/plugin.js +136 -465
  62. package/plugins/gemini-apps/plugin.json +13 -7
  63. package/plugins/gitkraken/icon.svg +2 -0
  64. package/plugins/gitkraken/plugin.js +5 -0
  65. package/plugins/gitkraken/plugin.json +9 -1
  66. package/plugins/helmcode/icon.svg +5 -0
  67. package/plugins/helmcode/plugin.js +48 -40
  68. package/plugins/helmcode/plugin.json +10 -2
  69. package/plugins/huggingface/plugin.js +6 -1
  70. package/plugins/huggingface/plugin.json +4 -3
  71. package/plugins/hyper/plugin.js +53 -47
  72. package/plugins/hyper/plugin.json +3 -2
  73. package/plugins/kimi/plugin.js +39 -14
  74. package/plugins/langdock/LICENSE +21 -0
  75. package/plugins/langdock/icon.svg +4 -0
  76. package/plugins/langdock/plugin.js +106 -0
  77. package/plugins/langdock/plugin.json +25 -0
  78. package/plugins/linkup/LICENSE +21 -0
  79. package/plugins/linkup/icon.svg +10 -0
  80. package/plugins/linkup/plugin.js +46 -0
  81. package/plugins/linkup/plugin.json +24 -0
  82. package/plugins/lithosai/plugin.js +5 -0
  83. package/plugins/lithosai/plugin.json +2 -1
  84. package/plugins/llmman/icon.svg +4 -0
  85. package/plugins/llmman/plugin.js +5 -0
  86. package/plugins/llmman/plugin.json +9 -1
  87. package/plugins/munito/LICENSE +21 -0
  88. package/plugins/munito/icon.svg +7 -0
  89. package/plugins/munito/plugin.js +171 -0
  90. package/plugins/munito/plugin.json +24 -0
  91. package/plugins/muse/icon.svg +3 -0
  92. package/plugins/muse/plugin.js +5 -0
  93. package/plugins/muse/plugin.json +9 -1
  94. package/plugins/museai/plugin.js +5 -0
  95. package/plugins/museai/plugin.json +2 -1
  96. package/plugins/nous/plugin.js +5 -0
  97. package/plugins/nous/plugin.json +9 -1
  98. package/plugins/ollama/plugin.js +58 -0
  99. package/plugins/opencode-go/plugin.js +17 -6
  100. package/plugins/parallel/LICENSE +21 -0
  101. package/plugins/parallel/icon.svg +11 -0
  102. package/plugins/parallel/plugin.js +97 -0
  103. package/plugins/parallel/plugin.json +25 -0
  104. package/plugins/qwencloud/LICENSE +21 -0
  105. package/plugins/qwencloud/plugin.js +197 -0
  106. package/plugins/qwencloud/plugin.json +17 -0
  107. package/plugins/raycast/icon.svg +4 -0
  108. package/plugins/raycast/plugin.js +5 -0
  109. package/plugins/raycast/plugin.json +9 -1
  110. package/plugins/replicate/plugin.js +5 -0
  111. package/plugins/replicate/plugin.json +2 -1
  112. package/plugins/sailresearch/LICENSE +21 -0
  113. package/plugins/sailresearch/icon.svg +4 -0
  114. package/plugins/sailresearch/plugin.js +104 -0
  115. package/plugins/sailresearch/plugin.json +24 -0
  116. package/plugins/sofya/LICENSE +21 -0
  117. package/plugins/sofya/icon.svg +3 -0
  118. package/plugins/sofya/plugin.js +86 -0
  119. package/plugins/sofya/plugin.json +24 -0
  120. package/plugins/soniox/LICENSE +21 -0
  121. package/plugins/soniox/icon.svg +3 -0
  122. package/plugins/soniox/plugin.js +103 -0
  123. package/plugins/soniox/plugin.json +24 -0
  124. package/plugins/tavily/LICENSE +21 -0
  125. package/plugins/tavily/icon.svg +3 -0
  126. package/plugins/tavily/plugin.js +61 -0
  127. package/plugins/tavily/plugin.json +24 -0
  128. package/plugins/tinyapi/LICENSE +21 -0
  129. package/plugins/tinyapi/icon.svg +4 -0
  130. package/plugins/tinyapi/plugin.js +73 -0
  131. package/plugins/tinyapi/plugin.json +25 -0
  132. package/plugins/traecn/LICENSE +21 -0
  133. package/plugins/traecn/icon.svg +3 -0
  134. package/plugins/traecn/plugin.js +104 -0
  135. package/plugins/traecn/plugin.json +25 -0
  136. package/plugins/typesafe/icon.svg +4 -0
  137. package/plugins/typesafe/plugin.js +30 -25
  138. package/plugins/typesafe/plugin.json +3 -2
  139. package/plugins/v0/plugin.js +5 -0
  140. package/plugins/v0/plugin.json +9 -1
  141. package/plugins/vercel/plugin.js +147 -0
  142. package/plugins/vercel/plugin.json +34 -3
  143. package/plugins/workbuddy/icon.svg +1 -1
  144. package/plugins/workbuddy/plugin.js +9 -4
  145. package/plugins/workbuddy/plugin.json +20 -2
  146. package/plugins/xai/plugin.js +9 -0
  147. package/plugins/xapi/LICENSE +21 -0
  148. package/plugins/xapi/icon.svg +1 -0
  149. package/plugins/xapi/plugin.js +90 -0
  150. package/plugins/xapi/plugin.json +25 -0
  151. package/plugins/xkiro/icon.svg +1 -0
  152. package/plugins/xkiro/plugin.js +5 -0
  153. package/plugins/xkiro/plugin.json +2 -1
  154. package/usagestat +0 -0
  155. package/usagestatd +0 -0
  156. package/plugins/cursor-nightly/plugin.js +0 -1416
@@ -478,8 +478,10 @@
478
478
 
479
479
  function poolLabel(normalizedLabel) {
480
480
  var lower = normalizedLabel.toLowerCase()
481
+ if (lower.indexOf('argon') !== -1) return null
481
482
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "Gemini Pro"
482
483
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("flash") !== -1) return "Gemini Flash"
484
+ if (lower.indexOf('gemini') !== -1) return null
483
485
  // All non-Gemini models (Claude, GPT-OSS, etc.) share a single quota pool
484
486
  return "Claude"
485
487
  }
@@ -520,6 +522,7 @@
520
522
  var frac = qi.remainingFraction
521
523
  var rtime = (qi && qi.resetTime) || undefined
522
524
  var pool = poolLabel(normalizeLabel(label))
525
+ if (!pool) continue
523
526
  if (!deduped[pool] || frac < deduped[pool].remainingFraction) {
524
527
  deduped[pool] = {
525
528
  label: pool,
@@ -551,6 +554,8 @@
551
554
  // --- Cloud Code API ---
552
555
 
553
556
  function requestCloudCodeJson(ctx, path, token, userAgent, body, timeoutMs) {
557
+ var summaries = path === '/v1internal:retrieveUserQuotaSummary' ? [] : null
558
+ var denial = null
554
559
  for (var i = 0; i < CLOUD_CODE_URLS.length; i++) {
555
560
  try {
556
561
  var resp = ctx.host.http.request({
@@ -569,28 +574,32 @@
569
574
  ctx.host.log.warn("Cloud Code returned invalid response shape (" + CLOUD_CODE_URLS[i] + ")")
570
575
  continue
571
576
  }
572
- if (resp.status === 401) return { _authFailed: true }
573
- if (resp.status === 403) return { _quotaDenied: true }
577
+ if (resp.status === 401 || resp.status === 403) {
578
+ denial = resp.status === 401 ? { _authFailed:true } : { _quotaDenied:true }
579
+ if (!summaries) return denial
580
+ continue
581
+ }
574
582
  if (resp.status >= 200 && resp.status < 300) {
575
583
  var json = ctx.util.tryParseJson(resp.bodyText)
576
584
  if (!json || typeof json !== "object") {
577
585
  ctx.host.log.warn("Cloud Code returned invalid JSON (" + CLOUD_CODE_URLS[i] + ")")
578
586
  continue
579
587
  }
588
+ if (summaries) {summaries.push(json);continue}
580
589
  return json
581
590
  }
582
591
  } catch (e) {
583
592
  ctx.host.log.warn("Cloud Code request failed (" + CLOUD_CODE_URLS[i] + "): " + String(e))
584
593
  }
585
594
  }
586
- return null
595
+ return summaries && summaries.length ? summaries : denial
587
596
  }
588
597
 
589
598
  function probeCloudCode(ctx, token, userAgent, skipSummary) {
590
599
  if (!skipSummary) {
591
600
  var summary = requestCloudCodeJson(ctx, "/v1internal:retrieveUserQuotaSummary", token, userAgent, {}, 2000)
592
601
  var lines = ctx.util.groupedQuotaLines(summary)
593
- if (lines.length) return { _summaryLines: lines }
602
+ if (lines !== null) return { _summaryLines: lines }
594
603
  }
595
604
  return requestCloudCodeJson(ctx, FETCH_MODELS_PATH, token, userAgent, {})
596
605
  }
@@ -665,7 +674,7 @@
665
674
  var quotaData = null
666
675
  var summary = requestCloudCodeJson(ctx, "/v1internal:retrieveUserQuotaSummary", token, "agy", project ? { project: project } : {}, 2000)
667
676
  var summaryLines = ctx.util.groupedQuotaLines(summary)
668
- if (summaryLines.length) return { plan: readAgyPlan(loadData), lines: summaryLines }
677
+ if (summaryLines !== null) return { plan: readAgyPlan(loadData), lines: summaryLines }
669
678
  if (project) {
670
679
  quotaData = requestCloudCodeJson(ctx, RETRIEVE_QUOTA_PATH, token, "agy", { project: project })
671
680
  }
@@ -696,7 +705,7 @@
696
705
  locale: "en",
697
706
  }
698
707
 
699
- var summaryLines = []
708
+ var summaryLines = null
700
709
  try {
701
710
  summaryLines = ctx.util.groupedQuotaLines(callLs(ctx, found.port, found.scheme, discovery.csrf,
702
711
  "RetrieveUserQuotaSummary", { forceRefresh: true }, 2000))
@@ -711,7 +720,7 @@
711
720
  }
712
721
  var hasUserStatus = data && data.userStatus
713
722
 
714
- if (!hasUserStatus && !summaryLines.length) {
723
+ if (!hasUserStatus && summaryLines === null) {
715
724
  ctx.host.log.warn("GetUserStatus failed, trying GetCommandModelConfigs")
716
725
  data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetCommandModelConfigs", { metadata: metadata })
717
726
  }
@@ -722,7 +731,7 @@
722
731
  configs = (data.userStatus.cascadeModelConfigData || {}).clientModelConfigs || []
723
732
  } else if (data && data.clientModelConfigs) {
724
733
  configs = data.clientModelConfigs
725
- } else if (summaryLines.length) {
734
+ } else if (summaryLines !== null) {
726
735
  configs = []
727
736
  } else {
728
737
  return null
@@ -735,8 +744,8 @@
735
744
  filtered.push(configs[j])
736
745
  }
737
746
 
738
- var lines = summaryLines.length ? summaryLines : buildModelLines(ctx, filtered)
739
- if (lines.length === 0) return null
747
+ var lines = summaryLines !== null ? summaryLines : buildModelLines(ctx, filtered)
748
+ if (lines.length === 0 && summaryLines === null) return null
740
749
 
741
750
  var plan = null
742
751
  if (hasUserStatus) {
@@ -346,11 +346,15 @@
346
346
 
347
347
  function requestJsonAcrossBases(ctx, path, token, body, options) {
348
348
  var urls = cloudCodeUrls(path)
349
+ var summaries = path === '/v1internal:retrieveUserQuotaSummary' ? [] : null
349
350
  for (var i = 0; i < urls.length; i += 1) {
350
351
  var data = requestJson(ctx, urls[i], token, body, options)
351
- if (data) return data
352
+ if (data) {
353
+ if (summaries) summaries.push(data)
354
+ else return data
355
+ }
352
356
  }
353
- return null
357
+ return summaries && summaries.length ? summaries : null
354
358
  }
355
359
 
356
360
  function makeAuthRetry(ctx, authState) {
@@ -425,6 +429,7 @@
425
429
 
426
430
  function poolForText(text) {
427
431
  var lower = String(text || "").toLowerCase()
432
+ if (lower.indexOf('argon') !== -1) return null
428
433
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "Gemini Pro"
429
434
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("flash") !== -1) return "Gemini Flash"
430
435
  if (lower.indexOf("gemini") !== -1) return null
@@ -549,7 +554,7 @@
549
554
  var summary = requestJsonAcrossBases(ctx, "/v1internal:retrieveUserQuotaSummary", authState.token,
550
555
  project ? { project: project } : {}, { optional: true, timeoutMs: 2000 })
551
556
  var summaryLines = ctx.util.groupedQuotaLines(summary)
552
- if (summaryLines.length) return { plan: plan || undefined, lines: summaryLines }
557
+ if (summaryLines !== null) return { plan: plan || undefined, lines: summaryLines }
553
558
 
554
559
  var buckets = []
555
560
  // fetchAvailableModels rejects `metadata` in the body (HTTP 400); use {} like Antigravity IDE.
@@ -111,8 +111,10 @@
111
111
 
112
112
  function poolLabel(normalizedLabel) {
113
113
  var lower = normalizedLabel.toLowerCase()
114
+ if (lower.indexOf('argon') !== -1) return null
114
115
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("pro") !== -1) return "Gemini Pro"
115
116
  if (lower.indexOf("gemini") !== -1 && lower.indexOf("flash") !== -1) return "Gemini Flash"
117
+ if (lower.indexOf('gemini') !== -1) return null
116
118
  // All non-Gemini models (Claude, GPT-OSS, etc.) share a single quota pool
117
119
  return "Claude"
118
120
  }
@@ -153,6 +155,7 @@
153
155
  var frac = qi.remainingFraction
154
156
  var rtime = (qi && qi.resetTime) || undefined
155
157
  var pool = poolLabel(normalizeLabel(label))
158
+ if (!pool) continue
156
159
  if (!deduped[pool] || frac < deduped[pool].remainingFraction) {
157
160
  deduped[pool] = {
158
161
  label: pool,
@@ -199,7 +202,7 @@
199
202
  locale: "en",
200
203
  }
201
204
 
202
- var summaryLines = []
205
+ var summaryLines = null
203
206
  try {
204
207
  summaryLines = ctx.util.groupedQuotaLines(callLs(ctx, found.port, found.scheme, discovery.csrf,
205
208
  "RetrieveUserQuotaSummary", { forceRefresh: true }, 2000))
@@ -214,7 +217,7 @@
214
217
  }
215
218
  var hasUserStatus = data && data.userStatus
216
219
 
217
- if (!hasUserStatus && !summaryLines.length) {
220
+ if (!hasUserStatus && summaryLines === null) {
218
221
  ctx.host.log.warn("GetUserStatus failed, trying GetCommandModelConfigs")
219
222
  data = callLs(ctx, found.port, found.scheme, discovery.csrf, "GetCommandModelConfigs", { metadata: metadata })
220
223
  }
@@ -225,7 +228,7 @@
225
228
  configs = (data.userStatus.cascadeModelConfigData || {}).clientModelConfigs || []
226
229
  } else if (data && data.clientModelConfigs) {
227
230
  configs = data.clientModelConfigs
228
- } else if (summaryLines.length) {
231
+ } else if (summaryLines !== null) {
229
232
  configs = []
230
233
  } else {
231
234
  throw "Start Antigravity IDE and try again."
@@ -238,8 +241,8 @@
238
241
  filtered.push(configs[j])
239
242
  }
240
243
 
241
- var lines = summaryLines.length ? summaryLines : buildModelLines(ctx, filtered)
242
- if (lines.length === 0) throw "Start Antigravity IDE and try again."
244
+ var lines = summaryLines !== null ? summaryLines : buildModelLines(ctx, filtered)
245
+ if (lines.length === 0 && summaryLines === null) throw "Start Antigravity IDE and try again."
243
246
 
244
247
  var plan = null
245
248
  if (hasUserStatus) {
@@ -1,5 +1,7 @@
1
1
  // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
2
  globalThis.__usagestat_bundled_options = {"labels":["Balance","Balance","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/atlascloud.js
3
5
  defineProvider({
4
6
  id: "atlascloud",
5
7
  name: "Atlas Cloud",
@@ -47,3 +49,6 @@ defineProvider({
47
49
  };
48
50
  },
49
51
  });
52
+
53
+ // END UPSTREAM
54
+ })(globalThis.defineProvider);
@@ -13,9 +13,10 @@
13
13
  "envVars": [
14
14
  "ATLASCLOUD_API_KEY"
15
15
  ],
16
+ "setupFields": [],
16
17
  "upstream": {
17
18
  "repository": "https://github.com/steipete/CodexBar",
18
- "revision": "6ba06fe9ec1522dd5c59e16810115bce28aa148f",
19
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
19
20
  "path": "Sources/CodexBarCore/Resources/Plugins/atlascloud.js",
20
21
  "sha256": "852fb78abaf4fcdbbfbfeac0b60d6b82a9ca9b1d1d2f9014f6e5299b41ac47fa",
21
22
  "license": "MIT"
@@ -0,0 +1,3 @@
1
+ <svg width="88" height="110" viewBox="0 0 88 110" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M65.8047 109.59H0V87.6719H65.8047V109.59ZM87.7393 87.6719H65.8047V65.7539H87.7393V87.6719ZM43.8701 65.7539H21.9355V43.8359H43.8701V65.7539ZM87.7393 43.8359H65.8047V21.918H87.7393V43.8359ZM65.8047 21.918H0V0H65.8047V21.918Z" fill="#33C19E"/>
3
+ </svg>
@@ -1,5 +1,7 @@
1
1
  // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
2
  globalThis.__usagestat_bundled_options = {"labels":["Budget","Secondary budget","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/bifrost.js
3
5
  function _nullishCoalesce(lhs, rhsFn) {
4
6
  if (lhs != null) {
5
7
  return lhs;
@@ -359,3 +361,6 @@ defineProvider({
359
361
  };
360
362
  },
361
363
  });
364
+
365
+ // END UPSTREAM
366
+ })(globalThis.defineProvider);
@@ -13,9 +13,17 @@
13
13
  "BIFROST_API_KEY",
14
14
  "BIFROST_BASE_URL"
15
15
  ],
16
+ "setupFields": [
17
+ {
18
+ "key": "BIFROST_BASE_URL",
19
+ "title": "Base URL",
20
+ "type": "string",
21
+ "description": "Provider-specific account or source selection."
22
+ }
23
+ ],
16
24
  "upstream": {
17
25
  "repository": "https://github.com/steipete/CodexBar",
18
- "revision": "6ba06fe9ec1522dd5c59e16810115bce28aa148f",
26
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
19
27
  "path": "Sources/CodexBarCore/Resources/Plugins/bifrost.js",
20
28
  "sha256": "b7576d59ccd077c812118cf09d282042589a80df470c621e414583050f03aa07",
21
29
  "license": "MIT"
@@ -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,4 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M17.0674 3.26367L17.0674 2.26367H17.0674V3.26367ZM17.0674 18.0566V19.0566H17.0674L17.0674 18.0566ZM7.66211 18.0566V17.0566C7.42949 17.0566 7.20415 17.1377 7.02487 17.286L7.66211 18.0566ZM4.41895 20.7383H3.41895C3.41895 21.1256 3.64261 21.4781 3.99306 21.6431C4.3435 21.808 4.75768 21.7558 5.05618 21.509L4.41895 20.7383ZM4.41895 17.165H5.41895C5.41895 16.8637 5.28308 16.5785 5.04912 16.3886L4.41895 17.165ZM6.93359 3.26367V2.26367H6.93357L6.93359 3.26367ZM17.0674 3.26367L17.0674 4.26367C18.721 4.26372 20.0615 5.6042 20.0615 7.25781H21.0615H22.0615C22.0615 4.49957 19.8256 2.26375 17.0674 2.26367L17.0674 3.26367ZM21.0615 7.25781H20.0615V14.0625H21.0615H22.0615V7.25781H21.0615ZM21.0615 14.0625H20.0615C20.0615 15.7161 18.721 17.0566 17.0674 17.0566L17.0674 18.0566L17.0674 19.0566C19.8256 19.0566 22.0615 16.8207 22.0615 14.0625H21.0615ZM17.0674 18.0566V17.0566H7.66211V18.0566V19.0566H17.0674V18.0566ZM7.66211 18.0566L7.02487 17.286L3.78171 19.9676L4.41895 20.7383L5.05618 21.509L8.29934 18.8273L7.66211 18.0566ZM4.41895 20.7383H5.41895V17.165H4.41895H3.41895V20.7383H4.41895ZM4.41895 17.165L5.04912 16.3886C4.37038 15.8377 3.93945 15.0007 3.93945 14.0625H2.93945H1.93945C1.93945 15.6296 2.66288 17.0277 3.78877 17.9415L4.41895 17.165ZM2.93945 14.0625H3.93945V7.25781H2.93945H1.93945V14.0625H2.93945ZM2.93945 7.25781H3.93945C3.93945 5.6042 5.27994 4.26372 6.93362 4.26367L6.93359 3.26367L6.93357 2.26367C4.17542 2.26375 1.93945 4.49957 1.93945 7.25781H2.93945ZM6.93359 3.26367V4.26367H17.0674V3.26367V2.26367H6.93359V3.26367Z" fill="#000000"/>
3
+ <path d="M9.13379 9.29883C9.83353 9.29883 10.4014 9.86667 10.4014 10.5664C10.4012 11.266 9.83346 11.833 9.13379 11.833C8.43421 11.8329 7.86731 11.266 7.86719 10.5664C7.86719 9.86673 8.43414 9.29893 9.13379 9.29883ZM14.8662 9.29883C15.566 9.29883 16.1338 9.86667 16.1338 10.5664C16.1337 11.266 15.5659 11.833 14.8662 11.833C14.1666 11.8329 13.5997 11.266 13.5996 10.5664C13.5996 9.86673 14.1666 9.29893 14.8662 9.29883Z" fill="#000000"/>
4
+ </svg>
@@ -0,0 +1,208 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Plan","Top-up credits","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/chatbox.js
5
+ function _nullishCoalesce(lhs, rhsFn) {
6
+ if (lhs != null) {
7
+ return lhs;
8
+ } else {
9
+ return rhsFn();
10
+ }
11
+ }
12
+ function _optionalChain(ops) {
13
+ let lastAccessLHS = undefined;
14
+ let value = ops[0];
15
+ let i = 1;
16
+ while (i < ops.length) {
17
+ const op = ops[i];
18
+ const fn = ops[i + 1];
19
+ i += 2;
20
+ if ((op === "optionalAccess" || op === "optionalCall") && value == null) {
21
+ return undefined;
22
+ }
23
+ if (op === "access" || op === "optionalAccess") {
24
+ lastAccessLHS = value;
25
+ value = fn(value);
26
+ } else if (op === "call" || op === "optionalCall") {
27
+ value = fn((...args) => value.call(lastAccessLHS, ...args));
28
+ lastAccessLHS = undefined;
29
+ }
30
+ }
31
+ return value;
32
+ }
33
+ defineProvider({
34
+ id: "chatbox",
35
+ name: "Chatbox AI",
36
+ endpoints: ["https://api.chatboxai.app"],
37
+ auth: { type: "header", header: "Authorization", secret: "CHATBOX_LICENSE_KEY" },
38
+ settings: [
39
+ { key: "CHATBOX_LICENSE_KEY", title: "API key", type: "secure" },
40
+ { key: "MESSAGES", title: "Chatbox AI", type: "plain" },
41
+ ],
42
+ capabilities: ["http-status"],
43
+ async fetchUsage(ctx) {
44
+ const messages = JSON.parse(ctx.settings.get("MESSAGES") || "{}");
45
+ const message = (key, status) => (messages[key] || key) + (status === undefined ? "" : ` (HTTP ${status})`);
46
+ const key = ctx.settings.getSecret("CHATBOX_LICENSE_KEY");
47
+ if (
48
+ !key ||
49
+ key.length > 4096 ||
50
+ /\s/u.test(key) ||
51
+ Array.from(key).some((character) => {
52
+ const code = character.charCodeAt(0);
53
+ return code < 32 || (code >= 127 && code <= 159);
54
+ })
55
+ ) {
56
+ throw ctx.fail.authenticationExpired(message("Chatbox AI license key is invalid. Update it in Settings."));
57
+ }
58
+ const response = await ctx.http.get("https://api.chatboxai.app/api/license/detail/realtime", {
59
+ headers: { "Cache-Control": "no-store" },
60
+ });
61
+ const record = (value) => (value && typeof value === "object" && !Array.isArray(value) ? value : undefined);
62
+ const decode = (body) => {
63
+ try {
64
+ return record(JSON.parse(body));
65
+ } catch (error) {
66
+ void error;
67
+ return undefined;
68
+ }
69
+ };
70
+ if (response.status === 401) {
71
+ throw ctx.fail.authenticationExpired(
72
+ message("Chatbox AI license key is invalid. Update it in Settings.", response.status),
73
+ );
74
+ }
75
+ if (response.status === 403) {
76
+ throw ctx.fail.permissionDenied(message("Chatbox AI denied access. Check your license.", response.status));
77
+ }
78
+ if (response.status === 429) {
79
+ const delay = Number(_nullishCoalesce(response.headers["retry-after"], () => 1));
80
+ throw ctx.fail.rateLimited(message("Chatbox AI is rate limiting requests. Try again later.", response.status), {
81
+ retryAfterSeconds: Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 10) : 1,
82
+ });
83
+ }
84
+ if (response.status >= 500) {
85
+ throw ctx.fail.providerUnavailable(message("Chatbox AI request failed. Try refreshing later.", response.status));
86
+ }
87
+ if (response.status !== 200) {
88
+ throw ctx.fail.apiFailure(message("Chatbox AI request failed. Try refreshing later.", response.status));
89
+ }
90
+ const fail = () => {
91
+ throw ctx.fail.parseFailure(
92
+ message("Chatbox AI returned an unrecognized response. Update CodexBar and try again."),
93
+ );
94
+ };
95
+ const root = decode(response.bodyText);
96
+ const data = record(_optionalChain([root, "optionalAccess", (_) => _.data]));
97
+ if (!data || _optionalChain([root, "optionalAccess", (_2) => _2.error])) return fail();
98
+ const now = ctx.date.now().getTime();
99
+ const text = (value) =>
100
+ typeof value === "string"
101
+ ? Array.from(value, (character) => {
102
+ const code = character.charCodeAt(0);
103
+ return code < 32 || code === 127 ? " " : character;
104
+ })
105
+ .join("")
106
+ .trim()
107
+ .slice(0, 100) || undefined
108
+ : undefined;
109
+ const counter = (value) => {
110
+ if (value === undefined || value === null) return undefined;
111
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : fail();
112
+ };
113
+ const date = (value) => {
114
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T/.test(value)) return undefined;
115
+ const parsed = new Date(value);
116
+ return Number.isFinite(parsed.getTime()) && parsed.getUTCFullYear() >= 1970 ? parsed : undefined;
117
+ };
118
+
119
+ const pool = (object, usedKey, limitKey) => {
120
+ const used = counter(object[usedKey]);
121
+ const limit = counter(object[limitKey]);
122
+ return used !== undefined && limit !== undefined ? { used, limit } : undefined;
123
+ };
124
+ const rows = [];
125
+ const addPool = (label, value) => {
126
+ if (!value || (value.used === 0 && value.limit === 0)) return;
127
+ const remaining = Math.max(value.limit - value.used, 0);
128
+ rows.push({
129
+ label,
130
+ value: `${ctx.format.number(remaining)} / ${ctx.format.number(value.limit)}`,
131
+ usageValue: value.used,
132
+ });
133
+ };
134
+ const details = data.unified_token_usage_details;
135
+ let plan;
136
+ let detailRewards;
137
+ if (details !== undefined && details !== null) {
138
+ if (!Array.isArray(details) || details.length > 100) return fail();
139
+ for (const entry of details) {
140
+ const item = record(entry);
141
+ if (!item || typeof item.type !== "string") return fail();
142
+ const value = pool(item, "token_usage", "token_limit");
143
+ if (item.type === "plan") {
144
+ if (plan) return fail();
145
+ plan = value;
146
+ } else if (value) {
147
+ const expires = date(item.expires_at);
148
+ if (expires && expires.getTime() <= now) continue;
149
+ detailRewards = {
150
+ used:
151
+ _nullishCoalesce(_optionalChain([detailRewards, "optionalAccess", (_3) => _3.used]), () => 0) +
152
+ value.used,
153
+ limit:
154
+ _nullishCoalesce(_optionalChain([detailRewards, "optionalAccess", (_4) => _4.limit]), () => 0) +
155
+ value.limit,
156
+ };
157
+ if (!Number.isSafeInteger(detailRewards.used) || !Number.isSafeInteger(detailRewards.limit)) return fail();
158
+ }
159
+ }
160
+ }
161
+ const total = pool(data, "unified_token_usage", "unified_token_limit");
162
+ const rewardRecord = record(data.aggregated_reward_details);
163
+ if (data.aggregated_reward_details != null && !rewardRecord) return fail();
164
+ const rewards = rewardRecord ? pool(rewardRecord, "token_usage", "token_limit") : detailRewards;
165
+ const expansion = pool(data, "expansion_pack_usage", "expansion_pack_limit");
166
+ const images = pool(data, "image_used_count", "image_total_quota");
167
+ if (!plan && !total && !rewards && !expansion && !images) return fail();
168
+ addPool("Plan", plan);
169
+ if (!plan) addPool("Quota", total);
170
+ addPool("Credits", rewards);
171
+ addPool("Top-up credits", expansion);
172
+ addPool("Image Generation", images);
173
+ const name = text(data.name);
174
+ const status = text(data.status);
175
+ const expires = _nullishCoalesce(date(data.token_expire_time), () => date(data.expires_at));
176
+ const next = date(data.token_next_refresh_time);
177
+ const usablePlan = (status === undefined || status === "active") && (!expires || expires.getTime() > now);
178
+ // Trial has no positive plan pool. Rewards and image totals do not share the plan's reset schedule.
179
+ const reset =
180
+ usablePlan &&
181
+ plan &&
182
+ plan.limit > 0 &&
183
+ next &&
184
+ next.getTime() > now &&
185
+ (!expires || next.getTime() < expires.getTime())
186
+ ? next
187
+ : undefined;
188
+ if (rows.length === 0) rows.push({ label: "Quota", value: "0 / 0", usageValue: 0 });
189
+ return {
190
+ primary:
191
+ usablePlan && plan && plan.limit > 0
192
+ ? {
193
+ usedPercent: ctx.pct(plan.used, plan.limit),
194
+ resetsAt: reset,
195
+ }
196
+ : undefined,
197
+ secondary:
198
+ expansion && expansion.limit > 0 ? { usedPercent: ctx.pct(expansion.used, expansion.limit) } : undefined,
199
+ identity: name ? { loginMethod: name } : undefined,
200
+ details: [{ title: "Remaining", rows }],
201
+ subscriptionExpiresAt: expires,
202
+ dataConfidence: "exact",
203
+ };
204
+ },
205
+ });
206
+
207
+ // END UPSTREAM
208
+ })(globalThis.defineProvider);
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "chatbox",
4
+ "name": "Chatbox AI",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "api"
10
+ ],
11
+ "autoMode": "api",
12
+ "usageDashboardUrl": "https://chatboxai.app",
13
+ "envVars": [
14
+ "CHATBOX_LICENSE_KEY"
15
+ ],
16
+ "setupFields": [],
17
+ "upstream": {
18
+ "repository": "https://github.com/steipete/CodexBar",
19
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
20
+ "path": "Sources/CodexBarCore/Resources/Plugins/chatbox.js",
21
+ "sha256": "cf0ae84410aa3794635c0df1edf4936ae514a070968a189dc6ef613c5445f957",
22
+ "license": "MIT"
23
+ }
24
+ }
@@ -19,6 +19,7 @@
19
19
  // Rate-limit state persisted across probe() calls (module scope survives re-invocations).
20
20
  const MIN_USAGE_FETCH_INTERVAL_MS = 5 * 60 * 1000 // never poll more than once per 5 min
21
21
  const DEFAULT_RATE_LIMIT_BACKOFF_MS = 5 * 60 * 1000 // fallback when no Retry-After header
22
+ const RESET_GRANTS_CHECK_INTERVAL_MS = 60 * 60 * 1000
22
23
  const LIVE_USAGE_CACHE_FILE = "live-usage-cache.json"
23
24
  const LIVE_USAGE_CACHE_VERSION = 2
24
25
  const LIVE_USAGE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000
@@ -596,15 +597,19 @@
596
597
 
597
598
  function fetchUsage(ctx, accessToken) {
598
599
  const oauthConfig = getOauthConfig(ctx)
600
+ const cache = readLiveUsageCache(ctx)
601
+ const now = Date.parse(ctx.nowIso)
602
+ const checkedAt = Number(cache && cache.resetGrantsCheckedAtMs)
603
+ const checkGrants = !Number.isFinite(checkedAt) || checkedAt <= 0 || now < checkedAt || now - checkedAt >= RESET_GRANTS_CHECK_INTERVAL_MS
599
604
  let version = "2.1.0"
600
605
  try {
601
606
  const result = ctx.host.command.run({program: "claude", args: ["--version"], timeoutMs: 3000})
602
607
  const match = result.status === 0 && /^\s*(\d+\.\d+\.\d+)(?:\s|$)/.exec(result.stdout || "")
603
608
  if (match) version = match[1]
604
609
  } catch (_) { /* The OAuth source also works without a local CLI. */ }
605
- return ctx.util.request({
610
+ const response = ctx.util.request({
606
611
  method: "GET",
607
- url: oauthConfig.usageUrl + "?cedar_ember=1&skip_spend=1",
612
+ url: oauthConfig.usageUrl + (checkGrants ? "?cedar_ember=1&skip_spend=1" : ""),
608
613
  headers: {
609
614
  Authorization: "Bearer " + accessToken.trim(),
610
615
  Accept: "application/json",
@@ -614,6 +619,21 @@
614
619
  },
615
620
  timeoutMs: 10000,
616
621
  })
622
+ if (response.status >= 200 && response.status < 300) {
623
+ const data = ctx.util.tryParseJson(response.bodyText)
624
+ if (data && typeof data === "object" && !Array.isArray(data) && !data.error) {
625
+ const validLines=[]
626
+ addUsageWindowLines(ctx,data,validLines)
627
+ if (!validLines.length) return response
628
+ if (checkGrants || data.cedar_ember) {
629
+ writeLiveUsageCache(ctx, {resetGrantsCheckedAtMs:now,resetGrantsBlock: data.cedar_ember || null})
630
+ } else if (cache && cache.resetGrantsBlock) {
631
+ data.cedar_ember = cache.resetGrantsBlock
632
+ response.bodyText = JSON.stringify(data)
633
+ }
634
+ }
635
+ }
636
+ return response
617
637
  }
618
638
 
619
639
  function parseRetryAfterSeconds(headers) {
@@ -1520,6 +1540,23 @@
1520
1540
  subtitle: expiries.length ? "Expires " + expiries.sort().join(", ") : undefined}))
1521
1541
  }
1522
1542
 
1543
+ const cloud = data.iguana_necktie
1544
+ if (cloud && typeof cloud === 'object' && !Array.isArray(cloud)) {
1545
+ const limit = cloud.limit_dollars, used = cloud.used_dollars
1546
+ const remaining = cloud.remaining_dollars != null ? cloud.remaining_dollars
1547
+ : typeof used === 'number' ? limit - used : null
1548
+ const validMoney = value => typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= limit
1549
+ const expires = cloud.resets_at == null ? null : ctx.util.toIso(cloud.resets_at)
1550
+ if (typeof limit === 'number' && Number.isFinite(limit) && limit > 0 && validMoney(remaining) &&
1551
+ (used == null || validMoney(used)) && (cloud.resets_at == null || expires) &&
1552
+ (cloud.locked_reason == null || typeof cloud.locked_reason === 'string')) {
1553
+ const expired = expires && Date.parse(expires) <= Date.parse(ctx.nowIso)
1554
+ const locked = typeof cloud.locked_reason === 'string' && cloud.locked_reason.trim()
1555
+ lines.push(ctx.line.text({label:'Cloud credits',value:expired ? 'Expired' : locked ? 'Unavailable' : '$'+remaining.toFixed(2)+' remaining',
1556
+ subtitle:expires ? (expired ? 'Expired ' : 'Expires ')+expires : undefined}))
1557
+ }
1558
+ }
1559
+
1523
1560
  if (data.extra_usage && data.extra_usage.is_enabled) {
1524
1561
  const used = moneyMajorUnits(data.extra_usage.used_credits)
1525
1562
  const limit = moneyMajorUnits(data.extra_usage.monthly_limit)
@@ -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,3 @@
1
+ <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M25.7146 63.2153L41.4393 54.3917L41.7025 53.6226L41.4393 53.1976H40.6705L38.0394 53.0359L29.054 52.7929L21.2624 52.4691L13.7134 52.0644L11.8111 51.6594L10.0303 49.3118L10.2123 48.138L11.8111 47.0657L14.0981 47.2681L19.1574 47.6119L26.7467 48.138L32.2516 48.4618L40.4073 49.3118H41.7025L41.8846 48.7857L41.4393 48.4618L41.0955 48.138L33.243 42.8155L24.7432 37.1894L20.2909 33.9513L17.8824 32.3119L16.6684 30.774L16.1422 27.4147L18.328 25.0062L21.2624 25.2088L22.0112 25.4112L24.9861 27.6979L31.3407 32.616L39.6381 38.7273L40.8525 39.7391L41.3381 39.395L41.399 39.1523L40.8525 38.2415L36.3394 30.0858L31.5227 21.7883L29.3775 18.3478L28.811 16.2837C28.6087 15.4334 28.4669 14.7252 28.4669 13.8549L30.9563 10.4753L32.3321 10.0303L35.6515 10.4756L37.0479 11.6897L39.112 16.4052L42.4513 23.8327L47.6321 33.9313L49.15 36.9265L49.9594 39.6991L50.2632 40.5491H50.7894V40.0632L51.2141 34.3766L52.0035 27.3944L52.7726 18.4087L53.0358 15.8793L54.2905 12.8435L56.7795 11.2041L58.7224 12.135L60.3212 14.422L60.0986 15.899L59.1474 22.0718L57.2857 31.7458L56.0713 38.2218H56.7795L57.5892 37.4121L60.8677 33.061L66.3723 26.18L68.801 23.448L71.6342 20.4325L73.4556 18.9957H76.8962L79.4255 22.7601L78.2926 26.6456L74.7509 31.1384L71.8163 34.943L67.607 40.6097L64.9758 45.1431L65.2188 45.5072L65.8464 45.4466L75.358 43.4228L80.4984 42.4917L86.6304 41.4393L89.4033 42.7346L89.7065 44.0502L88.6135 46.7419L82.0566 48.3607L74.3662 49.8989L62.9118 52.6109L62.77 52.7121L62.9321 52.9144L68.0925 53.4L70.2987 53.5214H75.7021L85.7601 54.2702L88.3912 56.0108L89.9697 58.1358L89.7065 59.7545L85.6589 61.8189L80.1949 60.5236L67.4452 57.4881L63.0735 56.3952H62.4665V56.7596L66.1093 60.3213L72.7877 66.3523L81.1461 74.1236L81.5707 76.0462L80.4984 77.5638L79.3649 77.4021L72.0186 71.8772L69.1854 69.3879L62.77 63.9844H62.3453V64.5509L63.8223 66.7164L71.6342 78.4544L72.0389 82.0567L71.4725 83.2308L69.4487 83.939L67.2222 83.534L62.6485 77.1189L57.9333 69.8937L54.1284 63.4177L53.6631 63.6809L51.4167 87.8651L50.3644 89.0995L47.9356 90.0303L45.9121 88.4924L44.8392 86.0031L45.9118 81.0852L47.2071 74.6701L48.2594 69.5699L49.2106 63.2356L49.7773 61.131L49.7367 60.9892L49.2715 61.0498L44.4954 67.607L37.23 77.4224L31.4825 83.5746L30.1063 84.1211L27.7181 82.8864L27.9408 80.6805L29.2763 78.7177L37.2297 68.5988L42.026 62.3248L45.1227 58.7025L45.1024 58.176H44.9204L23.7917 71.8975L20.0274 72.3831L18.4083 70.8655L18.6106 68.3761L19.3798 67.5664L25.7343 63.195L25.7146 63.2153Z" fill="currentColor"/>
3
+ </svg>