@hashimkarim/usagestat-linux-x64-gnu 2.0.2 → 2.2.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 (181) hide show
  1. package/native-manifest.json +808 -148
  2. package/package.json +112 -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 +14 -8
  32. package/plugins/codex/plugin.json +11 -2
  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 +27 -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/googledeveloper/LICENSE +21 -0
  67. package/plugins/googledeveloper/icon.svg +3 -0
  68. package/plugins/googledeveloper/plugin.js +204 -0
  69. package/plugins/googledeveloper/plugin.json +25 -0
  70. package/plugins/helmcode/icon.svg +5 -0
  71. package/plugins/helmcode/plugin.js +120 -55
  72. package/plugins/helmcode/plugin.json +10 -2
  73. package/plugins/huggingface/plugin.js +6 -1
  74. package/plugins/huggingface/plugin.json +4 -3
  75. package/plugins/hyper/plugin.js +53 -47
  76. package/plugins/hyper/plugin.json +3 -2
  77. package/plugins/jetbrains-ai-assistant/LICENSE +22 -0
  78. package/plugins/jetbrains-ai-assistant/plugin.js +502 -16
  79. package/plugins/jetbrains-ai-assistant/plugin.json +27 -4
  80. package/plugins/kimi/plugin.js +54 -14
  81. package/plugins/kimi/plugin.json +46 -6
  82. package/plugins/langdock/LICENSE +21 -0
  83. package/plugins/langdock/icon.svg +4 -0
  84. package/plugins/langdock/plugin.js +106 -0
  85. package/plugins/langdock/plugin.json +25 -0
  86. package/plugins/linkup/LICENSE +21 -0
  87. package/plugins/linkup/icon.svg +10 -0
  88. package/plugins/linkup/plugin.js +46 -0
  89. package/plugins/linkup/plugin.json +24 -0
  90. package/plugins/lithosai/plugin.js +5 -0
  91. package/plugins/lithosai/plugin.json +2 -1
  92. package/plugins/llmman/icon.svg +4 -0
  93. package/plugins/llmman/plugin.js +5 -0
  94. package/plugins/llmman/plugin.json +9 -1
  95. package/plugins/munito/LICENSE +21 -0
  96. package/plugins/munito/icon.svg +7 -0
  97. package/plugins/munito/plugin.js +171 -0
  98. package/plugins/munito/plugin.json +24 -0
  99. package/plugins/muse/icon.svg +3 -0
  100. package/plugins/muse/plugin.js +5 -0
  101. package/plugins/muse/plugin.json +9 -1
  102. package/plugins/museai/plugin.js +10 -3
  103. package/plugins/museai/plugin.json +3 -2
  104. package/plugins/nous/plugin.js +5 -0
  105. package/plugins/nous/plugin.json +9 -1
  106. package/plugins/ollama/plugin.js +58 -0
  107. package/plugins/opencode-go/plugin.js +21 -9
  108. package/plugins/opendesign/LICENSE +21 -0
  109. package/plugins/opendesign/icon.svg +4 -0
  110. package/plugins/opendesign/plugin.js +108 -0
  111. package/plugins/opendesign/plugin.json +25 -0
  112. package/plugins/parallel/LICENSE +21 -0
  113. package/plugins/parallel/icon.svg +11 -0
  114. package/plugins/parallel/plugin.js +97 -0
  115. package/plugins/parallel/plugin.json +25 -0
  116. package/plugins/perplexity/LICENSE +22 -0
  117. package/plugins/perplexity/plugin.js +122 -0
  118. package/plugins/perplexity/plugin.json +36 -7
  119. package/plugins/qwencloud/LICENSE +21 -0
  120. package/plugins/qwencloud/plugin.js +197 -0
  121. package/plugins/qwencloud/plugin.json +17 -0
  122. package/plugins/raycast/icon.svg +4 -0
  123. package/plugins/raycast/plugin.js +5 -0
  124. package/plugins/raycast/plugin.json +9 -1
  125. package/plugins/replicate/plugin.js +5 -0
  126. package/plugins/replicate/plugin.json +2 -1
  127. package/plugins/sailresearch/LICENSE +21 -0
  128. package/plugins/sailresearch/icon.svg +4 -0
  129. package/plugins/sailresearch/plugin.js +104 -0
  130. package/plugins/sailresearch/plugin.json +24 -0
  131. package/plugins/siliconflow/LICENSE +21 -0
  132. package/plugins/siliconflow/icon.svg +3 -0
  133. package/plugins/siliconflow/plugin.js +87 -0
  134. package/plugins/siliconflow/plugin.json +32 -0
  135. package/plugins/sofya/LICENSE +21 -0
  136. package/plugins/sofya/icon.svg +3 -0
  137. package/plugins/sofya/plugin.js +86 -0
  138. package/plugins/sofya/plugin.json +24 -0
  139. package/plugins/soniox/LICENSE +21 -0
  140. package/plugins/soniox/icon.svg +3 -0
  141. package/plugins/soniox/plugin.js +103 -0
  142. package/plugins/soniox/plugin.json +24 -0
  143. package/plugins/synthetic/plugin.js +12 -0
  144. package/plugins/synthetic/plugin.json +51 -9
  145. package/plugins/tavily/LICENSE +21 -0
  146. package/plugins/tavily/icon.svg +3 -0
  147. package/plugins/tavily/plugin.js +61 -0
  148. package/plugins/tavily/plugin.json +24 -0
  149. package/plugins/tinyapi/LICENSE +21 -0
  150. package/plugins/tinyapi/icon.svg +4 -0
  151. package/plugins/tinyapi/plugin.js +73 -0
  152. package/plugins/tinyapi/plugin.json +25 -0
  153. package/plugins/traecn/LICENSE +21 -0
  154. package/plugins/traecn/icon.svg +3 -0
  155. package/plugins/traecn/plugin.js +103 -0
  156. package/plugins/traecn/plugin.json +25 -0
  157. package/plugins/tripo/LICENSE +21 -0
  158. package/plugins/tripo/icon.svg +4 -0
  159. package/plugins/tripo/plugin.js +60 -0
  160. package/plugins/tripo/plugin.json +24 -0
  161. package/plugins/typesafe/icon.svg +4 -0
  162. package/plugins/typesafe/plugin.js +85 -129
  163. package/plugins/typesafe/plugin.json +3 -2
  164. package/plugins/v0/plugin.js +5 -0
  165. package/plugins/v0/plugin.json +9 -1
  166. package/plugins/vercel/plugin.js +147 -0
  167. package/plugins/vercel/plugin.json +34 -3
  168. package/plugins/workbuddy/icon.svg +1 -1
  169. package/plugins/workbuddy/plugin.js +110 -19
  170. package/plugins/workbuddy/plugin.json +32 -2
  171. package/plugins/xai/plugin.js +9 -0
  172. package/plugins/xapi/LICENSE +21 -0
  173. package/plugins/xapi/icon.svg +1 -0
  174. package/plugins/xapi/plugin.js +90 -0
  175. package/plugins/xapi/plugin.json +25 -0
  176. package/plugins/xkiro/icon.svg +1 -0
  177. package/plugins/xkiro/plugin.js +5 -0
  178. package/plugins/xkiro/plugin.json +2 -1
  179. package/usagestat +0 -0
  180. package/usagestatd +0 -0
  181. package/plugins/cursor-nightly/plugin.js +0 -1416
@@ -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];
@@ -22,16 +24,17 @@ function _optionalChain(ops) {
22
24
  return value;
23
25
  }
24
26
 
25
- const billingURL = "https://console.typesafe.ai/settings/billing";
26
- const billingOrigin = "https://console.typesafe.ai";
27
- const actionCacheKey = "typesafe.billing.next-action";
28
- const actionCacheTTL = 12 * 60 * 60;
29
- const maximumChunkCount = 60;
27
+ const billingOverviewURL = "https://console.typesafe.ai/api/billing/overview";
28
+ const overviewFailure =
29
+ "TypeSafe billing overview could not be read. Open https://console.typesafe.ai/billing to check access; if the page opens, report the integration failure.";
30
+ // Cloudflare blocks the Linux default UA (it contains "curl/"), so send a browser-style one.
31
+ const userAgent =
32
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36";
30
33
 
31
34
  defineProvider({
32
35
  id: "typesafe",
33
36
  name: "TypeSafe",
34
- endpoints: [billingOrigin],
37
+ endpoints: ["https://console.typesafe.ai"],
35
38
  settings: [],
36
39
  capabilities: ["browser-cookies", "http-status"],
37
40
  cookieDomains: ["console.typesafe.ai", "typesafe.ai"],
@@ -46,9 +49,27 @@ defineProvider({
46
49
  const value = raw && /^\d+(?:\.\d+)?$/.test(raw.trim()) ? Number(raw) : 1;
47
50
  return Number.isFinite(value) ? Math.min(10, value) : 1;
48
51
  };
52
+ // A redirect only means the session is gone when it targets a login/SSO route. The billing overview
53
+ // itself may redirect for benign route moves (for example /settings/billing -> /billing).
54
+ const loginRedirect = (location) => {
55
+ if (typeof location !== "string" || !location.trim()) return false;
56
+ const target = location.trim();
57
+ const loginPath = /(?:^|[/?#])(?:login|sign-?in|signin|auth|oauth|sso)(?:[/?#]|$)/i;
58
+ if (!/^(?:https?:)?\/\//i.test(target)) return loginPath.test(target);
59
+ const hostMatch = /^(?:https?:)?\/\/([^/?#]+)/i.exec(target);
60
+ const hostname = hostMatch ? hostMatch[1].toLowerCase().replace(/:\d+$/, "") : "";
61
+ const knownHost = hostname === "typesafe.ai" || hostname.endsWith(".typesafe.ai");
62
+ return !knownHost || loginPath.test(target);
63
+ };
64
+ // The billing overview API refuses requests that do not look same-origin, even with a valid session.
49
65
  const validate = (response) => {
50
- if (response.status === 401 || response.status === 403 || (response.status >= 300 && response.status < 400))
51
- expired();
66
+ if (response.status === 403 && /cross-origin request refused/i.test(response.bodyText))
67
+ throw ctx.fail.apiFailure(overviewFailure);
68
+ if (response.status === 401 || response.status === 403) expired();
69
+ if (response.status >= 300 && response.status < 400) {
70
+ if (loginRedirect(response.headers["location"])) expired();
71
+ throw ctx.fail.apiFailure(overviewFailure);
72
+ }
52
73
  if (response.status === 429)
53
74
  throw ctx.fail.rateLimited("TypeSafe rate limit reached.", { retryAfterSeconds: retryAfter(response) });
54
75
  if (response.status === 408 || response.status >= 500)
@@ -58,106 +79,40 @@ defineProvider({
58
79
  if (response.status < 200 || response.status >= 300)
59
80
  throw ctx.fail.apiFailure(`TypeSafe returned HTTP ${response.status}.`);
60
81
  };
61
- // Login pages can also arrive directly with HTTP 200, without an HTTP redirect.
62
- const isLoginLanding = (body) => /\\?"\(auth\)\\?",\{\\?"children\\?":\[\\?"login\\?"/.test(body);
63
- const validateBody = (response) => {
64
- validate(response);
65
- if (isLoginLanding(response.bodyText)) expired();
66
- };
67
- const actionIDPattern = /"([0-9a-f]{40,})"[^)]{0,150}"getBillingOverviewResult"/i;
68
- const discoverActionID = async (cookie) => {
69
- const page = await ctx.http.get(billingURL, {
70
- headers: { Cookie: cookie, Accept: "text/html" },
71
- timeoutSeconds: 6,
72
- });
73
- validateBody(page);
74
- const scriptPattern = /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi;
75
- const chunkURLs = [];
76
- let match;
77
- while ((match = scriptPattern.exec(page.bodyText)) && chunkURLs.length < maximumChunkCount) {
78
- const source = match[1];
79
- const normalized = source.startsWith("/") ? `${billingOrigin}${source}` : source;
80
- if (!normalized.startsWith(`${billingOrigin}/`) || !/\.js(?:$|\?)/i.test(normalized)) continue;
81
- if (!chunkURLs.includes(normalized)) chunkURLs.push(normalized);
82
- }
83
- let discoveryFailure;
84
- for (const url of chunkURLs) {
85
- try {
86
- const chunk = await ctx.http.get(url, { timeoutSeconds: 2 });
87
- if (chunk.status === 429 || chunk.status === 408 || chunk.status >= 500) validate(chunk);
88
- if (chunk.status < 200 || chunk.status >= 300) continue;
89
- const action = _optionalChain([
90
- actionIDPattern,
91
- "access",
92
- (_) => _.exec,
93
- "call",
94
- (_2) => _2(chunk.bodyText),
95
- "optionalAccess",
96
- (_3) => _3[1],
97
- ]);
98
- if (action) return action;
99
- } catch (error) {
100
- discoveryFailure ??= error;
101
- }
102
- }
103
- if (discoveryFailure !== undefined) throw discoveryFailure;
104
- throw ctx.fail.parseFailure("TypeSafe billing page format changed: action id not found.");
105
- };
106
82
  const cookie = await ctx.browser.cookieHeader("typesafe.ai");
107
- let actionID = ctx.cache.get(actionCacheKey);
108
- if (!actionID || !/^[0-9a-f]{40,}$/i.test(actionID)) {
109
- actionID = await discoverActionID(cookie);
110
- ctx.cache.set(actionCacheKey, actionID, actionCacheTTL);
111
- }
112
- const post = async (id) =>
113
- ctx.http.post(billingURL, {
114
- headers: {
115
- Cookie: cookie,
116
- Origin: billingOrigin,
117
- "Next-Action": id,
118
- Accept: "text/x-component",
119
- "Content-Type": "application/json",
120
- },
121
- body: [],
122
- timeoutSeconds: 6,
123
- });
124
- let response = await post(actionID);
125
- if (response.status === 404 && response.headers["x-nextjs-action-not-found"] === "1") {
126
- ctx.cache.set(actionCacheKey, "", 1);
127
- actionID = await discoverActionID(cookie);
128
- ctx.cache.set(actionCacheKey, actionID, actionCacheTTL);
129
- response = await post(actionID);
83
+ const response = await ctx.http.get(billingOverviewURL, {
84
+ headers: {
85
+ Cookie: cookie,
86
+ Accept: "application/json",
87
+ Origin: "https://console.typesafe.ai",
88
+ Referer: "https://console.typesafe.ai/billing",
89
+ "Sec-Fetch-Site": "same-origin",
90
+ "Sec-Fetch-Mode": "cors",
91
+ "Sec-Fetch-Dest": "empty",
92
+ "User-Agent": userAgent,
93
+ },
94
+ timeoutSeconds: 10,
95
+ });
96
+ validate(response);
97
+ // A signed-out console can answer with an HTML login page instead of JSON.
98
+ const body = response.bodyText.trim();
99
+ if (body.startsWith("<")) {
100
+ if (/(?:login|sign[-\s]?in|\(auth\))/i.test(body)) expired();
101
+ throw ctx.fail.apiFailure(overviewFailure);
130
102
  }
131
- validateBody(response);
132
-
133
- let result;
134
- for (const line of response.bodyText.split(/\r?\n/)) {
135
- const separator = line.indexOf(":");
136
- if (separator < 1) continue;
137
- let candidate;
138
- try {
139
- candidate = JSON.parse(line.slice(separator + 1));
140
- } catch (error) {
141
- void error;
142
- continue;
143
- }
144
- const object = record(candidate);
145
- if (object && Object.prototype.hasOwnProperty.call(object, "ok")) {
146
- result = object;
147
- break;
148
- }
103
+ let payload;
104
+ try {
105
+ payload = JSON.parse(body);
106
+ } catch (error) {
107
+ void error;
108
+ throw ctx.fail.parseFailure(overviewFailure);
149
109
  }
150
- if (!result) throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing result.");
151
- if (result.ok !== true) throw ctx.fail.apiFailure("TypeSafe billing request failed.");
152
- const billing = record(
153
- _optionalChain([record, "call", (_4) => _4(result.data), "optionalAccess", (_5) => _5.billing]),
154
- );
155
- if (!billing) throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing billing.");
110
+ const billing = record(_optionalChain([record, "call", (_) => _(payload), "optionalAccess", (_2) => _2.billing]));
111
+ if (!billing) throw ctx.fail.parseFailure(overviewFailure);
156
112
  const number = (value) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
157
113
  const spent = number(billing.spent);
158
114
  const balance = number(billing.balance);
159
- if (spent === undefined || balance === undefined)
160
- throw ctx.fail.parseFailure("TypeSafe billing response format changed: missing billing numbers.");
115
+ if (spent === undefined || balance === undefined) throw ctx.fail.parseFailure(overviewFailure);
161
116
  const cycleLabel =
162
117
  typeof billing.cycleLabel === "string" && billing.cycleLabel.trim() ? billing.cycleLabel.trim() : undefined;
163
118
  const plan = typeof billing.plan === "string" && billing.plan.trim() ? billing.plan.trim() : undefined;
@@ -167,46 +122,44 @@ defineProvider({
167
122
  : _optionalChain([
168
123
  plan,
169
124
  "optionalAccess",
170
- (_6) => _6.split,
125
+ (_3) => _3.split,
171
126
  "call",
172
- (_7) => _7(/[_-]+/),
127
+ (_4) => _4(/[_-]+/),
173
128
  "access",
174
- (_8) => _8.filter,
129
+ (_5) => _5.filter,
175
130
  "call",
176
- (_9) => _9(Boolean),
131
+ (_6) => _6(Boolean),
177
132
  "access",
178
- (_10) => _10.map,
133
+ (_7) => _7.map,
179
134
  "call",
180
- (_11) => _11((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()),
135
+ (_8) => _8((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()),
181
136
  "access",
182
- (_12) => _12.join,
137
+ (_9) => _9.join,
183
138
  "call",
184
- (_13) => _13(" "),
139
+ (_10) => _10(" "),
185
140
  ]);
186
141
  const rows = [{ label: cycleLabel ? `Spent (${cycleLabel})` : "Spent", value: ctx.format.usd(spent) }];
187
142
  if (planLabel) rows.push({ label: "Plan", value: planLabel });
188
143
  const availableRows = 24 - rows.length;
189
144
  const creditRows = [];
190
145
  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
- }
146
+ for (const item of Array.isArray(billing.credits) ? billing.credits : []) {
147
+ const credit = record(item);
148
+ const amount = number(_optionalChain([credit, "optionalAccess", (_11) => _11.amount]));
149
+ const remaining = number(_optionalChain([credit, "optionalAccess", (_12) => _12.remaining]));
150
+ const expiresAt =
151
+ typeof _optionalChain([credit, "optionalAccess", (_13) => _13.expiresAt]) === "string"
152
+ ? credit.expiresAt
153
+ : undefined;
154
+ if (amount === undefined || remaining === undefined || remaining <= 0 || !expiresAt) continue;
155
+ const expires = Date.parse(expiresAt);
156
+ if (!Number.isFinite(expires)) continue;
157
+ creditCount++;
158
+ if (creditRows.length >= availableRows) continue;
159
+ creditRows.push({
160
+ label: "Credit",
161
+ value: `${ctx.format.number(remaining)} of ${ctx.format.number(amount)}, expires ${ctx.format.monthDay(expires)}`,
162
+ });
210
163
  }
211
164
  if (creditCount > availableRows) {
212
165
  const shown = availableRows - 1;
@@ -223,3 +176,6 @@ defineProvider({
223
176
  };
224
177
  },
225
178
  });
179
+
180
+ // END UPSTREAM
181
+ })(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": "6ba06fe9ec1522dd5c59e16810115bce28aa148f",
20
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
20
21
  "path": "Sources/CodexBarCore/Resources/Plugins/typesafe.js",
21
- "sha256": "eb3941346a966b3f5f9e0010f17edaeed7dfb6c8b0331319a86f26da10d51980",
22
+ "sha256": "da91db172678133393d38836fcb4028342edade8aa7994d2bc5e95290498192f",
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": "6ba06fe9ec1522dd5c59e16810115bce28aa148f",
27
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
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": "6ba06fe9ec1522dd5c59e16810115bce28aa148f",
40
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
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": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
49
+ "path": "Sources/CodexBarCore/Resources/Plugins/vercel-budget.js",
50
+ "sha256": "a65d3affe808517590edc33e4d15b813ba1fa870b4435ef18cb7c7195ab376ad",
51
+ "license": "MIT"
52
+ }
53
+ ]
23
54
  }
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M2 5h3.2l2.3 9.2L10.2 5h3.6l2.7 9.2L18.8 5H22l-4 14h-3.2L12 9.6 9.2 19H6z"/></svg>
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>