@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,470 +1,141 @@
1
- (function () {
2
- const APP_URL = "https://gemini.google.com/app"
3
- const USAGE_URL = "https://gemini.google.com/usage"
4
- const BATCH_URL = "https://gemini.google.com/_/BardChatUi/data/batchexecute"
5
- const ORIGIN = "https://gemini.google.com"
6
- const RPC_USAGE = "jSf9Qc"
7
- const RPC_PLAN = "sJBwce"
8
- const CURRENT_MS = 5 * 60 * 60 * 1000
9
- const WEEKLY_MS = 7 * 24 * 60 * 60 * 1000
10
- const BRAND = "#3186FF"
11
- const COOKIE_HOSTS = [".google.com", "google.com", ".gemini.google.com", "gemini.google.com"]
12
- const COOKIE_NAMES = [
13
- "SID",
14
- "HSID",
15
- "SSID",
16
- "APISID",
17
- "SAPISID",
18
- "__Secure-1PSID",
19
- "__Secure-1PSIDTS",
20
- "__Secure-3PSID",
21
- "__Secure-1PAPISID",
22
- ]
23
-
24
- function readString(value) {
25
- if (typeof value !== "string") return null
26
- const trimmed = value.trim()
27
- return trimmed ? trimmed : null
28
- }
29
-
30
- function getEnv(ctx, name) {
31
- try {
32
- return readString(ctx.host.env.get(name))
33
- } catch (e) {
34
- ctx.host.log.warn("env read failed for " + name + ": " + String(e))
35
- return null
36
- }
37
- }
38
-
39
- function parseCookieHeader(raw) {
40
- const text = readString(raw)
41
- if (!text) return null
42
- const header = text.replace(/^Cookie:\s*/i, "")
43
- const map = {}
44
- const parts = header.split(";")
45
- for (let i = 0; i < parts.length; i += 1) {
46
- const part = parts[i].trim()
47
- const eq = part.indexOf("=")
48
- if (eq === -1) continue
49
- const name = part.slice(0, eq).trim()
50
- const value = part.slice(eq + 1).trim()
51
- if (name && value) map[name] = value
52
- }
53
- return Object.keys(map).length > 0 ? map : null
54
- }
55
-
56
- function hasGeminiSession(map) {
57
- if (!map || typeof map !== "object") return false
58
- const hasPsid = readString(map["__Secure-1PSID"])
59
- const hasSapisid = readString(map.SAPISID) || readString(map["__Secure-1PAPISID"])
60
- return !!(hasPsid && hasSapisid)
61
- }
62
-
63
- function readChromeCookies(ctx) {
64
- if (!ctx.host.chromiumCookies || typeof ctx.host.chromiumCookies.read !== "function") {
65
- return null
66
- }
67
- try {
68
- const map = ctx.host.chromiumCookies.read({ hosts: COOKIE_HOSTS, names: COOKIE_NAMES })
69
- if (!hasGeminiSession(map)) return null
70
- return map
71
- } catch (e) {
72
- ctx.host.log.warn("chromium cookie read failed: " + String(e))
73
- return null
74
- }
75
- }
76
-
77
- function loadCookies(ctx) {
78
- const provider = ctx.provider || {}
79
- if (Object.prototype.hasOwnProperty.call(provider, "cookieHeader")) {
80
- const configured = parseCookieHeader(provider.cookieHeader)
81
- return configured ? { map: configured, source: "Configured cookie" } : null
82
- }
83
- if (provider.instanceId && provider.instanceId !== "gemini-apps") return null
84
- const explicit = parseCookieHeader(getEnv(ctx, "GEMINI_COOKIE"))
85
- if (explicit) return { map: explicit, source: "GEMINI_COOKIE" }
86
- const chrome = readChromeCookies(ctx)
87
- if (chrome) return { map: chrome, source: "Chrome" }
88
-
89
- const envMap = parseCookieHeader(getEnv(ctx, "GEMINI_COOKIE"))
90
- if (envMap) return { map: envMap, source: "GEMINI_COOKIE" }
91
-
92
- return null
93
- }
94
-
95
- function headerSafe(value) {
96
- const text = readString(value)
97
- if (!text) return null
98
- for (let i = 0; i < text.length; i += 1) {
99
- const code = text.charCodeAt(i)
100
- if (code < 32 || code > 126) return null
101
- }
102
- return text
103
- }
104
-
105
- function cookieHeader(map) {
106
- const parts = []
107
- for (let i = 0; i < COOKIE_NAMES.length; i += 1) {
108
- const name = COOKIE_NAMES[i]
109
- const value = headerSafe(map[name])
110
- if (value) parts.push(name + "=" + value)
111
- }
112
- const keys = Object.keys(map)
113
- for (let i = 0; i < keys.length; i += 1) {
114
- const name = keys[i]
115
- if (COOKIE_NAMES.indexOf(name) !== -1) continue
116
- const value = headerSafe(map[name])
117
- if (value) parts.push(name + "=" + value)
118
- }
119
- return parts.join("; ")
120
- }
121
-
122
- function sapisidHash(ctx, map) {
123
- const sapisid = readString(map.SAPISID) || readString(map["__Secure-1PAPISID"])
124
- if (!sapisid) return null
125
- if (!ctx.host.crypto || typeof ctx.host.crypto.sha1Hex !== "function") {
126
- ctx.host.log.warn("sha1Hex missing; SAPISIDHASH skipped")
127
- return null
128
- }
129
- const ts = Math.floor(Date.parse(ctx.nowIso || "") / 1000)
130
- const timestamp = Number.isFinite(ts) && ts > 0 ? ts : Math.floor(Date.now() / 1000)
131
- const digest = ctx.host.crypto.sha1Hex(timestamp + " " + sapisid + " " + ORIGIN)
132
- return "SAPISIDHASH " + timestamp + "_" + digest
133
- }
134
-
135
- function geminiHeaders(map, extra) {
136
- const headers = {
137
- Cookie: cookieHeader(map),
138
- Origin: ORIGIN,
139
- Referer: USAGE_URL,
140
- "X-Same-Domain": "1",
141
- "User-Agent":
142
- "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
143
- }
144
- if (extra) {
145
- const keys = Object.keys(extra)
146
- for (let i = 0; i < keys.length; i += 1) headers[keys[i]] = extra[keys[i]]
147
- }
148
- return headers
149
- }
150
-
151
- function extractMeta(html) {
152
- function field(name) {
153
- const re = new RegExp('"' + name + '"\\s*:\\s*"([^"]+)"')
154
- const m = re.exec(html)
155
- if (m && m[1]) return m[1]
156
- const re2 = new RegExp("'" + name + "'\\s*:\\s*'([^']+)'")
157
- const m2 = re2.exec(html)
158
- return m2 && m2[1] ? m2[1] : null
159
- }
160
- return {
161
- at: field("SNlM0e"),
162
- bl: field("cfb2h"),
163
- sid: field("FdrFJe"),
164
- }
165
- }
166
-
167
- function looksLikeLogin(html, status) {
168
- if (status === 302 || status === 303 || status === 307 || status === 308) return true
169
- const text = String(html || "")
170
- if (/accounts\.google\.com\/(?:ServiceLogin|v3\/signin)/i.test(text) && !/"SNlM0e"/.test(text)) {
171
- return true
172
- }
173
- return false
174
- }
175
-
176
- function parsePlan(text) {
177
- const s = String(text || "")
178
- if (/\bUltra\b/i.test(s)) return "Ultra"
179
- if (/\bPRO\b/.test(s) || /\bPro\b/.test(s)) return "Pro"
180
- if (/\bFree\b/i.test(s)) return "Free"
181
- return null
182
- }
183
-
184
- function tryParseJson(ctx, text) {
185
- return ctx.util.tryParseJson(text)
186
- }
187
-
188
- function parseBatchexecuteFrames(ctx, text) {
189
- let s = String(text || "").replace(/^\)\]\}'\s*/, "")
190
- const frames = []
191
- let pos = 0
192
- while (pos < s.length) {
193
- while (pos < s.length && /\s/.test(s.charAt(pos))) pos += 1
194
- if (pos >= s.length) break
195
- if (s.charAt(pos) === "[") {
196
- const parsed = tryParseJson(ctx, s.slice(pos))
197
- if (parsed) frames.push(parsed)
198
- break
199
- }
200
- const nl = s.indexOf("\n", pos)
201
- if (nl < 0) break
202
- const n = Number(s.slice(pos, nl).trim())
203
- if (!Number.isFinite(n) || n <= 0) {
204
- pos = nl + 1
205
- continue
1
+ // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
+ globalThis.__usagestat_bundled_options = {"labels":["Current","Weekly","Additional"],"defaults":{},"autoMode":"web","providerId":"gemini-apps","displayName":"Gemini Apps","cookieEnv":"GEMINI_COOKIE"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/gemini-web.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: "gemini",
28
+ name: "Gemini app",
29
+ endpoints: ["https://gemini.google.com"],
30
+ settings: [],
31
+ capabilities: ["browser-cookies", "http-status"],
32
+ cookieDomains: ["gemini.google.com", "google.com"],
33
+ cookiePolicy: { selection: "request-url", cache: "nonpersistent", imports: "access-gated" },
34
+ async fetchUsage(ctx) {
35
+ const domain = "gemini.google.com";
36
+ if (ctx.browser.availability(domain) === "off")
37
+ throw ctx.fail.missingCredential("Gemini app cookies are disabled.");
38
+ // UsagePageQuery from the public UsagePageSurface bundle; this is separate from Gemini CLI quotas.
39
+ const signature = "2/dWbCvkVB0X16r9NPnxifeOAAqdCqMf0xeWvau9yPy7g=";
40
+ const variables = JSON.stringify({
41
+ g1Request: { entrypoints: ["GEMINI_USAGE_SETTINGS_UPGRADE_BANNER"] },
42
+ workspaceRequest: { recommendationsForSurfaceRequests: [] },
43
+ });
44
+ const url =
45
+ `https://${domain}/_/BardChatUi/graphql/schemas/GEMINI_WEB_GRAPHQL/executeQuery` +
46
+ `?query_signature=${encodeURIComponent(signature)}&variables=${encodeURIComponent(variables)}`;
47
+ const invalid = () => {
48
+ throw ctx.fail.parseFailure("Gemini app usage response changed. Update CodexBar or report this.");
49
+ };
50
+ const object = (value) =>
51
+ value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
52
+ let rejected = false;
53
+ for await (const session of ctx.browser.sessions(domain)) {
54
+ const response = await ctx.http.get(url, {
55
+ cookieSession: session.id,
56
+ headers: { Accept: "application/json", Referer: `https://${domain}/usage` },
57
+ retryPolicy: "transientIdempotent",
58
+ });
59
+ if (
60
+ response.status === 401 ||
61
+ response.status === 403 ||
62
+ (response.status === 200 && response.bodyText.trimStart().startsWith("<"))
63
+ ) {
64
+ ctx.browser.rejectCookie(domain, session);
65
+ rejected = true;
66
+ continue;
206
67
  }
207
- const frame = s.slice(nl + 1, nl + 1 + n)
208
- const parsed = tryParseJson(ctx, frame)
209
- if (parsed) frames.push(parsed)
210
- pos = nl + 1 + n
211
- }
212
- return frames
213
- }
214
-
215
- function extractRpcPayload(ctx, text, rpcId) {
216
- const marker = '"' + rpcId + '",'
217
- const idx = String(text || "").indexOf(marker)
218
- if (idx === -1) return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
219
- let p = idx + marker.length
220
- while (p < text.length && /\s/.test(text.charAt(p))) p += 1
221
- if (text.charAt(p) !== '"') {
222
- return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
223
- }
224
- let i = p + 1
225
- while (i < text.length) {
226
- const ch = text.charAt(i)
227
- if (ch === "\\") {
228
- i += 2
229
- continue
68
+ if (response.status === 429) throw ctx.fail.rateLimited("Gemini app usage is rate limited.");
69
+ if (response.status >= 500) throw ctx.fail.providerUnavailable("Gemini app usage is temporarily unavailable.");
70
+ if (response.status !== 200) throw ctx.fail.apiFailure(`Gemini app returned HTTP ${response.status}.`);
71
+ let results;
72
+ try {
73
+ results = JSON.parse(response.bodyText.trim().replace(/^\)\]\}'\s*/, ""));
74
+ } catch (error) {
75
+ void error;
76
+ return invalid();
230
77
  }
231
- if (ch === '"') {
232
- const sliced = text.slice(p, i + 1)
233
- const inner = tryParseJson(ctx, sliced)
234
- if (typeof inner === "string") return tryParseJson(ctx, inner) || inner
235
- return inner
78
+ if (!Array.isArray(results)) return invalid();
79
+ const data = results
80
+ .map((result) => object(_optionalChain([object, "call", (_) => _(result), "optionalAccess", (_2) => _2.data])))
81
+ .filter((value) => value !== undefined);
82
+ if (data.length === 0) return invalid();
83
+ const result = data.find((value) => Object.prototype.hasOwnProperty.call(value, "viewer"));
84
+ if (!result) return invalid();
85
+ if (result.viewer === null) {
86
+ ctx.browser.rejectCookie(domain, session);
87
+ rejected = true;
88
+ continue;
236
89
  }
237
- i += 1
238
- }
239
- return findWrbPayload(ctx, parseBatchexecuteFrames(ctx, text), rpcId)
240
- }
241
-
242
- function findWrbPayload(ctx, frames, rpcId) {
243
- function walk(node) {
244
- if (!node) return null
245
- if (Array.isArray(node)) {
246
- if (node[0] === "wrb.fr" && node[1] === rpcId) {
247
- const payload = node[2]
248
- if (typeof payload === "string") return tryParseJson(ctx, payload) || payload
249
- return payload
250
- }
251
- for (let i = 0; i < node.length; i += 1) {
252
- const found = walk(node[i])
253
- if (found != null) return found
90
+ const viewer = object(result.viewer);
91
+ if (!viewer) return invalid();
92
+ const usage = object(
93
+ _optionalChain([object, "call", (_3) => _3(viewer.rpcs), "optionalAccess", (_4) => _4.getUsageInfo]),
94
+ );
95
+ if (!usage || !Array.isArray(usage.pools)) return invalid();
96
+ const pools = usage.pools;
97
+ const window = (type, minutes) => {
98
+ const pool = pools
99
+ .map(object)
100
+ .find((pool) => _optionalChain([pool, "optionalAccess", (_5) => _5.poolType]) === type);
101
+ const percent = _optionalChain([pool, "optionalAccess", (_6) => _6.percentageUsed]);
102
+ if (typeof percent !== "number" || !Number.isFinite(percent) || percent < 0) return undefined;
103
+ const rawReset = _optionalChain([
104
+ object,
105
+ "call",
106
+ (_7) => _7(_optionalChain([pool, "optionalAccess", (_8) => _8.gxu])),
107
+ "optionalAccess",
108
+ (_9) => _9.windowRefreshesAt,
109
+ ]);
110
+ let resetsAt;
111
+ if (typeof rawReset === "string" && /^\d{4}-\d{2}-\d{2}T/.test(rawReset)) {
112
+ try {
113
+ const date = ctx.date.iso(rawReset);
114
+ if (Number.isFinite(date.getTime())) resetsAt = date;
115
+ } catch (error) {
116
+ void error;
117
+ }
254
118
  }
119
+ return { usedPercent: Math.min(100, percent * 100), windowMinutes: minutes, resetsAt };
120
+ };
121
+ const primary = window("GXU_FIVE_HOURLY", 300);
122
+ const secondary = window("GXU_WEEKLY", 10080);
123
+ if (!primary && !secondary) {
124
+ throw ctx.fail.providerUnavailable("This Google account reports no Gemini app usage windows.");
255
125
  }
256
- return null
257
- }
258
- for (let i = 0; i < frames.length; i += 1) {
259
- const found = walk(frames[i])
260
- if (found != null) return found
261
- }
262
- return null
263
- }
264
-
265
- function resetUnix(row) {
266
- if (!row || !Array.isArray(row[3])) return null
267
- const first = row[3][0]
268
- if (Array.isArray(first)) {
269
- const n = Number(first[0])
270
- return Number.isFinite(n) && n > 0 ? n : null
271
- }
272
- const n = Number(first)
273
- return Number.isFinite(n) && n > 0 ? n : null
274
- }
275
-
276
- function parseUsagePayload(payload) {
277
- let rows = null
278
- if (Array.isArray(payload) && Array.isArray(payload[1])) rows = payload[1]
279
- else if (Array.isArray(payload) && payload.length && Array.isArray(payload[0])) rows = payload
280
- if (!rows) return null
281
-
282
- let current = null
283
- let weekly = null
284
- for (let i = 0; i < rows.length; i += 1) {
285
- const row = rows[i]
286
- if (!Array.isArray(row) || row.length < 3) continue
287
- if (typeof row[1] !== "number" || row[1] < 0 || row[1] > 1) continue
288
- const frac = row[1]
289
- const type = Number(row[2])
290
- if (!Number.isFinite(frac) || !Number.isFinite(type)) continue
291
- const used = Math.max(0, Math.min(100, Math.round(frac * 100)))
292
- const bucket = { used: used, resetsAtUnix: resetUnix(row) }
293
- if (type === 1) current = bucket
294
- else if (type === 2) weekly = bucket
295
- }
296
- if (!current || !weekly) return null
297
- return { current: current, weekly: weekly }
298
- }
299
-
300
- function encodeForm(fields) {
301
- const parts = []
302
- const keys = Object.keys(fields)
303
- for (let i = 0; i < keys.length; i += 1) {
304
- const key = keys[i]
305
- if (fields[key] == null) continue
306
- parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(fields[key])))
307
- }
308
- return parts.join("&")
309
- }
310
-
311
- function batchExecute(ctx, cookies, meta, rpcId, innerPayload) {
312
- const params = [
313
- "rpcids=" + encodeURIComponent(rpcId),
314
- "rt=c",
315
- "source-path=" + encodeURIComponent("/usage"),
316
- ]
317
- if (meta.bl) params.push("bl=" + encodeURIComponent(meta.bl))
318
- if (meta.sid) params.push("f.sid=" + encodeURIComponent(meta.sid))
319
- params.push("_reqid=" + String(100000 + Math.floor(Math.random() * 900000)))
320
-
321
- const headers = geminiHeaders(cookies.map, {
322
- "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
323
- })
324
- const auth = sapisidHash(ctx, cookies.map)
325
- if (auth) headers.Authorization = auth
326
-
327
- const form = encodeForm({
328
- "f.req": JSON.stringify([[[rpcId, innerPayload, null, "generic"]]]),
329
- at: meta.at,
330
- })
331
-
332
- let resp
333
- try {
334
- resp = ctx.util.request({
335
- method: "POST",
336
- url: BATCH_URL + "?" + params.join("&"),
337
- headers: headers,
338
- bodyText: form,
339
- timeoutMs: 15000,
340
- http1Only: true,
341
- })
342
- } catch (e) {
343
- ctx.host.log.warn("batchexecute failed (" + rpcId + "): " + String(e))
344
- return null
345
- }
346
- if (looksLikeLogin(resp.bodyText, resp.status)) {
347
- throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
348
- }
349
- if (resp.status < 200 || resp.status >= 300) {
350
- ctx.host.log.warn("batchexecute HTTP " + resp.status + " for " + rpcId)
351
- return null
352
- }
353
- return extractRpcPayload(ctx, resp.bodyText, rpcId)
354
- }
355
-
356
- function fetchHtml(ctx, cookies, url) {
357
- const headers = geminiHeaders(cookies.map, { Accept: "text/html" })
358
- const auth = sapisidHash(ctx, cookies.map)
359
- if (auth) headers.Authorization = auth
360
- let resp
361
- try {
362
- resp = ctx.util.request({
363
- method: "GET",
364
- url: url,
365
- headers: headers,
366
- timeoutMs: 15000,
367
- http1Only: true,
368
- })
369
- } catch (e) {
370
- ctx.host.log.warn("html request failed (" + url + "): " + String(e))
371
- throw "Could not reach gemini.google.com. Check your connection."
372
- }
373
- if (looksLikeLogin(resp.bodyText, resp.status)) {
374
- throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
375
- }
376
- if (ctx.util.isAuthStatus(resp.status)) {
377
- throw "Gemini Apps session expired. Log into gemini.google.com in Chrome."
378
- }
379
- if (resp.status < 200 || resp.status >= 300) {
380
- throw "Gemini Apps page failed (HTTP " + resp.status + ")."
381
- }
382
- return resp.bodyText
383
- }
384
-
385
- function fetchUsagePage(ctx, cookies) {
386
- let html = fetchHtml(ctx, cookies, APP_URL)
387
- let meta = extractMeta(html)
388
- if (!meta.at) {
389
- ctx.host.log.info("SNlM0e missing on /app; trying /usage")
390
- html = fetchHtml(ctx, cookies, USAGE_URL)
391
- meta = extractMeta(html)
392
- }
393
- ctx.host.log.info(
394
- "htmlChars=" +
395
- String(html.length) +
396
- " hasAt=" +
397
- (meta.at ? "yes" : "no") +
398
- " hasBl=" +
399
- (meta.bl ? "yes" : "no"),
400
- )
401
- if (!meta.at) {
402
- throw "Could not read Gemini Apps session token. Log into gemini.google.com in Chrome."
403
- }
404
- return { html: html, meta: meta }
405
- }
406
-
407
- function probe(ctx) {
408
- const cookies = loadCookies(ctx)
409
- if (!cookies) {
410
- throw "Gemini Apps auth missing. Log into gemini.google.com in Chrome."
411
- }
412
- ctx.host.log.info("cookie names=" + Object.keys(cookies.map).sort().join(","))
413
- if (!hasGeminiSession(cookies.map)) {
414
- throw "Gemini Apps auth missing. Log into gemini.google.com in Chrome."
415
- }
416
-
417
- const page = fetchUsagePage(ctx, cookies)
418
- const payload = batchExecute(ctx, cookies, page.meta, RPC_USAGE, "[]")
419
- const usage = parseUsagePayload(payload)
420
- if (!usage) throw "Could not parse Gemini Apps usage."
421
-
422
- let plan = null
423
- try {
424
- const planPayload = batchExecute(ctx, cookies, page.meta, RPC_PLAN, "[]")
425
- if (planPayload != null) {
426
- plan = parsePlan(JSON.stringify(planPayload))
427
- }
428
- } catch (e) {
429
- ctx.host.log.warn("plan RPC skipped: " + String(e))
430
- }
431
-
432
- const currentResetsAt = usage.current.resetsAtUnix
433
- ? ctx.util.toIso(usage.current.resetsAtUnix)
434
- : undefined
435
- const weeklyResetsAt = usage.weekly.resetsAtUnix
436
- ? ctx.util.toIso(usage.weekly.resetsAtUnix)
437
- : undefined
438
-
439
- const lines = [
440
- ctx.line.progress({
441
- label: "Current",
442
- used: usage.current.used,
443
- limit: 100,
444
- format: { kind: "percent" },
445
- resetsAt: currentResetsAt,
446
- periodDurationMs: CURRENT_MS,
447
- color: BRAND,
448
- }),
449
- ctx.line.progress({
450
- label: "Weekly",
451
- used: usage.weekly.used,
452
- limit: 100,
453
- format: { kind: "percent" },
454
- resetsAt: weeklyResetsAt,
455
- periodDurationMs: WEEKLY_MS,
456
- color: BRAND,
457
- }),
458
- ctx.line.text({ label: "Source", value: cookies.source }),
459
- ]
460
-
461
- const result = { lines: lines, source: "web" }
462
- if (plan) result.plan = ctx.fmt.planLabel(plan)
463
- return result
464
- }
465
-
466
- globalThis.__openusage_plugin = {
467
- id: "gemini-apps",
468
- probe: probe,
469
- }
470
- })()
126
+ return {
127
+ primary,
128
+ secondary,
129
+ identity: { loginMethod: usage.subscriptionTier === "TIER1" ? "Pro" : undefined },
130
+ dataConfidence: "exact",
131
+ };
132
+ }
133
+ if (rejected) throw ctx.fail.authenticationExpired("Sign in again at gemini.google.com/usage.");
134
+ throw ctx.fail.missingCredential(
135
+ `Sign in at gemini.google.com/usage. Supported browsers: ${ctx.browser.supportedBrowsers}. Or paste its Cookie header.`,
136
+ );
137
+ },
138
+ });
139
+
140
+ // END UPSTREAM
141
+ })(globalThis.defineProvider);
@@ -1,19 +1,25 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "gemini-apps",
4
- "name": "Gemini Apps",
4
+ "name": "Gemini app",
5
5
  "version": "0.1.0",
6
6
  "entry": "plugin.js",
7
7
  "enabledByDefault": false,
8
- "supportedModes": ["web"],
8
+ "supportedModes": [
9
+ "web"
10
+ ],
9
11
  "autoMode": "web",
10
- "webUrl": "https://gemini.google.com",
11
12
  "usageDashboardUrl": "https://gemini.google.com/usage",
12
- "envVars": ["GEMINI_COOKIE"],
13
+ "webUrl": "https://gemini.google.com",
14
+ "envVars": [
15
+ "GEMINI_COOKIE"
16
+ ],
17
+ "setupFields": [],
13
18
  "upstream": {
14
- "repository": "https://github.com/barramee27/crossusage",
15
- "revision": "2b90b00be84e06acbf71a7c5c8db22d1c4e83966",
16
- "path": "plugins/gemini-apps/plugin.js",
19
+ "repository": "https://github.com/steipete/CodexBar",
20
+ "revision": "1f6da9bec44455288ddee5ef6f3617a803d8eb2a",
21
+ "path": "Sources/CodexBarCore/Resources/Plugins/gemini-web.js",
22
+ "sha256": "7c71195603b2051ad74d85c0acc523546cf5ec62a600ae4900a1d7e54be7b02e",
17
23
  "license": "MIT"
18
24
  }
19
25
  }
@@ -0,0 +1,2 @@
1
+ <!-- GitKraken mark from GitKraken browser extension: https://github.com/gitkraken/gk-browser-extension/blob/main/src/hosts/github.ts -->
2
+ <svg role="img" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>GitKraken</title><path d="M0 16C0 7.177 7.177 0 16 0s16 7.177 16 16-7.177 16-16 16S0 24.823 0 16Zm28.226-4.714a.607.607 0 0 0-.269-.317l-.004-.001c-.123-.07-.268-.095-.409-.07a.613.613 0 0 0-.5.6c.002.073.014.144.04.214.502 1.337.757 2.741.756 4.17a11.835 11.835 0 0 1-10.188 11.729v-5.396c.284-.058.566-.134.84-.226v4.67l.111-.027a11.128 11.128 0 0 0 6.042-3.864 10.939 10.939 0 0 0 2.406-6.884 11.052 11.052 0 0 0-5.703-9.685.618.618 0 0 0-.476-.046.61.61 0 0 0-.113 1.113 9.846 9.846 0 0 1-1.031 17.733v-3.954a1.666 1.666 0 0 0 1.104-1.387 1.67 1.67 0 0 0-.768-1.606c.237-2.17.927-2.598 1.433-2.913.302-.186.561-.348.561-.817v-.668c0-.692-.687-2.307-2.224-4.353-2.127-2.834-3.34-3.13-3.659-3.153-.12-.012-.223-.007-.337 0h-.012c-.321.023-1.534.32-3.664 3.153-1.539 2.046-2.227 3.661-2.227 4.353v.666c0 .47.26.63.56.817.506.313 1.197.742 1.433 2.912a1.664 1.664 0 0 0-.779 1.423c0 .692.456 1.33 1.117 1.572v3.955a9.837 9.837 0 0 1-4.364-3.51 9.784 9.784 0 0 1-1.752-5.604c0-3.578 1.95-6.88 5.088-8.62a.609.609 0 0 0-.294-1.14h-.001a.593.593 0 0 0-.29.076 11.057 11.057 0 0 0-5.71 9.684c0 2.53.833 4.91 2.407 6.885a11.131 11.131 0 0 0 6.041 3.864l.111.027v-4.669c.275.092.557.168.84.226v5.395A11.834 11.834 0 0 1 4.154 15.884c0-1.43.255-2.833.76-4.17a.612.612 0 0 0-.017-.464.597.597 0 0 0-.34-.316.611.611 0 0 0-.655.155.61.61 0 0 0-.125.202 13.133 13.133 0 0 0-.744 6.067A13.135 13.135 0 0 0 5.128 23.1a13.134 13.134 0 0 0 4.477 4.162 13.14 13.14 0 0 0 5.88 1.672l.093.003v-6.565a17.775 17.775 0 0 0 .479.012c.08-.002.233-.006.362-.012v6.565l.093-.003a13.156 13.156 0 0 0 5.878-1.676 13.152 13.152 0 0 0 4.477-4.163 13.145 13.145 0 0 0 2.097-5.741 13.146 13.146 0 0 0-.738-6.068ZM13.664 20.01a.977.977 0 0 0-.436-1.442.978.978 0 0 0-1.329.71.978.978 0 0 0 .583 1.09.974.974 0 0 0 1.183-.357h-.002Zm6.343-.994a.971.971 0 0 0-1.14-.475.978.978 0 0 0-.69 1.025.975.975 0 0 0 .968.881.974.974 0 0 0 .861-1.431h.001Z"/></svg>
@@ -1,5 +1,7 @@
1
1
  // Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
2
2
  globalThis.__usagestat_bundled_options = {"labels":["Personal","Shared pool","Additional"],"defaults":{},"autoMode":"api"};
3
+ (function(defineProvider) {
4
+ // BEGIN UPSTREAM Sources/CodexBarCore/Resources/Plugins/gitkraken.js
3
5
  function _nullishCoalesce(lhs, rhsFn) {
4
6
  if (lhs != null) {
5
7
  return lhs;
@@ -151,3 +153,6 @@ defineProvider({
151
153
  };
152
154
  },
153
155
  });
156
+
157
+ // END UPSTREAM
158
+ })(globalThis.defineProvider);