@hashimkarim/usagestat-linux-x64-gnu 2.0.1 → 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 (161) hide show
  1. package/native-manifest.json +743 -119
  2. package/package.json +106 -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 +20 -11
  15. package/plugins/antigravity-cli/plugin.js +9 -4
  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 +33 -8
  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 +49 -41
  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/LICENSE +21 -0
  83. package/plugins/lithosai/icon.svg +1 -0
  84. package/plugins/lithosai/plugin.js +130 -0
  85. package/plugins/lithosai/plugin.json +26 -0
  86. package/plugins/llmman/icon.svg +4 -0
  87. package/plugins/llmman/plugin.js +5 -0
  88. package/plugins/llmman/plugin.json +9 -1
  89. package/plugins/munito/LICENSE +21 -0
  90. package/plugins/munito/icon.svg +7 -0
  91. package/plugins/munito/plugin.js +171 -0
  92. package/plugins/munito/plugin.json +24 -0
  93. package/plugins/muse/icon.svg +3 -0
  94. package/plugins/muse/plugin.js +98 -129
  95. package/plugins/muse/plugin.json +10 -2
  96. package/plugins/museai/LICENSE +21 -0
  97. package/plugins/museai/icon.svg +3 -0
  98. package/plugins/museai/plugin.js +159 -0
  99. package/plugins/museai/plugin.json +26 -0
  100. package/plugins/nous/plugin.js +5 -0
  101. package/plugins/nous/plugin.json +9 -1
  102. package/plugins/ollama/plugin.js +58 -0
  103. package/plugins/opencode-go/plugin.js +72 -24
  104. package/plugins/parallel/LICENSE +21 -0
  105. package/plugins/parallel/icon.svg +11 -0
  106. package/plugins/parallel/plugin.js +97 -0
  107. package/plugins/parallel/plugin.json +25 -0
  108. package/plugins/qwencloud/LICENSE +21 -0
  109. package/plugins/qwencloud/plugin.js +197 -0
  110. package/plugins/qwencloud/plugin.json +17 -0
  111. package/plugins/raycast/icon.svg +4 -0
  112. package/plugins/raycast/plugin.js +5 -0
  113. package/plugins/raycast/plugin.json +9 -1
  114. package/plugins/replicate/plugin.js +5 -0
  115. package/plugins/replicate/plugin.json +2 -1
  116. package/plugins/sailresearch/LICENSE +21 -0
  117. package/plugins/sailresearch/icon.svg +4 -0
  118. package/plugins/sailresearch/plugin.js +104 -0
  119. package/plugins/sailresearch/plugin.json +24 -0
  120. package/plugins/sofya/LICENSE +21 -0
  121. package/plugins/sofya/icon.svg +3 -0
  122. package/plugins/sofya/plugin.js +86 -0
  123. package/plugins/sofya/plugin.json +24 -0
  124. package/plugins/soniox/LICENSE +21 -0
  125. package/plugins/soniox/icon.svg +3 -0
  126. package/plugins/soniox/plugin.js +103 -0
  127. package/plugins/soniox/plugin.json +24 -0
  128. package/plugins/tavily/LICENSE +21 -0
  129. package/plugins/tavily/icon.svg +3 -0
  130. package/plugins/tavily/plugin.js +61 -0
  131. package/plugins/tavily/plugin.json +24 -0
  132. package/plugins/tinyapi/LICENSE +21 -0
  133. package/plugins/tinyapi/icon.svg +4 -0
  134. package/plugins/tinyapi/plugin.js +73 -0
  135. package/plugins/tinyapi/plugin.json +25 -0
  136. package/plugins/traecn/LICENSE +21 -0
  137. package/plugins/traecn/icon.svg +3 -0
  138. package/plugins/traecn/plugin.js +104 -0
  139. package/plugins/traecn/plugin.json +25 -0
  140. package/plugins/typesafe/icon.svg +4 -0
  141. package/plugins/typesafe/plugin.js +30 -25
  142. package/plugins/typesafe/plugin.json +3 -2
  143. package/plugins/v0/plugin.js +5 -0
  144. package/plugins/v0/plugin.json +9 -1
  145. package/plugins/vercel/plugin.js +147 -0
  146. package/plugins/vercel/plugin.json +34 -3
  147. package/plugins/workbuddy/LICENSE +21 -0
  148. package/plugins/workbuddy/icon.svg +1 -0
  149. package/plugins/workbuddy/plugin.js +232 -0
  150. package/plugins/workbuddy/plugin.json +51 -0
  151. package/plugins/xai/plugin.js +9 -0
  152. package/plugins/xapi/LICENSE +21 -0
  153. package/plugins/xapi/icon.svg +1 -0
  154. package/plugins/xapi/plugin.js +90 -0
  155. package/plugins/xapi/plugin.json +25 -0
  156. package/plugins/xkiro/icon.svg +1 -0
  157. package/plugins/xkiro/plugin.js +5 -0
  158. package/plugins/xkiro/plugin.json +2 -1
  159. package/usagestat +0 -0
  160. package/usagestatd +0 -0
  161. package/plugins/cursor-nightly/plugin.js +0 -1416
@@ -0,0 +1,73 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Credits","Additional","Additional"],"defaults":{},"autoMode":"web"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/tinyapi.js
5
+ defineProvider({
6
+ id: "tinyapi",
7
+ name: "TinyApi",
8
+ endpoints: ["https://tinyapi.rest"],
9
+ settings: [],
10
+ capabilities: ["browser-cookies", "http-status"],
11
+ cookieDomains: ["tinyapi.rest"],
12
+ async fetchUsage(ctx) {
13
+ const domain = "tinyapi.rest";
14
+ const policy = ctx.browser.availability(domain);
15
+ if (policy === "off") throw ctx.fail.missingCredential("TinyApi cookies are disabled.");
16
+ let response;
17
+ let rejected = false;
18
+ for await (const session of ctx.browser.sessions(domain)) {
19
+ const candidate = await ctx.http.get(`https://${domain}/api/user/credits`, {
20
+ headers: { Cookie: session.header },
21
+ timeoutSeconds: 15,
22
+ });
23
+ if (candidate.status === 401 || candidate.status === 403) {
24
+ rejected = true;
25
+ ctx.browser.rejectCookie(domain, session);
26
+ if (policy === "manual") break;
27
+ continue;
28
+ }
29
+ response = candidate;
30
+ break;
31
+ }
32
+ if (!response) {
33
+ throw rejected
34
+ ? ctx.fail.authenticationExpired("TinyApi session expired. Sign in again or paste a fresh Cookie header.")
35
+ : ctx.fail.missingCredential(
36
+ `Sign in to tinyapi.rest. Supported browsers: ${ctx.browser.supportedBrowsers}. Or set a manual Cookie header.`,
37
+ );
38
+ }
39
+ if (response.status === 429) throw ctx.fail.rateLimited("TinyApi credits requests are rate limited.");
40
+ if (response.status >= 500)
41
+ throw ctx.fail.providerUnavailable(`TinyApi credits service returned HTTP ${response.status}.`);
42
+ if (response.status !== 200) throw ctx.fail.apiFailure(`TinyApi credits request returned HTTP ${response.status}.`);
43
+ let payload;
44
+ try {
45
+ payload = JSON.parse(response.bodyText);
46
+ } catch {
47
+ throw ctx.fail.parseFailure("TinyApi credits response is not valid JSON.");
48
+ }
49
+ const balance = payload?.data?.totalAvailable;
50
+ if (payload?.success !== true || typeof balance !== "number" || !Number.isFinite(balance) || balance < 0) {
51
+ throw ctx.fail.parseFailure("TinyApi credits response is missing a valid available balance.");
52
+ }
53
+ return {
54
+ details: [
55
+ {
56
+ title: "Credits",
57
+ rows: [
58
+ {
59
+ label: "Available credits",
60
+ value: `${ctx.format.number(balance, { maximumFractionDigits: 2 })} credits`,
61
+ usageValue: balance,
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ identity: { loginMethod: "Browser session" },
67
+ dataConfidence: "exact",
68
+ };
69
+ },
70
+ });
71
+
72
+ // END UPSTREAM
73
+ })(globalThis.defineProvider);
@@ -0,0 +1,25 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "tinyapi",
4
+ "name": "TinyApi",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "web"
10
+ ],
11
+ "autoMode": "web",
12
+ "usageDashboardUrl": "https://tinyapi.rest",
13
+ "webUrl": "https://tinyapi.rest",
14
+ "envVars": [
15
+ "TINYAPI_COOKIE"
16
+ ],
17
+ "setupFields": [],
18
+ "upstream": {
19
+ "repository": "https://github.com/steipete/CodexBar",
20
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
21
+ "path": "Sources/CodexBarCore/Resources/Plugins/tinyapi.js",
22
+ "sha256": "14f874b433198e9bfb9f1d689a0822f35ac8d97945cab015c039f836eed4ef17",
23
+ "license": "MIT"
24
+ }
25
+ }
@@ -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="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
2
+ <path fill-rule="evenodd" d="M24 20.541H3.428v-3.426H0V3.4h24V20.54zM3.428 17.115h17.144V6.827H3.428v10.288zm8.573-5.196l-2.425 2.424-2.424-2.424 2.424-2.424 2.425 2.424zm6.857-.001l-2.424 2.423-2.425-2.423 2.425-2.425 2.424 2.425z"/>
3
+ </svg>
@@ -0,0 +1,104 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Credits","Additional","Additional"],"defaults":{},"autoMode":"web"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/traecn.js
5
+ defineProvider({
6
+ id: "traecn",
7
+ name: "Trae CN",
8
+ endpoints: ["https://api.trae.cn"],
9
+ settings: [],
10
+ capabilities: ["browser-cookies", "http-status"],
11
+ // Include parent-domain sessions shared by the dashboard and API.
12
+ cookieDomains: ["trae.cn", "api.trae.cn"],
13
+ async fetchUsage(ctx) {
14
+ const domain = ctx.browser.availability("api.trae.cn") === "manual" ? "api.trae.cn" : "trae.cn";
15
+ const signIn = "Sign in to Trae CN at trae.cn, then refresh CodexBar.";
16
+ const malformed = () => {
17
+ throw ctx.fail.parseFailure("Trae CN usage response format changed.");
18
+ };
19
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
20
+ const rejected = (response) =>
21
+ response.status === 401 || response.status === 403 || (response.status >= 300 && response.status < 400);
22
+ const read = (response) => {
23
+ const message = "Trae CN request failed. Try refreshing later.";
24
+ if (response.status === 429) throw ctx.fail.rateLimited(message);
25
+ if (response.status === 408 || response.status >= 500) throw ctx.fail.providerUnavailable(message);
26
+ if (response.status !== 200) throw ctx.fail.apiFailure(message);
27
+ try {
28
+ return JSON.parse(response.bodyText);
29
+ } catch {
30
+ return malformed();
31
+ }
32
+ };
33
+ if (ctx.browser.availability(domain) === "off") throw ctx.fail.missingCredential(signIn);
34
+ let sessionRejected = false;
35
+ for await (const session of ctx.browser.sessions(domain)) {
36
+ const headers = { Cookie: session.header ?? "", Origin: "https://www.trae.cn", Referer: "https://www.trae.cn/" };
37
+ const tokenResponse = await ctx.http.post("https://api.trae.cn/cloudide/api/v3/common/GetUserToken", {
38
+ headers,
39
+ body: {},
40
+ timeoutSeconds: 15,
41
+ });
42
+ if (rejected(tokenResponse)) {
43
+ sessionRejected = true;
44
+ ctx.browser.rejectCookie(domain, session);
45
+ continue;
46
+ }
47
+ const tokenPayload = read(tokenResponse);
48
+ const token = tokenPayload?.Result?.Token;
49
+ if (typeof token !== "string" || !token.trim()) return malformed();
50
+ // The exchanged token lives only for this refresh, and only goes back to the declared API origin.
51
+ const usageResponse = await ctx.http.post("https://api.trae.cn/trae/api/v2/pay/user_current_entitlement_list", {
52
+ headers: { ...headers, Authorization: `Cloud-IDE-JWT ${token}` },
53
+ body: { require_usage: true, full_data: true },
54
+ timeoutSeconds: 15,
55
+ });
56
+ if (rejected(usageResponse)) {
57
+ sessionRejected = true;
58
+ ctx.browser.rejectCookie(domain, session);
59
+ continue;
60
+ }
61
+ const payload = read(usageResponse);
62
+ const summary = payload?.usage_summary;
63
+ if (payload?.code === 1001) {
64
+ sessionRejected = true;
65
+ ctx.browser.rejectCookie(domain, session);
66
+ continue;
67
+ }
68
+ if (
69
+ payload?.is_credits_billing !== true ||
70
+ !summary ||
71
+ !finite(summary.total_amount) ||
72
+ !finite(summary.consumed_amount)
73
+ )
74
+ return malformed();
75
+ const { total_amount: total, consumed_amount: used } = summary;
76
+ const packs = Array.isArray(payload.user_entitlement_pack_list) ? payload.user_entitlement_pack_list : [];
77
+ const subscription = packs.find(
78
+ (pack) => pack && pack.group_type === 0 && (pack.status === undefined || pack.status === 1),
79
+ );
80
+ const plan = typeof subscription?.display_desc === "string" ? subscription.display_desc.trim() : undefined;
81
+ const number = (value) => ctx.format.number(value, { maximumFractionDigits: 2 });
82
+ // Pack expiry and billing timestamps do not establish an aggregate reset or subscription renewal.
83
+ return {
84
+ primary: total > 0 ? { usedPercent: Math.min(100, (used / total) * 100) } : undefined,
85
+ identity: plan ? { loginMethod: plan } : undefined,
86
+ details: [
87
+ {
88
+ title: "Credits",
89
+ rows: [
90
+ { label: "Used", value: number(used) },
91
+ { label: "Total", value: number(total) },
92
+ { label: "Remaining", value: number(Math.max(0, total - used)) },
93
+ ],
94
+ },
95
+ ],
96
+ dataConfidence: "exact",
97
+ };
98
+ }
99
+ throw sessionRejected ? ctx.fail.authenticationExpired(signIn) : ctx.fail.missingCredential(signIn);
100
+ },
101
+ });
102
+
103
+ // END UPSTREAM
104
+ })(globalThis.defineProvider);
@@ -0,0 +1,25 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "traecn",
4
+ "name": "Trae CN",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "web"
10
+ ],
11
+ "autoMode": "web",
12
+ "usageDashboardUrl": "https://www.trae.cn",
13
+ "webUrl": "https://trae.cn",
14
+ "envVars": [
15
+ "TRAECN_COOKIE"
16
+ ],
17
+ "setupFields": [],
18
+ "upstream": {
19
+ "repository": "https://github.com/steipete/CodexBar",
20
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
21
+ "path": "Sources/CodexBarCore/Resources/Plugins/traecn.js",
22
+ "sha256": "045bf8cbba44d779fc69a1ddbd14598a1d1178f265be875336b36b261af5604a",
23
+ "license": "MIT"
24
+ }
25
+ }
@@ -0,0 +1,4 @@
1
+ <svg fill="currentColor" viewBox="24.68 24.68 350.65 350.65" xmlns="http://www.w3.org/2000/svg">
2
+ <title>TypeSafe</title>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M256.407 86.5946V138.33L303.032 168.589L303.045 283.148L199.987 350L143.594 313.42V260.25L96.9556 229.993V116.866L101.395 113.977L199.999 50L256.407 86.5946ZM171.208 308.124L199.975 326.788L275.394 277.838L246.639 259.188L171.208 308.124ZM209.728 179.157V230.005L163.078 260.264V290.192L236.923 242.283V161.517L209.728 179.157ZM256.407 242.31L283.55 259.92V179.144L256.407 161.532V242.31ZM124.582 224.709L153.337 243.374L182.104 224.709L153.349 206.057L124.582 224.709ZM116.438 127.421V206.791L143.594 189.166V138.318L190.258 108.034V79.5105L116.438 127.421ZM163.078 189.154L190.246 206.779V179.144L163.078 161.517V189.154ZM171.208 143.589L199.999 162.266L228.768 143.601L199.987 124.925L171.208 143.589ZM209.742 108.034L236.923 125.671V97.1619L209.742 79.5105V108.034Z" />
4
+ </svg>
@@ -1,5 +1,7 @@
1
1
  // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
2
  globalThis.__usagestat_bundled_options = {"labels":["Spend","Spend","Additional"],"defaults":{},"autoMode":"web"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/typesafe.js
3
5
  function _optionalChain(ops) {
4
6
  let lastAccessLHS = undefined;
5
7
  let value = ops[0];
@@ -24,6 +26,9 @@ function _optionalChain(ops) {
24
26
 
25
27
  const billingURL = "https://console.typesafe.ai/settings/billing";
26
28
  const billingOrigin = "https://console.typesafe.ai";
29
+ // Cloudflare blocks the Linux default UA (it contains "curl/"), so send a browser-style one.
30
+ const userAgent =
31
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
27
32
  const actionCacheKey = "typesafe.billing.next-action";
28
33
  const actionCacheTTL = 12 * 60 * 60;
29
34
  const maximumChunkCount = 60;
@@ -59,15 +64,13 @@ defineProvider({
59
64
  throw ctx.fail.apiFailure(`TypeSafe returned HTTP ${response.status}.`);
60
65
  };
61
66
  // Login pages can also arrive directly with HTTP 200, without an HTTP redirect.
62
- const isLoginLanding = (body) => /\\?"\(auth\)\\?",\{\\?"children\\?":\[\\?"login\\?"/.test(body);
63
67
  const validateBody = (response) => {
64
68
  validate(response);
65
- if (isLoginLanding(response.bodyText)) expired();
69
+ if (/\\?"\(auth\)\\?",\{\\?"children\\?":\[\\?"login\\?"/.test(response.bodyText)) expired();
66
70
  };
67
- const actionIDPattern = /"([0-9a-f]{40,})"[^)]{0,150}"getBillingOverviewResult"/i;
68
71
  const discoverActionID = async (cookie) => {
69
72
  const page = await ctx.http.get(billingURL, {
70
- headers: { Cookie: cookie, Accept: "text/html" },
73
+ headers: { Cookie: cookie, Accept: "text/html", "User-Agent": userAgent },
71
74
  timeoutSeconds: 6,
72
75
  });
73
76
  validateBody(page);
@@ -83,11 +86,11 @@ defineProvider({
83
86
  let discoveryFailure;
84
87
  for (const url of chunkURLs) {
85
88
  try {
86
- const chunk = await ctx.http.get(url, { timeoutSeconds: 2 });
89
+ const chunk = await ctx.http.get(url, { headers: { "User-Agent": userAgent }, timeoutSeconds: 2 });
87
90
  if (chunk.status === 429 || chunk.status === 408 || chunk.status >= 500) validate(chunk);
88
91
  if (chunk.status < 200 || chunk.status >= 300) continue;
89
92
  const action = _optionalChain([
90
- actionIDPattern,
93
+ /"([0-9a-f]{40,})"[^)]{0,150}"getBillingOverviewResult"/i,
91
94
  "access",
92
95
  (_) => _.exec,
93
96
  "call",
@@ -117,6 +120,7 @@ defineProvider({
117
120
  "Next-Action": id,
118
121
  Accept: "text/x-component",
119
122
  "Content-Type": "application/json",
123
+ "User-Agent": userAgent,
120
124
  },
121
125
  body: [],
122
126
  timeoutSeconds: 6,
@@ -188,25 +192,23 @@ defineProvider({
188
192
  const availableRows = 24 - rows.length;
189
193
  const creditRows = [];
190
194
  let creditCount = 0;
191
- if (Array.isArray(billing.credits)) {
192
- for (const item of billing.credits) {
193
- const credit = record(item);
194
- const amount = number(_optionalChain([credit, "optionalAccess", (_14) => _14.amount]));
195
- const remaining = number(_optionalChain([credit, "optionalAccess", (_15) => _15.remaining]));
196
- const expiresAt =
197
- typeof _optionalChain([credit, "optionalAccess", (_16) => _16.expiresAt]) === "string"
198
- ? credit.expiresAt
199
- : undefined;
200
- if (amount === undefined || remaining === undefined || remaining <= 0 || !expiresAt) continue;
201
- const expires = Date.parse(expiresAt);
202
- if (!Number.isFinite(expires)) continue;
203
- creditCount++;
204
- if (creditRows.length >= availableRows) continue;
205
- creditRows.push({
206
- label: "Credit",
207
- value: `${ctx.format.number(remaining)} of ${ctx.format.number(amount)}, expires ${ctx.format.monthDay(expires)}`,
208
- });
209
- }
195
+ for (const item of Array.isArray(billing.credits) ? billing.credits : []) {
196
+ const credit = record(item);
197
+ const amount = number(_optionalChain([credit, "optionalAccess", (_14) => _14.amount]));
198
+ const remaining = number(_optionalChain([credit, "optionalAccess", (_15) => _15.remaining]));
199
+ const expiresAt =
200
+ typeof _optionalChain([credit, "optionalAccess", (_16) => _16.expiresAt]) === "string"
201
+ ? credit.expiresAt
202
+ : undefined;
203
+ if (amount === undefined || remaining === undefined || remaining <= 0 || !expiresAt) continue;
204
+ const expires = Date.parse(expiresAt);
205
+ if (!Number.isFinite(expires)) continue;
206
+ creditCount++;
207
+ if (creditRows.length >= availableRows) continue;
208
+ creditRows.push({
209
+ label: "Credit",
210
+ value: `${ctx.format.number(remaining)} of ${ctx.format.number(amount)}, expires ${ctx.format.monthDay(expires)}`,
211
+ });
210
212
  }
211
213
  if (creditCount > availableRows) {
212
214
  const shown = availableRows - 1;
@@ -223,3 +225,6 @@ defineProvider({
223
225
  };
224
226
  },
225
227
  });
228
+
229
+ // END UPSTREAM
230
+ })(globalThis.defineProvider);
@@ -14,11 +14,12 @@
14
14
  "envVars": [
15
15
  "TYPESAFE_COOKIE"
16
16
  ],
17
+ "setupFields": [],
17
18
  "upstream": {
18
19
  "repository": "https://github.com/steipete/CodexBar",
19
- "revision": "25bba9b7fd9ce83c33053958f7366e23b2dc8a82",
20
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
20
21
  "path": "Sources/CodexBarCore/Resources/Plugins/typesafe.js",
21
- "sha256": "eb3941346a966b3f5f9e0010f17edaeed7dfb6c8b0331319a86f26da10d51980",
22
+ "sha256": "ef710412712c83277acda605517d363a4cea58256e33513477b7b863ccf6dc06",
22
23
  "license": "MIT"
23
24
  }
24
25
  }
@@ -1,5 +1,7 @@
1
1
  // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
2
  globalThis.__usagestat_bundled_options = {"labels":["Billing","Rate limit","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/v0.js
3
5
  function _nullishCoalesce(lhs, rhsFn) {
4
6
  if (lhs != null) {
5
7
  return lhs;
@@ -212,3 +214,6 @@ defineProvider({
212
214
  };
213
215
  },
214
216
  });
217
+
218
+ // END UPSTREAM
219
+ })(globalThis.defineProvider);
@@ -14,9 +14,17 @@
14
14
  "V0_API_KEY",
15
15
  "V0_SCOPE"
16
16
  ],
17
+ "setupFields": [
18
+ {
19
+ "key": "V0_SCOPE",
20
+ "title": "Scope",
21
+ "type": "string",
22
+ "description": "Provider-specific account or source selection."
23
+ }
24
+ ],
17
25
  "upstream": {
18
26
  "repository": "https://github.com/steipete/CodexBar",
19
- "revision": "25bba9b7fd9ce83c33053958f7366e23b2dc8a82",
27
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
20
28
  "path": "Sources/CodexBarCore/Resources/Plugins/v0.js",
21
29
  "sha256": "5f54a9329dca35d59d76e427c34d4d9db8d3400d0b38c5fcee8899928a900019",
22
30
  "license": "MIT"
@@ -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/vercel.js
3
5
  defineProvider({
4
6
  id: "vercel",
5
7
  name: "Vercel AI Gateway",
@@ -53,3 +55,148 @@ defineProvider({
53
55
  };
54
56
  },
55
57
  });
58
+
59
+ // END UPSTREAM
60
+ })(globalThis.defineProvider);
61
+ (function() { const credits=globalThis.__usagestat_plugin; let budget;
62
+ (function(defineProvider) {
63
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/vercel-budget.js
64
+ defineProvider({
65
+ id: "vercel",
66
+ name: "Vercel AI Gateway",
67
+ endpoints: ["https://api.vercel.com"],
68
+ auth: { type: "bearer", secret: "VERCEL_ACCESS_TOKEN" },
69
+ settings: [
70
+ { key: "VERCEL_ACCESS_TOKEN", title: "Vercel access token", type: "secure" },
71
+ { key: "VERCEL_TEAM_ID", title: "Team ID", type: "plain" },
72
+ ],
73
+ capabilities: ["http-status"],
74
+ async fetchUsage(ctx) {
75
+ const team = ctx.settings.get("VERCEL_TEAM_ID")?.trim();
76
+ if (!team) throw ctx.fail.missingCredential("Set the Vercel Team ID to read your personal budget.");
77
+ if (!/^[A-Za-z0-9_-]{1,128}$/.test(team)) {
78
+ throw ctx.fail.permissionDenied("Invalid Vercel Team ID. Enter the team ID or slug without spaces.");
79
+ }
80
+ function fail() {
81
+ throw ctx.fail.parseFailure("Vercel returned an unrecognized personal budget.");
82
+ }
83
+ async function read(url) {
84
+ const response = await ctx.http.get(url);
85
+ const message = `Vercel API returned HTTP ${response.status}.`;
86
+ if (response.status === 401) throw ctx.fail.authenticationExpired(message);
87
+ if (response.status === 403) throw ctx.fail.permissionDenied(message);
88
+ if (response.status === 429) {
89
+ const delay = Number(response.headers["retry-after"] ?? 1);
90
+ throw ctx.fail.rateLimited(message, {
91
+ retryAfterSeconds: Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 10) : 1,
92
+ });
93
+ }
94
+ if (response.status >= 500) throw ctx.fail.providerUnavailable(message);
95
+ if (response.status !== 200) throw ctx.fail.apiFailure(message);
96
+ try {
97
+ return JSON.parse(response.bodyText);
98
+ } catch {
99
+ return fail();
100
+ }
101
+ }
102
+
103
+ const user = (await read("https://api.vercel.com/v2/user"))?.user;
104
+ if (typeof user?.id !== "string" || !user.id) return fail();
105
+
106
+ // At most 20 pages; the plugin deadline bounds slower teams.
107
+ const matches = [];
108
+ const cursors = new Set();
109
+ let cursor = null;
110
+ for (let page = 0; ; page += 1) {
111
+ const query =
112
+ `teamId=${encodeURIComponent(team)}&scopeType=user&limit=200` +
113
+ (cursor === null ? "" : `&cursor=${encodeURIComponent(cursor)}`);
114
+ const body = await read(`https://api.vercel.com/ai-gateway/budgets/list?${query}`);
115
+ if (!Array.isArray(body?.budgets)) return fail();
116
+ for (const budget of body.budgets) {
117
+ if (budget?.scopeType !== "user" || ![user.id, `usr_${user.id}`].includes(budget.scopeId)) continue;
118
+ if (typeof budget.active !== "boolean" || typeof budget.archived !== "boolean") return fail();
119
+ if (budget.source !== undefined && budget.source !== "default") return fail();
120
+ if (budget.active && !budget.archived) matches.push(budget);
121
+ }
122
+ const next = body.nextCursor;
123
+ if (next === undefined || next === null) break;
124
+ if (typeof next !== "string" || !next || cursors.has(next) || page >= 19) return fail();
125
+ cursors.add(next);
126
+ cursor = next;
127
+ }
128
+
129
+ const identity = {
130
+ email: typeof user.email === "string" ? user.email : null,
131
+ organization: team,
132
+ loginMethod: "Access token",
133
+ accountID: user.id,
134
+ };
135
+ const custom = matches.filter((budget) => budget.source !== "default");
136
+ const effective = custom.length > 0 ? custom : matches;
137
+ if (effective.length > 1) return fail();
138
+ const budget = effective[0];
139
+ if (!budget) {
140
+ return {
141
+ details: [{ title: "Personal budget", rows: [{ label: "No personal budget configured", value: "—" }] }],
142
+ identity,
143
+ dataConfidence: "exact",
144
+ };
145
+ }
146
+ const limit = budget.limitAmount;
147
+ const spent = budget.currentSpend;
148
+ if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) return fail();
149
+ if (typeof spent !== "number" || !Number.isFinite(spent) || spent < 0) return fail();
150
+
151
+ // Documented UTC boundaries: daily midnight, weekly Monday, monthly the 1st; `none` never resets.
152
+ const now = ctx.date.now();
153
+ const year = now.getUTCFullYear();
154
+ const month = now.getUTCMonth();
155
+ const day = now.getUTCDate();
156
+ let start = null;
157
+ let end = null;
158
+ if (budget.refreshPeriod === "daily") {
159
+ start = Date.UTC(year, month, day);
160
+ end = Date.UTC(year, month, day + 1);
161
+ } else if (budget.refreshPeriod === "weekly") {
162
+ start = Date.UTC(year, month, day - ((now.getUTCDay() + 6) % 7));
163
+ end = start + 7 * 24 * 60 * 60 * 1000;
164
+ } else if (budget.refreshPeriod === "monthly") {
165
+ start = Date.UTC(year, month, 1);
166
+ end = Date.UTC(year, month + 1, 1);
167
+ } else if (budget.refreshPeriod !== "none") {
168
+ return fail();
169
+ }
170
+ const spentText = ctx.format.usd(spent);
171
+ const limitText = ctx.format.usd(limit);
172
+ return {
173
+ primary: {
174
+ usedPercent: (spent / limit) * 100,
175
+ windowMinutes: start === null ? null : (end - start) / 60000,
176
+ resetsAt: end === null ? null : new Date(end),
177
+ resetDescription: `${spentText} / ${limitText}`,
178
+ },
179
+ details: [
180
+ {
181
+ title: "Personal budget",
182
+ rows: [
183
+ { label: "Spend", value: spentText },
184
+ { label: "Limit", value: limitText },
185
+ { label: "Remaining", value: ctx.format.usd(Math.max(0, limit - spent)) },
186
+ ],
187
+ },
188
+ ],
189
+ identity,
190
+ dataConfidence: "exact",
191
+ };
192
+ },
193
+ });
194
+
195
+ // END UPSTREAM
196
+ })(definition => { budget=globalThis.__usagestat_bundledProvider(definition,{"labels":["Personal budget","Additional","Additional"],"autoMode":"api","genericApiKey":false}); });
197
+ globalThis.__usagestat_plugin = {id:"vercel",probe(ctx) {
198
+ const selected=ctx.provider?.settings?.personalBudget;
199
+ if(selected!==undefined && typeof selected!=='boolean') throw new Error('personalBudget must be a boolean.');
200
+ return (selected===true ? budget : credits).probe(ctx);
201
+ }};
202
+ })();
@@ -11,13 +11,44 @@
11
11
  "autoMode": "api",
12
12
  "usageDashboardUrl": "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway",
13
13
  "envVars": [
14
- "AI_GATEWAY_API_KEY"
14
+ "AI_GATEWAY_API_KEY",
15
+ "VERCEL_ACCESS_TOKEN",
16
+ "VERCEL_TEAM_ID"
17
+ ],
18
+ "setupFields": [
19
+ {
20
+ "key": "personalBudget",
21
+ "title": "Read personal budget",
22
+ "type": "boolean",
23
+ "description": "Use a Vercel access token and Team ID instead of AI Gateway key credits."
24
+ },
25
+ {
26
+ "key": "VERCEL_ACCESS_TOKEN",
27
+ "title": "Vercel access token",
28
+ "type": "string",
29
+ "description": "Separate account token; the AI Gateway API key is not used for budgets."
30
+ },
31
+ {
32
+ "key": "VERCEL_TEAM_ID",
33
+ "title": "Team ID",
34
+ "type": "string",
35
+ "description": "Team ID or slug whose personal budget belongs to the token user."
36
+ }
15
37
  ],
16
38
  "upstream": {
17
39
  "repository": "https://github.com/steipete/CodexBar",
18
- "revision": "25bba9b7fd9ce83c33053958f7366e23b2dc8a82",
40
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
19
41
  "path": "Sources/CodexBarCore/Resources/Plugins/vercel.js",
20
42
  "sha256": "27b39f42709e93a2cfbf850171f198069553c99967f9b7ba6d16614862cb084a",
21
43
  "license": "MIT"
22
- }
44
+ },
45
+ "upstreamSources": [
46
+ {
47
+ "repository": "https://github.com/steipete/CodexBar",
48
+ "revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
49
+ "path": "Sources/CodexBarCore/Resources/Plugins/vercel-budget.js",
50
+ "sha256": "a65d3affe808517590edc33e4d15b813ba1fa870b4435ef18cb7c7195ab376ad",
51
+ "license": "MIT"
52
+ }
53
+ ]
23
54
  }
@@ -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 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" fill-rule="evenodd"><path d="M18.777 1.647c.28-.02.536.114.972.51 1.018.926 2.437 2.828 3.318 4.452l.34.631.482.24.11.06v3.638a5.206 5.206 0 00-5.32-1.23c-.491.166-1.021.471-2.08 1.082l-6.09 3.516c-1.057.61-1.586.916-1.975 1.259a5.208 5.208 0 00-1.493 5.572c.165.49.471 1.02 1.082 2.08l.315.543h-3.26c-.685 0-1.34-.135-1.939-.377-.169-.956-.009-1.789.469-2.335.158-.18.164-.189.13-.493a11.846 11.846 0 01-.057-1.711l.02-.444-.667-1.18C2.1 15.622 1.445 14.078 1.192 12.9c-.133-.647-.125-.934.04-1.146.1-.128.427-.261.822-.334.994-.175 3.162-.017 5.575.41l.25.043.551-.487c.915-.81 1.522-1.264 2.641-1.962 1.167-.73 2.484-1.331 3.967-1.807l.476-.152.261-.688c.937-2.471 1.896-4.293 2.58-4.9.235-.21.25-.22.422-.23z"/><path d="M12.139 18.2a1.203 1.203 0 011.642.44l1.296 2.243a1.204 1.204 0 01-2.083 1.203l-1.296-2.243a1.203 1.203 0 01.44-1.644zM18.629 14.452a1.203 1.203 0 011.642.44l1.295 2.244a1.203 1.203 0 11-2.083 1.203l-1.295-2.243a1.203 1.203 0 01.44-1.644z"/></svg>