@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
@@ -0,0 +1,244 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Credits","Additional","Additional"],"defaults":{},"autoMode":"web","balanceOnly":true};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/claudeplatform.js
5
+ function _nullishCoalesce(lhs, rhsFn) {
6
+ if (lhs != null) {
7
+ return lhs;
8
+ } else {
9
+ return rhsFn();
10
+ }
11
+ }
12
+ defineProvider({
13
+ id: "claudeplatform",
14
+ name: "Claude Platform",
15
+ settings: [],
16
+ endpoints: ["https://platform.claude.com"],
17
+ capabilities: ["browser-cookies", "http-status"],
18
+ cookieDomains: ["platform.claude.com"],
19
+ cookiePolicy: {
20
+ selection: "request-url",
21
+ cache: "nonpersistent",
22
+ imports: "access-gated",
23
+ requiredCookies: ["sessionKey"],
24
+ },
25
+ async fetchUsage(ctx) {
26
+ const domain = "platform.claude.com";
27
+ if (ctx.browser.availability(domain) === "off")
28
+ throw ctx.fail.missingCredential("Claude Platform cookies are disabled.");
29
+
30
+ const invalid = (field) => {
31
+ throw ctx.fail.parseFailure(
32
+ `Could not parse Claude Console ${field}. The Console response format may have changed; update CodexBar or report this.`,
33
+ );
34
+ };
35
+ const object = (value, field) =>
36
+ value !== null && typeof value === "object" && !Array.isArray(value) ? value : invalid(field);
37
+ const minorUnits = (value, field) =>
38
+ typeof value === "number" && Number.isSafeInteger(value) ? value : invalid(field);
39
+ const text = (value) => (typeof value === "string" && value.trim() ? value.trim() : undefined);
40
+ // A malformed date means an unknown expiry, never a failed refresh.
41
+ const timestamp = (value) => {
42
+ const raw = text(value);
43
+ if (!raw || !/^\d{4}-\d{2}-\d{2}T/.test(raw) || !Number.isFinite(Date.parse(raw))) return undefined;
44
+ try {
45
+ const date = ctx.date.iso(raw);
46
+ return Number.isFinite(date.getTime()) ? date : undefined;
47
+ } catch (error) {
48
+ void error;
49
+ return undefined;
50
+ }
51
+ };
52
+ const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
53
+ const now = ctx.date.now();
54
+ // Console grants expire at UTC midnight; show that UTC calendar date instead of shifting it into the local zone.
55
+ const expiry = (date) => {
56
+ const [year, month, day] = date.toISOString().slice(0, 10).split("-").map(Number);
57
+ const label = `${months[month - 1]} ${day}`;
58
+ return `Expires ${year === now.getUTCFullYear() ? label : `${label}, ${year}`} (UTC)`;
59
+ };
60
+ const sessionRejected = ctx.fail.authenticationExpired("Claude Console session expired.");
61
+ const billingDenied = ctx.fail.permissionDenied(
62
+ "Claude Console billing access was denied. Ask the organization owner for billing access.",
63
+ );
64
+ let rejected = false;
65
+ for await (const session of ctx.browser.sessions(domain)) {
66
+ const request = async (path, forbidden) => {
67
+ const response = await ctx.http.get(`https://${domain}${path}`, {
68
+ cookieSession: session.id,
69
+ headers: { Accept: "application/json" },
70
+ });
71
+ if (response.status === 401) throw sessionRejected;
72
+ if (response.status === 403) throw forbidden;
73
+ if (response.status === 429) {
74
+ throw ctx.fail.rateLimited("Claude Console requests are rate limited. Try again in a few minutes.");
75
+ }
76
+ if (response.status >= 500) {
77
+ throw ctx.fail.providerUnavailable("Claude Console is unavailable. Try again later.");
78
+ }
79
+ if (response.status !== 200) {
80
+ throw ctx.fail.apiFailure(`Claude Console returned HTTP ${response.status}. Try again later or report this.`);
81
+ }
82
+ try {
83
+ return JSON.parse(response.bodyText);
84
+ } catch (error) {
85
+ void error;
86
+ return invalid("JSON response");
87
+ }
88
+ };
89
+ try {
90
+ // The organizations list doubles as the session check; a rejected cookie answers 403.
91
+ const organizations = await request("/api/organizations", sessionRejected);
92
+ if (!Array.isArray(organizations)) return invalid("organizations");
93
+ // Claude.ai chat organizations share this list; only API organizations own Console billing.
94
+ const candidates = organizations
95
+ .map((value) => object(value, "organization"))
96
+ .filter((value) => Array.isArray(value.capabilities) && value.capabilities.some((item) => item === "api"))
97
+ .map((organization) => {
98
+ const id = text(organization.uuid);
99
+ if (!id || !/^[A-Za-z0-9-]{1,64}$/.test(id)) return invalid("organization ID");
100
+ return { id, name: text(organization.name) };
101
+ });
102
+ if (candidates.length === 0)
103
+ throw ctx.fail.permissionDenied(
104
+ "This Claude account has no Claude Console API organization. Create one at platform.claude.com.",
105
+ );
106
+
107
+ // Try API organizations in listed order; the first one whose billing this member can read wins.
108
+ let selected;
109
+ for (const candidate of candidates) {
110
+ try {
111
+ const credits = object(
112
+ await request(`/api/organizations/${candidate.id}/prepaid/credits`, billingDenied),
113
+ "credit balance",
114
+ );
115
+ selected = { ...candidate, credits };
116
+ break;
117
+ } catch (error) {
118
+ if (error !== billingDenied) throw error;
119
+ }
120
+ }
121
+ if (!selected) throw billingDenied;
122
+ const credits = selected.credits;
123
+ const currency = _nullishCoalesce(text(credits.currency), () => "USD");
124
+ if (!/^[A-Z]{3}$/.test(currency)) return invalid("currency");
125
+ let exponent = 2;
126
+ if (credits.balance !== undefined && credits.balance !== null) {
127
+ const reported = object(
128
+ _nullishCoalesce(object(credits.balance, "balance").credits, () => ({})),
129
+ "balance credits",
130
+ ).exponent;
131
+ if (reported !== undefined && reported !== null) {
132
+ if (typeof reported !== "number" || !Number.isInteger(reported) || reported < 0 || reported > 4)
133
+ return invalid("currency exponent");
134
+ exponent = reported;
135
+ }
136
+ }
137
+ const scale = 10 ** exponent;
138
+ const money = (minor) => ctx.format.currency(minor / scale, currency);
139
+ const balance = minorUnits(credits.amount, "credit balance") / scale;
140
+
141
+ const grants = [];
142
+ for (const [field, purchased] of [
143
+ ["promo_tranches", false],
144
+ ["tranches", true],
145
+ ]) {
146
+ const list = credits[field];
147
+ if (list === undefined || list === null) continue;
148
+ if (!Array.isArray(list)) return invalid(field);
149
+ for (const raw of list) {
150
+ const tranche = object(raw, "credit grant");
151
+ const granted = minorUnits(tranche.granted_amount_minor_units, "granted credit");
152
+ const remaining = minorUnits(tranche.remaining_amount_minor_units, "remaining credit");
153
+ const expiresAt = timestamp(tranche.expires_at);
154
+ if (granted <= 0 || (expiresAt && expiresAt.getTime() <= now.getTime())) continue;
155
+ grants.push({
156
+ label: _nullishCoalesce(text(tranche.name), () => (purchased ? "Purchased credit" : "API credit")),
157
+ program: text(tranche.program_id),
158
+ purchased,
159
+ remaining: Math.max(0, remaining),
160
+ granted,
161
+ expiresAt,
162
+ });
163
+ }
164
+ }
165
+ grants.sort((lhs, rhs) => {
166
+ if (!lhs.expiresAt && !rhs.expiresAt) return 0;
167
+ if (!lhs.expiresAt) return 1;
168
+ if (!rhs.expiresAt) return -1;
169
+ return lhs.expiresAt.getTime() - rhs.expiresAt.getTime();
170
+ });
171
+ // The soonest-expiring grant that still has credit is the one about to be lost. An exhausted subscription
172
+ // grant is not a lane: the menu bar falls back to purchased credit and the grant moves to the detail list.
173
+ const subscription = grants.find((grant) => grant.program === "subscription_api_credit" && grant.remaining > 0);
174
+ const purchasedGrants = grants.filter((grant) => grant.purchased);
175
+ const purchased = _nullishCoalesce(
176
+ purchasedGrants.find((grant) => grant.remaining > 0),
177
+ () => purchasedGrants[0],
178
+ );
179
+ const amounts = (grant) => `${money(grant.remaining)} of ${money(grant.granted)} left`;
180
+ const [primary, secondary] = [subscription, purchased].map((grant) =>
181
+ grant
182
+ ? {
183
+ usedPercent: ctx.pct(grant.granted - grant.remaining, grant.granted),
184
+ resetDescription: grant.expiresAt ? `${amounts(grant)} · ${expiry(grant.expiresAt)}` : amounts(grant),
185
+ }
186
+ : undefined,
187
+ );
188
+
189
+ const details = [];
190
+ const otherGrants = grants.filter((grant) => grant !== subscription && grant !== purchased);
191
+ if (otherGrants.length > 0) {
192
+ details.push({
193
+ title: "Other credit grants",
194
+ rows: otherGrants.map((grant) => ({
195
+ label: grant.label,
196
+ value: amounts(grant),
197
+ secondaryValue: grant.expiresAt ? expiry(grant.expiresAt) : null,
198
+ progress: (grant.granted - Math.min(grant.remaining, grant.granted)) / grant.granted,
199
+ })),
200
+ });
201
+ }
202
+
203
+ // Spend is optional: a broken report must not discard an authenticated balance.
204
+ let spendRow;
205
+ try {
206
+ const spend = object(
207
+ await request(`/api/organizations/${selected.id}/current_spend`, billingDenied),
208
+ "spend",
209
+ );
210
+ const amount = minorUnits(spend.amount, "spend");
211
+ if (amount < 0) return invalid("spend");
212
+ spendRow = { label: "This month (UTC)", value: money(amount) };
213
+ } catch (error) {
214
+ if (error.transportClass === "cancelled") throw error;
215
+ spendRow = { label: "Spend", value: "Unavailable" };
216
+ }
217
+ details.push({ title: "Billing", rows: [spendRow] });
218
+
219
+ return {
220
+ primary,
221
+ secondary,
222
+ cost: { used: balance, currency, period: "Prepaid credits", balance },
223
+ details,
224
+ identity: { organization: selected.name, loginMethod: "Console session" },
225
+ dataConfidence: "exact",
226
+ };
227
+ } catch (error) {
228
+ if (error !== sessionRejected) throw error;
229
+ ctx.browser.rejectCookie(domain, session);
230
+ rejected = true;
231
+ }
232
+ }
233
+ if (rejected)
234
+ throw ctx.fail.authenticationExpired(
235
+ "Claude Console session expired. Sign in at platform.claude.com or paste a fresh Cookie header.",
236
+ );
237
+ throw ctx.fail.missingCredential(
238
+ `Sign in at platform.claude.com. Supported browsers: ${ctx.browser.supportedBrowsers}. Or paste its Cookie header.`,
239
+ );
240
+ },
241
+ });
242
+
243
+ // END UPSTREAM
244
+ })(globalThis.defineProvider);
@@ -0,0 +1,25 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "claudeplatform",
4
+ "name": "Claude Platform",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "web"
10
+ ],
11
+ "autoMode": "web",
12
+ "usageDashboardUrl": "https://platform.claude.com/settings/billing",
13
+ "webUrl": "https://platform.claude.com",
14
+ "envVars": [
15
+ "CLAUDEPLATFORM_COOKIE"
16
+ ],
17
+ "setupFields": [],
18
+ "upstream": {
19
+ "repository": "https://github.com/steipete/CodexBar",
20
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
21
+ "path": "Sources/CodexBarCore/Resources/Plugins/claudeplatform.js",
22
+ "sha256": "d3c61855621fd4e5b5b753bdd223524e0ee7f4b55cfd63f24f23516ac33027e8",
23
+ "license": "MIT"
24
+ }
25
+ }
@@ -375,12 +375,18 @@
375
375
  function formatCodexPlan(ctx, planType) {
376
376
  const rawPlan = typeof planType === "string" ? planType.trim() : ""
377
377
  if (!rawPlan) return null
378
- if (rawPlan.toLowerCase() === "prolite") return "Pro 5x"
379
- if (rawPlan.toLowerCase() === "pro") return "Pro 20x"
378
+ if (rawPlan.toLowerCase() === "prolite") return "Pro 100"
379
+ if (rawPlan.toLowerCase() === "pro") return "Pro 200"
380
+ if (rawPlan.toLowerCase() === "promax") return "Pro 500"
380
381
  if (rawPlan.toLowerCase() === "self_serve_business_prolite") return "Business Premium"
381
382
  return ctx.fmt.planLabel(rawPlan) || null
382
383
  }
383
384
 
385
+ function reportedPeriod(window, fallback) {
386
+ const seconds = window && window.limit_window_seconds
387
+ return typeof seconds === "number" && Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : fallback
388
+ }
389
+
384
390
  function getResetsAtIso(ctx, nowSec, window) {
385
391
  if (!window) return null
386
392
  if (typeof window.reset_at === "number") {
@@ -779,12 +785,12 @@
779
785
 
780
786
  if (headerPrimary !== null) {
781
787
  lines.push(ctx.line.progress({
782
- label: "Session",
788
+ label: reportedPeriod(primaryWindow, PERIOD_SESSION_MS) === 30*86400000 ? "Monthly" : "Session",
783
789
  used: headerPrimary,
784
790
  limit: 100,
785
791
  format: { kind: "percent" },
786
792
  resetsAt: getResetsAtIso(ctx, nowSec, primaryWindow),
787
- periodDurationMs: PERIOD_SESSION_MS
793
+ periodDurationMs: reportedPeriod(primaryWindow, PERIOD_SESSION_MS)
788
794
  }))
789
795
  }
790
796
  if (headerSecondary !== null) {
@@ -794,19 +800,19 @@
794
800
  limit: 100,
795
801
  format: { kind: "percent" },
796
802
  resetsAt: getResetsAtIso(ctx, nowSec, secondaryWindow),
797
- periodDurationMs: PERIOD_WEEKLY_MS
803
+ periodDurationMs: reportedPeriod(secondaryWindow, PERIOD_WEEKLY_MS)
798
804
  }))
799
805
  }
800
806
 
801
807
  if (lines.length === 0 && data.rate_limit) {
802
808
  if (data.rate_limit.primary_window && typeof data.rate_limit.primary_window.used_percent === "number") {
803
809
  lines.push(ctx.line.progress({
804
- label: "Session",
810
+ label: reportedPeriod(primaryWindow, PERIOD_SESSION_MS) === 30*86400000 ? "Monthly" : "Session",
805
811
  used: data.rate_limit.primary_window.used_percent,
806
812
  limit: 100,
807
813
  format: { kind: "percent" },
808
814
  resetsAt: getResetsAtIso(ctx, nowSec, primaryWindow),
809
- periodDurationMs: PERIOD_SESSION_MS
815
+ periodDurationMs: reportedPeriod(primaryWindow, PERIOD_SESSION_MS)
810
816
  }))
811
817
  }
812
818
  if (data.rate_limit.secondary_window && typeof data.rate_limit.secondary_window.used_percent === "number") {
@@ -816,7 +822,7 @@
816
822
  limit: 100,
817
823
  format: { kind: "percent" },
818
824
  resetsAt: getResetsAtIso(ctx, nowSec, secondaryWindow),
819
- periodDurationMs: PERIOD_WEEKLY_MS
825
+ periodDurationMs: reportedPeriod(secondaryWindow, PERIOD_WEEKLY_MS)
820
826
  }))
821
827
  }
822
828
  }
@@ -13,10 +13,14 @@
13
13
  },
14
14
  {
15
15
  "label": "Usage dashboard",
16
- "url": "https://chatgpt.com/codex/cloud/settings/analytics#usage"
16
+ "url": "https://chatgpt.com/settings/usage?tab=overview"
17
17
  }
18
18
  ],
19
- "supportedModes": ["oauth", "local", "web"],
19
+ "supportedModes": [
20
+ "oauth",
21
+ "local",
22
+ "web"
23
+ ],
20
24
  "autoMode": "oauth",
21
25
  "webUrl": "https://chatgpt.com",
22
26
  "lines": [
@@ -77,6 +81,11 @@
77
81
  "type": "barChart",
78
82
  "label": "Usage Trend",
79
83
  "scope": "detail"
84
+ },
85
+ {
86
+ "type": "progress",
87
+ "label": "Monthly",
88
+ "scope": "overview"
80
89
  }
81
90
  ]
82
91
  }
@@ -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" viewBox="0 0 24 24"><path fill="currentColor" d="M16 4a9 9 0 1 0 0 16l-2-3a5.5 5.5 0 1 1 0-10Z"/><path fill="currentColor" d="m19 3 1.2 3.8L24 8l-3.8 1.2L19 13l-1.2-3.8L14 8l3.8-1.2Z"/></svg>
@@ -0,0 +1,134 @@
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Input tokens","Output tokens","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/cosmic.js
5
+ function _optionalChain(ops) {
6
+ let lastAccessLHS = undefined;
7
+ let value = ops[0];
8
+ let i = 1;
9
+ while (i < ops.length) {
10
+ const op = ops[i];
11
+ const fn = ops[i + 1];
12
+ i += 2;
13
+ if ((op === "optionalAccess" || op === "optionalCall") && value == null) {
14
+ return undefined;
15
+ }
16
+ if (op === "access" || op === "optionalAccess") {
17
+ lastAccessLHS = value;
18
+ value = fn(value);
19
+ } else if (op === "call" || op === "optionalCall") {
20
+ value = fn((...args) => value.call(lastAccessLHS, ...args));
21
+ lastAccessLHS = undefined;
22
+ }
23
+ }
24
+ return value;
25
+ }
26
+ defineProvider({
27
+ id: "cosmic",
28
+ name: "Cosmic AI",
29
+ endpoints: ["https://dapi.cosmicjs.com"],
30
+ auth: { type: "bearer", secret: "COSMIC_TOKEN" },
31
+ settings: [
32
+ { key: "COSMIC_TOKEN", title: "Personal Access Token", type: "secure" },
33
+ { key: "COSMIC_PROJECT_ID", title: "Project ID", type: "plain" },
34
+ ],
35
+ capabilities: ["http-status"],
36
+ async fetchUsage(ctx) {
37
+ const project = _optionalChain([
38
+ ctx,
39
+ "access",
40
+ (_) => _.settings,
41
+ "access",
42
+ (_2) => _2.get,
43
+ "call",
44
+ (_3) => _3("COSMIC_PROJECT_ID"),
45
+ "optionalAccess",
46
+ (_4) => _4.trim,
47
+ "call",
48
+ (_5) => _5(),
49
+ ]);
50
+ if (!project) throw ctx.fail.missingCredential("Set a Cosmic Project ID in Settings or COSMIC_PROJECT_ID.");
51
+ const response = await ctx.http.get(
52
+ `https://dapi.cosmicjs.com/v3/projects/usage?project_id=${encodeURIComponent(project)}`,
53
+ { headers: { Origin: "https://app.cosmicjs.com" } },
54
+ );
55
+ const message = `Cosmic AI returned HTTP ${response.status}.`;
56
+ if (response.status === 401) throw ctx.fail.authenticationExpired(message);
57
+ if (response.status === 403) throw ctx.fail.permissionDenied(message);
58
+ if (response.status === 429) throw ctx.fail.rateLimited(message);
59
+ if (response.status >= 500) throw ctx.fail.providerUnavailable(message);
60
+ if (response.status !== 200) throw ctx.fail.apiFailure(message);
61
+ const fail = () => {
62
+ throw ctx.fail.parseFailure("Cosmic AI returned an unrecognized project AI-token usage response.");
63
+ };
64
+ const record = (value) => (value && typeof value === "object" && !Array.isArray(value) ? value : undefined);
65
+ let decoded;
66
+ try {
67
+ decoded = JSON.parse(response.bodyText);
68
+ } catch (error) {
69
+ void error;
70
+ return fail();
71
+ }
72
+ const root = record(decoded);
73
+ const usage = record(
74
+ _optionalChain([
75
+ record,
76
+ "call",
77
+ (_6) => _6(_optionalChain([root, "optionalAccess", (_7) => _7.usage])),
78
+ "optionalAccess",
79
+ (_8) => _8.ai,
80
+ ]),
81
+ );
82
+ const allowance = record(
83
+ _optionalChain([
84
+ record,
85
+ "call",
86
+ (_9) => _9(_optionalChain([root, "optionalAccess", (_10) => _10.plan_info])),
87
+ "optionalAccess",
88
+ (_11) => _11.ai_tokens,
89
+ ]),
90
+ );
91
+ const count = (value) => {
92
+ if (value === undefined || value === null) return undefined;
93
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : fail();
94
+ };
95
+ const limit = (value) => {
96
+ if (typeof value !== "string") return count(value);
97
+ const match = /^(\d+(?:\.\d+)?)\s*([km]?)$/i.exec(value.trim());
98
+ if (!match) return undefined;
99
+ return count(Number(match[1]) * (match[2].toLowerCase() === "m" ? 1000000 : match[2] ? 1000 : 1));
100
+ };
101
+ const details = [];
102
+ const window = (name, rawUsed, rawLimit) => {
103
+ const used = count(rawUsed);
104
+ const total = limit(rawLimit);
105
+ const rows = [];
106
+ if (used !== undefined) rows.push({ label: "Used", value: ctx.format.number(used) });
107
+ if (total !== undefined) rows.push({ label: "Allowance", value: ctx.format.number(total) });
108
+ else if (typeof rawLimit === "string" && rawLimit.trim()) {
109
+ rows.push({ label: "Allowance", value: /^unlimited$/i.test(rawLimit.trim()) ? "Unlimited" : "Not reported" });
110
+ }
111
+ if (used !== undefined && total !== undefined) {
112
+ rows.push({ label: "Remaining", value: ctx.format.number(Math.max(0, total - used)) });
113
+ if (used > total) rows.push({ label: "Above allowance", value: ctx.format.number(used - total) });
114
+ }
115
+ if (rows.length) details.push({ title: name, rows });
116
+ return used !== undefined && total !== undefined && total > 0 ? { usedPercent: ctx.pct(used, total) } : undefined;
117
+ };
118
+ const primary = window(
119
+ "Input tokens",
120
+ _optionalChain([usage, "optionalAccess", (_12) => _12.input_tokens]),
121
+ _optionalChain([allowance, "optionalAccess", (_13) => _13.max_input]),
122
+ );
123
+ const secondary = window(
124
+ "Output tokens",
125
+ _optionalChain([usage, "optionalAccess", (_14) => _14.output_tokens]),
126
+ _optionalChain([allowance, "optionalAccess", (_15) => _15.max_output]),
127
+ );
128
+ if (!details.length) return fail();
129
+ return { primary, secondary, details, dataConfidence: "exact" };
130
+ },
131
+ });
132
+
133
+ // END UPSTREAM
134
+ })(globalThis.defineProvider);
@@ -0,0 +1,32 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "cosmic",
4
+ "name": "Cosmic AI",
5
+ "version": "0.1.0",
6
+ "entry": "plugin.js",
7
+ "enabledByDefault": false,
8
+ "supportedModes": [
9
+ "api"
10
+ ],
11
+ "autoMode": "api",
12
+ "usageDashboardUrl": "https://app.cosmicjs.com",
13
+ "envVars": [
14
+ "COSMIC_TOKEN",
15
+ "COSMIC_PROJECT_ID"
16
+ ],
17
+ "setupFields": [
18
+ {
19
+ "key": "COSMIC_PROJECT_ID",
20
+ "title": "Project ID",
21
+ "type": "string",
22
+ "description": "Provider-specific account or source selection."
23
+ }
24
+ ],
25
+ "upstream": {
26
+ "repository": "https://github.com/steipete/CodexBar",
27
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
28
+ "path": "Sources/CodexBarCore/Resources/Plugins/cosmic.js",
29
+ "sha256": "eb869360f719b3b3b96311617c173e624efa05b91c0f7d31ce985f0a3a77d7a6",
30
+ "license": "MIT"
31
+ }
32
+ }
@@ -1,4 +1,3 @@
1
- globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
2
1
  (function () {
3
2
  const KEYCHAIN_ACCESS_TOKEN_SERVICE = "cursor-access-token"
4
3
  const KEYCHAIN_REFRESH_TOKEN_SERVICE = "cursor-refresh-token"
@@ -198,15 +197,16 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
198
197
  const injected = readInjectedCredential(ctx)
199
198
  if (injected) return injected
200
199
 
201
- const sqliteAccessToken = readStateValue(ctx, "cursorAuth/accessToken")
202
- const sqliteRefreshToken = readStateValue(ctx, "cursorAuth/refreshToken")
200
+ const usable = value => typeof value === "string" && value.trim() && !value.trim().startsWith("cursor-safe-storage:") ? value.trim() : null
201
+ const sqliteAccessToken = usable(readStateValue(ctx, "cursorAuth/accessToken"))
202
+ const sqliteRefreshToken = usable(readStateValue(ctx, "cursorAuth/refreshToken"))
203
203
  if (sqliteAccessToken || sqliteRefreshToken) {
204
204
  return { accessToken: sqliteAccessToken, refreshToken: sqliteRefreshToken, source: "sqlite" }
205
205
  }
206
206
  // Generic CLI keychain targets are shared, not tied to an IDE variant.
207
207
  // They cannot represent Nightly or an explicitly selected database profile.
208
- const keychainAccessToken = sharedCredentialsAllowed(ctx) ? readKeychainValue(ctx, KEYCHAIN_ACCESS_TOKEN_SERVICE) : null
209
- const keychainRefreshToken = sharedCredentialsAllowed(ctx) ? readKeychainValue(ctx, KEYCHAIN_REFRESH_TOKEN_SERVICE) : null
208
+ const keychainAccessToken = sharedCredentialsAllowed(ctx) ? usable(readKeychainValue(ctx, KEYCHAIN_ACCESS_TOKEN_SERVICE)) : null
209
+ const keychainRefreshToken = sharedCredentialsAllowed(ctx) ? usable(readKeychainValue(ctx, KEYCHAIN_REFRESH_TOKEN_SERVICE)) : null
210
210
 
211
211
  if (keychainAccessToken || keychainRefreshToken) {
212
212
  return {
@@ -216,6 +216,13 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
216
216
  }
217
217
  }
218
218
 
219
+ if (sharedCredentialsAllowed(ctx) && (!ctx.provider?.instanceId || ctx.provider.instanceId === "cursor") && ctx.host.cursorPaths?.readCliAuth) {
220
+ try {
221
+ const cli = ctx.util.tryParseJson(ctx.host.cursorPaths.readCliAuth())
222
+ if (cli && (usable(cli.accessToken) || usable(cli.refreshToken)))
223
+ return {accessToken:usable(cli.accessToken),refreshToken:usable(cli.refreshToken),source:"cli-file"}
224
+ } catch (_) {}
225
+ }
219
226
  return {
220
227
  accessToken: null,
221
228
  refreshToken: null,
@@ -232,6 +239,8 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
232
239
  if (!credential) return null
233
240
  const accessToken = String(credential.accessToken || credential.sessionKey || "").trim()
234
241
  const refreshToken = String(credential.refreshToken || "").trim()
242
+ if (accessToken.startsWith("cursor-safe-storage:") || refreshToken.startsWith("cursor-safe-storage:"))
243
+ throw {code:"credential-malformed",message:"Cursor safe-storage values are encrypted, not provider access tokens."}
235
244
  if (!accessToken && !refreshToken) return null
236
245
  return {
237
246
  accessToken: accessToken || null,
@@ -239,6 +248,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
239
248
  source: "provider-account",
240
249
  }
241
250
  } catch (e) {
251
+ if (e && e.code) throw e
242
252
  ctx.host.log.warn("provider account credential read failed: " + String(e))
243
253
  return null
244
254
  }
@@ -313,6 +323,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
313
323
  }
314
324
 
315
325
  function persistAccessToken(ctx, source, accessToken) {
326
+ if (source === "cli-file") return false // The Cursor CLI owns its credential file.
316
327
  if (source === "provider-account") {
317
328
  try {
318
329
  if (ctx.host.credentials && typeof ctx.host.credentials.update === "function") {
@@ -1137,13 +1148,21 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
1137
1148
  if (!Number.isFinite(percent) || percent < 0) return result
1138
1149
  const resetMs = ctx.util.parseDateMs(usage.nextResetTimestampUtc)
1139
1150
  const startMs = ctx.util.parseDateMs(usage.currentPeriodStart)
1151
+ const paid = usage.hasNonZeroIncludedLimit === true || usage.includedLimitZero === false
1152
+ const requestsPrimary = result.lines.some(item => item.type === 'progress' && item.label === 'Requests')
1153
+ if (usage.isUnified === true && paid && Number.isFinite(resetMs) && resetMs > Date.parse(ctx.nowIso) && !requestsPrimary) {
1154
+ result.lines = result.lines.filter(item => !['Total usage','Cursor Models','Other Models','Grok Bot usage'].includes(item.label))
1155
+ result.lines.unshift(ctx.line.progress({label:'Weekly',used:Math.min(100,percent),limit:100,format:{kind:'percent'},
1156
+ resetsAt:ctx.util.toIso(resetMs),periodDurationMs:7*24*60*60*1000}))
1157
+ return result
1158
+ }
1140
1159
  const line = ctx.line.progress({
1141
1160
  label: "Grok Bot usage",
1142
1161
  used: Math.min(100, percent),
1143
1162
  limit: 100,
1144
1163
  format: { kind: "percent" },
1145
1164
  resetsAt: Number.isFinite(resetMs) ? ctx.util.toIso(resetMs) : undefined,
1146
- periodDurationMs: Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
1165
+ periodDurationMs: paid ? 7*24*60*60*1000 : Number.isFinite(startMs) && Number.isFinite(resetMs) && resetMs > startMs
1147
1166
  ? resetMs - startMs : 7 * 24 * 60 * 60 * 1000,
1148
1167
  })
1149
1168
  let insertAt = result.lines.length
@@ -1173,7 +1192,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
1173
1192
 
1174
1193
  if (!accessToken && !refreshTokenValue) {
1175
1194
  ctx.host.log.error("probe failed: no access or refresh token in sqlite/keychain")
1176
- throw "Not logged in. " + LOGIN_HINT
1195
+ throw "Not logged in. Cursor encrypted sign-ins require `agent login` for the stable CLI, or explicit provider credentials. " + LOGIN_HINT
1177
1196
  }
1178
1197
 
1179
1198
  ctx.host.log.info("tokens loaded from " + authSource + ": accessToken=" + (accessToken ? "yes" : "no") + " refreshToken=" + (refreshTokenValue ? "yes" : "no"))
@@ -1195,7 +1214,7 @@ globalThis.__OPENUSAGE_PLUGIN_REGISTRATION_ID__ = "cursor";
1195
1214
  accessToken = refreshed
1196
1215
  } else if (!accessToken) {
1197
1216
  ctx.host.log.error("refresh failed and no access token available")
1198
- throw "Not logged in. " + LOGIN_HINT
1217
+ throw "Not logged in. Cursor encrypted sign-ins require `agent login` for the stable CLI, or explicit provider credentials. " + LOGIN_HINT
1199
1218
  }
1200
1219
  }
1201
1220