@hashimkarim/usagestat-linux-x64-gnu 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native-manifest.json +685 -133
- package/package.json +94 -2
- package/plugins/abliteration/LICENSE +21 -0
- package/plugins/abliteration/icon.svg +5 -0
- package/plugins/abliteration/plugin.js +89 -0
- package/plugins/abliteration/plugin.json +24 -0
- package/plugins/aerostack/LICENSE +21 -0
- package/plugins/aerostack/icon.svg +1 -0
- package/plugins/aerostack/plugin.js +94 -0
- package/plugins/aerostack/plugin.json +24 -0
- package/plugins/aixy/icon.svg +1 -0
- package/plugins/aixy/plugin.js +5 -0
- package/plugins/aixy/plugin.json +9 -1
- package/plugins/antigravity/plugin.js +19 -10
- package/plugins/antigravity-cli/plugin.js +8 -3
- package/plugins/antigravity-ide/plugin.js +8 -5
- package/plugins/atlascloud/plugin.js +5 -0
- package/plugins/atlascloud/plugin.json +2 -1
- package/plugins/bifrost/icon.svg +3 -0
- package/plugins/bifrost/plugin.js +5 -0
- package/plugins/bifrost/plugin.json +9 -1
- package/plugins/chatbox/LICENSE +21 -0
- package/plugins/chatbox/icon.svg +4 -0
- package/plugins/chatbox/plugin.js +208 -0
- package/plugins/chatbox/plugin.json +24 -0
- package/plugins/claude/plugin.js +39 -2
- package/plugins/claudeplatform/LICENSE +21 -0
- package/plugins/claudeplatform/icon.svg +3 -0
- package/plugins/claudeplatform/plugin.js +244 -0
- package/plugins/claudeplatform/plugin.json +25 -0
- package/plugins/codex/plugin.js +3 -2
- package/plugins/codex/plugin.json +1 -1
- package/plugins/cosmic/LICENSE +21 -0
- package/plugins/cosmic/icon.svg +1 -0
- package/plugins/cosmic/plugin.js +134 -0
- package/plugins/cosmic/plugin.json +32 -0
- package/plugins/cursor/plugin.js +9 -2
- package/plugins/cursor-nightly/plugin.json +1 -1
- package/plugins/devpass/icon.svg +3 -0
- package/plugins/devpass/plugin.js +5 -0
- package/plugins/devpass/plugin.json +2 -1
- package/plugins/exa/LICENSE +21 -0
- package/plugins/exa/icon-color.svg +1 -0
- package/plugins/exa/icon.svg +1 -0
- package/plugins/exa/plugin.js +75 -0
- package/plugins/exa/plugin.json +32 -0
- package/plugins/fal/LICENSE +21 -0
- package/plugins/fal/icon-color.svg +1 -0
- package/plugins/fal/icon.svg +1 -0
- package/plugins/fal/plugin.js +74 -0
- package/plugins/fal/plugin.json +24 -0
- package/plugins/flora/LICENSE +21 -0
- package/plugins/flora/icon.svg +4 -0
- package/plugins/flora/plugin.js +93 -0
- package/plugins/flora/plugin.json +32 -0
- package/plugins/futureagi/LICENSE +21 -0
- package/plugins/futureagi/icon.svg +24 -0
- package/plugins/futureagi/plugin.js +139 -0
- package/plugins/futureagi/plugin.json +32 -0
- package/plugins/gemini-apps/LICENSE +1 -2
- package/plugins/gemini-apps/plugin.js +136 -465
- package/plugins/gemini-apps/plugin.json +13 -7
- package/plugins/gitkraken/icon.svg +2 -0
- package/plugins/gitkraken/plugin.js +5 -0
- package/plugins/gitkraken/plugin.json +9 -1
- package/plugins/helmcode/icon.svg +5 -0
- package/plugins/helmcode/plugin.js +48 -40
- package/plugins/helmcode/plugin.json +10 -2
- package/plugins/huggingface/plugin.js +6 -1
- package/plugins/huggingface/plugin.json +4 -3
- package/plugins/hyper/plugin.js +53 -47
- package/plugins/hyper/plugin.json +3 -2
- package/plugins/kimi/plugin.js +39 -14
- package/plugins/langdock/LICENSE +21 -0
- package/plugins/langdock/icon.svg +4 -0
- package/plugins/langdock/plugin.js +106 -0
- package/plugins/langdock/plugin.json +25 -0
- package/plugins/linkup/LICENSE +21 -0
- package/plugins/linkup/icon.svg +10 -0
- package/plugins/linkup/plugin.js +46 -0
- package/plugins/linkup/plugin.json +24 -0
- package/plugins/lithosai/plugin.js +5 -0
- package/plugins/lithosai/plugin.json +2 -1
- package/plugins/llmman/icon.svg +4 -0
- package/plugins/llmman/plugin.js +5 -0
- package/plugins/llmman/plugin.json +9 -1
- package/plugins/munito/LICENSE +21 -0
- package/plugins/munito/icon.svg +7 -0
- package/plugins/munito/plugin.js +171 -0
- package/plugins/munito/plugin.json +24 -0
- package/plugins/muse/icon.svg +3 -0
- package/plugins/muse/plugin.js +5 -0
- package/plugins/muse/plugin.json +9 -1
- package/plugins/museai/plugin.js +5 -0
- package/plugins/museai/plugin.json +2 -1
- package/plugins/nous/plugin.js +5 -0
- package/plugins/nous/plugin.json +9 -1
- package/plugins/ollama/plugin.js +58 -0
- package/plugins/opencode-go/plugin.js +17 -6
- package/plugins/parallel/LICENSE +21 -0
- package/plugins/parallel/icon.svg +11 -0
- package/plugins/parallel/plugin.js +97 -0
- package/plugins/parallel/plugin.json +25 -0
- package/plugins/qwencloud/LICENSE +21 -0
- package/plugins/qwencloud/plugin.js +197 -0
- package/plugins/qwencloud/plugin.json +17 -0
- package/plugins/raycast/icon.svg +4 -0
- package/plugins/raycast/plugin.js +5 -0
- package/plugins/raycast/plugin.json +9 -1
- package/plugins/replicate/plugin.js +5 -0
- package/plugins/replicate/plugin.json +2 -1
- package/plugins/sailresearch/LICENSE +21 -0
- package/plugins/sailresearch/icon.svg +4 -0
- package/plugins/sailresearch/plugin.js +104 -0
- package/plugins/sailresearch/plugin.json +24 -0
- package/plugins/sofya/LICENSE +21 -0
- package/plugins/sofya/icon.svg +3 -0
- package/plugins/sofya/plugin.js +86 -0
- package/plugins/sofya/plugin.json +24 -0
- package/plugins/soniox/LICENSE +21 -0
- package/plugins/soniox/icon.svg +3 -0
- package/plugins/soniox/plugin.js +103 -0
- package/plugins/soniox/plugin.json +24 -0
- package/plugins/tavily/LICENSE +21 -0
- package/plugins/tavily/icon.svg +3 -0
- package/plugins/tavily/plugin.js +61 -0
- package/plugins/tavily/plugin.json +24 -0
- package/plugins/tinyapi/LICENSE +21 -0
- package/plugins/tinyapi/icon.svg +4 -0
- package/plugins/tinyapi/plugin.js +73 -0
- package/plugins/tinyapi/plugin.json +25 -0
- package/plugins/traecn/LICENSE +21 -0
- package/plugins/traecn/icon.svg +3 -0
- package/plugins/traecn/plugin.js +104 -0
- package/plugins/traecn/plugin.json +25 -0
- package/plugins/typesafe/icon.svg +4 -0
- package/plugins/typesafe/plugin.js +30 -25
- package/plugins/typesafe/plugin.json +3 -2
- package/plugins/v0/plugin.js +5 -0
- package/plugins/v0/plugin.json +9 -1
- package/plugins/vercel/plugin.js +147 -0
- package/plugins/vercel/plugin.json +34 -3
- package/plugins/workbuddy/icon.svg +1 -1
- package/plugins/workbuddy/plugin.js +9 -4
- package/plugins/workbuddy/plugin.json +20 -2
- package/plugins/xai/plugin.js +9 -0
- package/plugins/xapi/LICENSE +21 -0
- package/plugins/xapi/icon.svg +1 -0
- package/plugins/xapi/plugin.js +90 -0
- package/plugins/xapi/plugin.json +25 -0
- package/plugins/xkiro/icon.svg +1 -0
- package/plugins/xkiro/plugin.js +5 -0
- package/plugins/xkiro/plugin.json +2 -1
- package/usagestat +0 -0
- package/usagestatd +0 -0
- package/plugins/cursor-nightly/plugin.js +0 -1416
|
@@ -1,470 +1,141 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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 (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
4
|
+
"name": "Gemini app",
|
|
5
5
|
"version": "0.1.0",
|
|
6
6
|
"entry": "plugin.js",
|
|
7
7
|
"enabledByDefault": false,
|
|
8
|
-
"supportedModes": [
|
|
8
|
+
"supportedModes": [
|
|
9
|
+
"web"
|
|
10
|
+
],
|
|
9
11
|
"autoMode": "web",
|
|
10
|
-
"webUrl": "https://gemini.google.com",
|
|
11
12
|
"usageDashboardUrl": "https://gemini.google.com/usage",
|
|
12
|
-
"
|
|
13
|
+
"webUrl": "https://gemini.google.com",
|
|
14
|
+
"envVars": [
|
|
15
|
+
"GEMINI_COOKIE"
|
|
16
|
+
],
|
|
17
|
+
"setupFields": [],
|
|
13
18
|
"upstream": {
|
|
14
|
-
"repository": "https://github.com/
|
|
15
|
-
"revision": "
|
|
16
|
-
"path": "
|
|
19
|
+
"repository": "https://github.com/steipete/CodexBar",
|
|
20
|
+
"revision": "6070b29a1a5d51193a634dc1b0a14a67c39d0ccc",
|
|
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);
|