@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1 → 2.0.0-alpha.4
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/LICENSE +0 -0
- package/native-manifest.json +1088 -134
- package/package.json +160 -1
- package/plugins/UPSTREAM-LICENSES.md +113 -0
- package/plugins/_provider-icons/LICENSE +21 -0
- package/plugins/_provider-icons/NOTICE +12 -0
- package/plugins/_provider-icons/licenses/CodexBar-MIT.txt +21 -0
- package/plugins/_provider-icons/licenses/LobeHub-MIT.txt +21 -0
- package/plugins/_provider-icons/licenses/UsageStat-Bar-MIT.txt +21 -0
- package/plugins/_provider-icons/manifest.json +1219 -0
- package/plugins/_provider-icons/provenance.json +269 -0
- package/plugins/_provider-icons/source.json +4 -0
- package/plugins/abacus-ai/icon-color.svg +18 -0
- package/plugins/abacus-ai/icon.svg +0 -0
- package/plugins/abacus-ai/plugin.js +0 -0
- package/plugins/abacus-ai/plugin.json +0 -0
- package/plugins/aiand/icon.svg +6 -0
- package/plugins/aiand/plugin.js +108 -0
- package/plugins/aiand/plugin.json +22 -0
- package/plugins/aixy/LICENSE +21 -0
- package/plugins/aixy/plugin.js +261 -0
- package/plugins/aixy/plugin.json +24 -0
- package/plugins/alibaba/icon-color.svg +0 -0
- package/plugins/alibaba/icon.svg +0 -0
- package/plugins/alibaba/plugin.js +0 -0
- package/plugins/alibaba/plugin.json +0 -0
- package/plugins/alibaba-token-plan/icon-color.svg +0 -0
- package/plugins/alibaba-token-plan/icon.svg +0 -0
- package/plugins/alibaba-token-plan/plugin.js +0 -0
- package/plugins/alibaba-token-plan/plugin.json +0 -0
- package/plugins/amp/icon-color.svg +0 -0
- package/plugins/amp/icon-conflict.svg +0 -0
- package/plugins/amp/icon.svg +0 -0
- package/plugins/amp/plugin.js +0 -0
- package/plugins/amp/plugin.json +0 -0
- package/plugins/antigravity/icon-color.svg +0 -0
- package/plugins/antigravity/icon-conflict.svg +0 -0
- package/plugins/antigravity/icon.svg +0 -0
- package/plugins/antigravity/plugin.js +136 -46
- package/plugins/antigravity/plugin.json +0 -0
- package/plugins/antigravity-cli/icon-color.svg +1 -0
- package/plugins/antigravity-cli/icon.svg +1 -3
- package/plugins/antigravity-cli/plugin.js +11 -15
- package/plugins/antigravity-cli/plugin.json +0 -0
- package/plugins/antigravity-ide/icon-color.svg +1 -0
- package/plugins/antigravity-ide/icon.svg +1 -3
- package/plugins/antigravity-ide/plugin.js +0 -0
- package/plugins/antigravity-ide/plugin.json +0 -0
- package/plugins/atlascloud/LICENSE +21 -0
- package/plugins/atlascloud/icon-color.svg +1 -0
- package/plugins/atlascloud/icon.svg +1 -0
- package/plugins/atlascloud/plugin.js +49 -0
- package/plugins/atlascloud/plugin.json +23 -0
- package/plugins/augment/icon-color.svg +14 -0
- package/plugins/augment/icon.svg +0 -0
- package/plugins/augment/plugin.js +0 -0
- package/plugins/augment/plugin.json +0 -0
- package/plugins/aws-bedrock/icon-color.svg +0 -0
- package/plugins/aws-bedrock/icon.svg +0 -0
- package/plugins/aws-bedrock/plugin.js +0 -0
- package/plugins/aws-bedrock/plugin.json +0 -0
- package/plugins/azure-openai/icon-color.svg +0 -0
- package/plugins/azure-openai/icon.svg +0 -0
- package/plugins/azure-openai/plugin.js +0 -0
- package/plugins/azure-openai/plugin.json +0 -0
- package/plugins/bifrost/LICENSE +21 -0
- package/plugins/bifrost/plugin.js +361 -0
- package/plugins/bifrost/plugin.json +23 -0
- package/plugins/chutes/icon.svg +0 -0
- package/plugins/chutes/plugin.js +0 -0
- package/plugins/chutes/plugin.json +0 -0
- package/plugins/claude/icon-color.svg +0 -0
- package/plugins/claude/icon.svg +0 -0
- package/plugins/claude/plugin.js +358 -161
- package/plugins/claude/plugin.json +1 -1
- package/plugins/clawrouter/icon.svg +7 -0
- package/plugins/clawrouter/plugin.js +45 -0
- package/plugins/clawrouter/plugin.json +22 -0
- package/plugins/clinepass/icon.svg +4 -0
- package/plugins/clinepass/plugin.js +87 -0
- package/plugins/clinepass/plugin.json +23 -0
- package/plugins/codebuddy/plugin.js +331 -0
- package/plugins/codebuddy/plugin.json +30 -0
- package/plugins/codebuff/icon-color.svg +92 -0
- package/plugins/codebuff/icon.svg +0 -0
- package/plugins/codebuff/plugin.js +0 -0
- package/plugins/codebuff/plugin.json +0 -0
- package/plugins/coderabbit/plugin.js +25 -0
- package/plugins/coderabbit/plugin.json +12 -0
- package/plugins/codex/icon-color.svg +0 -0
- package/plugins/codex/icon.svg +0 -0
- package/plugins/codex/plugin.js +8 -2
- package/plugins/codex/plugin.json +0 -0
- package/plugins/command-code/icon-color.svg +3 -0
- package/plugins/command-code/icon.svg +0 -0
- package/plugins/command-code/plugin.js +14 -3
- package/plugins/command-code/plugin.json +0 -0
- package/plugins/copilot/icon-color.svg +0 -0
- package/plugins/copilot/icon.svg +1 -1
- package/plugins/copilot/plugin.js +61 -9
- package/plugins/copilot/plugin.json +4 -1
- package/plugins/crof/icon-color.svg +3 -0
- package/plugins/crof/icon.svg +0 -0
- package/plugins/crof/plugin.js +0 -0
- package/plugins/crof/plugin.json +0 -0
- package/plugins/crofai/icon-color.svg +3 -0
- package/plugins/crofai/icon.svg +2 -15
- package/plugins/crofai/plugin.js +0 -0
- package/plugins/crofai/plugin.json +0 -0
- package/plugins/cursor/icon-color.svg +1 -0
- package/plugins/cursor/icon-conflict.svg +0 -0
- package/plugins/cursor/icon.svg +0 -0
- package/plugins/cursor/plugin.js +46 -9
- package/plugins/cursor/plugin.json +3 -2
- package/plugins/cursor-nightly/icon.png +0 -0
- package/plugins/cursor-nightly/plugin.js +45 -8
- package/plugins/cursor-nightly/plugin.json +3 -2
- package/plugins/deepgram/icon.svg +0 -0
- package/plugins/deepgram/plugin.js +0 -0
- package/plugins/deepgram/plugin.json +0 -0
- package/plugins/deepinfra/icon-color.svg +1 -0
- package/plugins/deepinfra/icon.svg +1 -0
- package/plugins/deepinfra/plugin.js +97 -0
- package/plugins/deepinfra/plugin.json +28 -0
- package/plugins/deepseek/icon-color.svg +0 -0
- package/plugins/deepseek/icon.svg +0 -0
- package/plugins/deepseek/plugin.js +150 -59
- package/plugins/deepseek/plugin.json +3 -1
- package/plugins/devin/icon.svg +0 -0
- package/plugins/devin/plugin.js +114 -0
- package/plugins/devin/plugin.json +2 -1
- package/plugins/devpass/LICENSE +21 -0
- package/plugins/devpass/plugin.js +115 -0
- package/plugins/devpass/plugin.json +23 -0
- package/plugins/doubao/icon-color.svg +0 -0
- package/plugins/doubao/icon.svg +0 -0
- package/plugins/doubao/plugin.js +0 -0
- package/plugins/doubao/plugin.json +0 -0
- package/plugins/droid/icon.svg +0 -0
- package/plugins/droid/plugin.json +2 -1
- package/plugins/elevenlabs/icon-color.svg +4 -0
- package/plugins/elevenlabs/icon.svg +0 -0
- package/plugins/elevenlabs/plugin.js +17 -2
- package/plugins/elevenlabs/plugin.json +0 -0
- package/plugins/factory/icon-color.svg +1 -0
- package/plugins/factory/icon-conflict.svg +0 -0
- package/plugins/factory/icon.svg +0 -0
- package/plugins/factory/plugin.js +38 -14
- package/plugins/factory/plugin.json +2 -1
- package/plugins/fireworks-ai/icon-color.svg +1 -0
- package/plugins/fireworks-ai/icon.svg +1 -6
- package/plugins/fireworks-ai/plugin.js +0 -0
- package/plugins/fireworks-ai/plugin.json +0 -0
- package/plugins/gemini/icon-color.svg +0 -0
- package/plugins/gemini/icon.svg +0 -0
- package/plugins/gemini/plugin.js +0 -0
- package/plugins/gemini/plugin.json +0 -0
- package/plugins/gemini-apps/LICENSE +22 -0
- package/plugins/gemini-apps/icon-color.svg +1 -0
- package/plugins/gemini-apps/icon.svg +1 -0
- package/plugins/gemini-apps/plugin.js +470 -0
- package/plugins/gemini-apps/plugin.json +19 -0
- package/plugins/gitkraken/LICENSE +21 -0
- package/plugins/gitkraken/plugin.js +153 -0
- package/plugins/gitkraken/plugin.json +24 -0
- package/plugins/grok/icon-color.svg +1 -0
- package/plugins/grok/icon.svg +0 -0
- package/plugins/grok/plugin.js +30 -5
- package/plugins/grok/plugin.json +3 -1
- package/plugins/groqcloud/icon-color.svg +1 -0
- package/plugins/groqcloud/icon.svg +0 -0
- package/plugins/groqcloud/plugin.js +0 -0
- package/plugins/groqcloud/plugin.json +0 -0
- package/plugins/helmcode/LICENSE +21 -0
- package/plugins/helmcode/plugin.js +188 -0
- package/plugins/helmcode/plugin.json +24 -0
- package/plugins/huggingface/LICENSE +21 -0
- package/plugins/huggingface/icon-color.svg +1 -0
- package/plugins/huggingface/icon.svg +1 -0
- package/plugins/huggingface/plugin.js +293 -0
- package/plugins/huggingface/plugin.json +25 -0
- package/plugins/hyper/LICENSE +21 -0
- package/plugins/hyper/plugin.js +131 -0
- package/plugins/hyper/plugin.json +26 -0
- package/plugins/ibmbob/icon.svg +23 -0
- package/plugins/ibmbob/plugin.js +61 -0
- package/plugins/ibmbob/plugin.json +22 -0
- package/plugins/jetbrains-ai-assistant/icon-color.svg +0 -0
- package/plugins/jetbrains-ai-assistant/icon-conflict.svg +0 -0
- package/plugins/jetbrains-ai-assistant/icon.svg +0 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +0 -0
- package/plugins/jetbrains-ai-assistant/plugin.json +0 -0
- package/plugins/kilo/icon-color.svg +1 -0
- package/plugins/kilo/icon.svg +0 -0
- package/plugins/kilo/plugin.js +0 -0
- package/plugins/kilo/plugin.json +0 -0
- package/plugins/kimi/icon-color.svg +0 -0
- package/plugins/kimi/icon-conflict.svg +0 -0
- package/plugins/kimi/icon.svg +0 -0
- package/plugins/kimi/plugin.js +122 -141
- package/plugins/kimi/plugin.json +5 -2
- package/plugins/kimi-k2/icon-color.svg +0 -0
- package/plugins/kimi-k2/icon.svg +0 -0
- package/plugins/kimi-k2/plugin.js +0 -0
- package/plugins/kimi-k2/plugin.json +0 -0
- package/plugins/kiro/icon-color.svg +0 -0
- package/plugins/kiro/icon-conflict.svg +0 -0
- package/plugins/kiro/icon.svg +0 -0
- package/plugins/kiro/plugin.js +49 -10
- package/plugins/kiro/plugin.json +3 -1
- package/plugins/litellm/icon.svg +0 -0
- package/plugins/litellm/plugin.js +54 -3
- package/plugins/litellm/plugin.json +0 -0
- package/plugins/llm-proxy/icon.svg +0 -0
- package/plugins/llm-proxy/plugin.js +0 -0
- package/plugins/llm-proxy/plugin.json +0 -0
- package/plugins/llmman/LICENSE +21 -0
- package/plugins/llmman/plugin.js +133 -0
- package/plugins/llmman/plugin.json +23 -0
- package/plugins/longcat/icon-color.svg +1 -0
- package/plugins/longcat/icon.svg +1 -0
- package/plugins/longcat/plugin.js +182 -0
- package/plugins/longcat/plugin.json +23 -0
- package/plugins/manus/icon-color.svg +6 -0
- package/plugins/manus/icon.svg +0 -0
- package/plugins/manus/plugin.js +0 -0
- package/plugins/manus/plugin.json +0 -0
- package/plugins/mimo/icon-color.svg +4 -0
- package/plugins/mimo/icon.svg +4 -1
- package/plugins/mimo/plugin.js +0 -0
- package/plugins/mimo/plugin.json +0 -0
- package/plugins/minimax/icon-color.svg +0 -0
- package/plugins/minimax/icon-conflict.svg +0 -0
- package/plugins/minimax/icon.svg +0 -0
- package/plugins/minimax/plugin.js +25 -31
- package/plugins/minimax/plugin.json +0 -0
- package/plugins/mistral/icon-color.svg +0 -0
- package/plugins/mistral/icon.svg +0 -0
- package/plugins/mistral/plugin.js +110 -5
- package/plugins/mistral/plugin.json +0 -0
- package/plugins/moonshot/icon-color.svg +1 -0
- package/plugins/moonshot/icon.svg +0 -0
- package/plugins/moonshot/plugin.js +8 -6
- package/plugins/moonshot/plugin.json +0 -0
- package/plugins/muse/LICENSE +21 -0
- package/plugins/muse/plugin.js +319 -0
- package/plugins/muse/plugin.json +26 -0
- package/plugins/nanogpt/icon-color.svg +0 -0
- package/plugins/nanogpt/icon.svg +0 -0
- package/plugins/nanogpt/plugin.js +0 -0
- package/plugins/nanogpt/plugin.json +0 -0
- package/plugins/neuralwatt/icon.svg +0 -0
- package/plugins/neuralwatt/plugin.js +0 -0
- package/plugins/neuralwatt/plugin.json +0 -0
- package/plugins/notion/icon.svg +1 -0
- package/plugins/notion/plugin.js +282 -0
- package/plugins/notion/plugin.json +29 -0
- package/plugins/nous/LICENSE +21 -0
- package/plugins/nous/icon-color.svg +1 -0
- package/plugins/nous/icon.svg +1 -0
- package/plugins/nous/plugin.js +124 -0
- package/plugins/nous/plugin.json +24 -0
- package/plugins/ollama/icon-color.svg +1 -0
- package/plugins/ollama/icon.svg +0 -0
- package/plugins/ollama/plugin.js +257 -56
- package/plugins/ollama/plugin.json +6 -3
- package/plugins/openai-api/icon-color.svg +1 -0
- package/plugins/openai-api/icon.svg +0 -0
- package/plugins/openai-api/plugin.js +0 -0
- package/plugins/openai-api/plugin.json +0 -0
- package/plugins/opencode/icon-color.svg +1 -0
- package/plugins/opencode/icon.svg +0 -0
- package/plugins/opencode/plugin.js +0 -0
- package/plugins/opencode/plugin.json +0 -0
- package/plugins/opencode-go/icon-color.svg +1 -0
- package/plugins/opencode-go/icon-conflict.svg +0 -0
- package/plugins/opencode-go/icon.svg +0 -0
- package/plugins/opencode-go/plugin.js +103 -17
- package/plugins/opencode-go/plugin.json +5 -2
- package/plugins/openrouter/icon-color.svg +1 -0
- package/plugins/openrouter/icon.svg +0 -0
- package/plugins/openrouter/plugin.js +371 -114
- package/plugins/openrouter/plugin.json +6 -0
- package/plugins/perplexity/icon-color.svg +0 -0
- package/plugins/perplexity/icon-conflict.svg +0 -0
- package/plugins/perplexity/icon.svg +0 -0
- package/plugins/perplexity/plugin.js +0 -0
- package/plugins/perplexity/plugin.json +0 -0
- package/plugins/pi/plugin.js +113 -0
- package/plugins/pi/plugin.json +11 -0
- package/plugins/poe/icon.svg +0 -0
- package/plugins/poe/plugin.js +24 -18
- package/plugins/poe/plugin.json +2 -0
- package/plugins/qoder/icon.svg +3 -0
- package/plugins/qoder/plugin.js +227 -0
- package/plugins/qoder/plugin.json +23 -0
- package/plugins/qwencloud/icon-color.svg +1 -0
- package/plugins/qwencloud/icon.svg +1 -0
- package/plugins/qwencloud/plugin.js +677 -0
- package/plugins/qwencloud/plugin.json +29 -0
- package/plugins/raycast/LICENSE +21 -0
- package/plugins/raycast/plugin.js +174 -0
- package/plugins/raycast/plugin.json +24 -0
- package/plugins/replicate/LICENSE +21 -0
- package/plugins/replicate/icon-color.svg +1 -0
- package/plugins/replicate/icon.svg +1 -0
- package/plugins/replicate/plugin.js +165 -0
- package/plugins/replicate/plugin.json +24 -0
- package/plugins/sakana/icon.svg +5 -0
- package/plugins/sakana/plugin.js +156 -0
- package/plugins/sakana/plugin.json +23 -0
- package/plugins/stepfun/icon-color.svg +0 -0
- package/plugins/stepfun/icon.svg +0 -0
- package/plugins/stepfun/plugin.js +0 -0
- package/plugins/stepfun/plugin.json +0 -0
- package/plugins/sub2api/icon.svg +4 -0
- package/plugins/sub2api/plugin.js +191 -0
- package/plugins/sub2api/plugin.json +16 -0
- package/plugins/synthetic/icon-color.svg +14 -0
- package/plugins/synthetic/icon-conflict.svg +0 -0
- package/plugins/synthetic/icon.svg +0 -0
- package/plugins/synthetic/plugin.js +0 -0
- package/plugins/synthetic/plugin.json +0 -0
- package/plugins/t3chat/icon-color.svg +0 -0
- package/plugins/t3chat/icon.svg +0 -0
- package/plugins/t3chat/plugin.js +0 -0
- package/plugins/t3chat/plugin.json +0 -0
- package/plugins/typesafe/LICENSE +21 -0
- package/plugins/typesafe/plugin.js +225 -0
- package/plugins/typesafe/plugin.json +24 -0
- package/plugins/v0/LICENSE +21 -0
- package/plugins/v0/icon-color.svg +1 -0
- package/plugins/v0/icon.svg +1 -0
- package/plugins/v0/plugin.js +214 -0
- package/plugins/v0/plugin.json +24 -0
- package/plugins/venice/icon-color.svg +0 -0
- package/plugins/venice/icon.svg +0 -0
- package/plugins/venice/plugin.js +50 -1
- package/plugins/venice/plugin.json +2 -1
- package/plugins/vercel/LICENSE +21 -0
- package/plugins/vercel/icon-color.svg +1 -0
- package/plugins/vercel/icon.svg +1 -0
- package/plugins/vercel/plugin.js +55 -0
- package/plugins/vercel/plugin.json +23 -0
- package/plugins/vertex-ai/icon-color.svg +0 -0
- package/plugins/vertex-ai/icon.svg +0 -0
- package/plugins/vertex-ai/plugin.js +0 -0
- package/plugins/vertex-ai/plugin.json +0 -0
- package/plugins/warp/icon-color.svg +0 -0
- package/plugins/warp/icon.svg +0 -0
- package/plugins/warp/plugin.js +0 -0
- package/plugins/warp/plugin.json +0 -0
- package/plugins/wayfinder/icon.svg +5 -0
- package/plugins/wayfinder/plugin.js +47 -0
- package/plugins/wayfinder/plugin.json +16 -0
- package/plugins/windsurf/icon-color.svg +1 -0
- package/plugins/windsurf/icon-conflict.svg +0 -0
- package/plugins/windsurf/icon.svg +0 -0
- package/plugins/windsurf/plugin.js +0 -0
- package/plugins/windsurf/plugin.json +0 -0
- package/plugins/xai/icon-color.svg +1 -0
- package/plugins/xai/icon.svg +1 -0
- package/plugins/xai/plugin.js +162 -0
- package/plugins/xai/plugin.json +28 -0
- package/plugins/xkiro/LICENSE +21 -0
- package/plugins/xkiro/plugin.js +99 -0
- package/plugins/xkiro/plugin.json +23 -0
- package/plugins/zai/icon-color.svg +1 -0
- package/plugins/zai/icon-conflict.svg +0 -0
- package/plugins/zai/icon.svg +0 -0
- package/plugins/zai/plugin.js +111 -161
- package/plugins/zai/plugin.json +4 -1
- package/plugins/zed/icon.svg +0 -0
- package/plugins/zed/plugin.js +0 -0
- package/plugins/zed/plugin.json +0 -0
- package/plugins/zenmux/icon-color.svg +1 -0
- package/plugins/zenmux/icon.svg +1 -0
- package/plugins/zenmux/plugin.js +102 -0
- package/plugins/zenmux/plugin.json +23 -0
- package/plugins/zoommate/icon.svg +1 -0
- package/plugins/zoommate/plugin.js +244 -0
- package/plugins/zoommate/plugin.json +29 -0
- package/usagestat +0 -0
- package/usagestatd +0 -0
|
@@ -0,0 +1,470 @@
|
|
|
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
|
|
206
|
+
}
|
|
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
|
|
230
|
+
}
|
|
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
|
|
236
|
+
}
|
|
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
|
|
254
|
+
}
|
|
255
|
+
}
|
|
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
|
+
})()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "gemini-apps",
|
|
4
|
+
"name": "Gemini Apps",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"enabledByDefault": false,
|
|
8
|
+
"supportedModes": ["web"],
|
|
9
|
+
"autoMode": "web",
|
|
10
|
+
"webUrl": "https://gemini.google.com",
|
|
11
|
+
"usageDashboardUrl": "https://gemini.google.com/usage",
|
|
12
|
+
"envVars": ["GEMINI_COOKIE"],
|
|
13
|
+
"upstream": {
|
|
14
|
+
"repository": "https://github.com/barramee27/crossusage",
|
|
15
|
+
"revision": "2b90b00be84e06acbf71a7c5c8db22d1c4e83966",
|
|
16
|
+
"path": "plugins/gemini-apps/plugin.js",
|
|
17
|
+
"license": "MIT"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -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,153 @@
|
|
|
1
|
+
// Generated by tools/vendor-codexbar-providers.cjs; see LICENSE.
|
|
2
|
+
globalThis.__usagestat_bundled_options = {"labels":["Personal","Shared pool","Additional"],"defaults":{},"autoMode":"api"};
|
|
3
|
+
function _nullishCoalesce(lhs, rhsFn) {
|
|
4
|
+
if (lhs != null) {
|
|
5
|
+
return lhs;
|
|
6
|
+
} else {
|
|
7
|
+
return rhsFn();
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function _optionalChain(ops) {
|
|
11
|
+
let lastAccessLHS = undefined;
|
|
12
|
+
let value = ops[0];
|
|
13
|
+
let i = 1;
|
|
14
|
+
while (i < ops.length) {
|
|
15
|
+
const op = ops[i];
|
|
16
|
+
const fn = ops[i + 1];
|
|
17
|
+
i += 2;
|
|
18
|
+
if ((op === "optionalAccess" || op === "optionalCall") && value == null) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (op === "access" || op === "optionalAccess") {
|
|
22
|
+
lastAccessLHS = value;
|
|
23
|
+
value = fn(value);
|
|
24
|
+
} else if (op === "call" || op === "optionalCall") {
|
|
25
|
+
value = fn((...args) => value.call(lastAccessLHS, ...args));
|
|
26
|
+
lastAccessLHS = undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
defineProvider({
|
|
33
|
+
id: "gitkraken",
|
|
34
|
+
name: "GitKraken AI",
|
|
35
|
+
endpoints: ["https://api.gitkraken.dev"],
|
|
36
|
+
auth: { type: "bearer", secret: "GITKRAKEN_API_TOKEN" },
|
|
37
|
+
settings: [
|
|
38
|
+
{ key: "GITKRAKEN_API_TOKEN", title: "GitKraken access token", type: "secure" },
|
|
39
|
+
{ key: "GITKRAKEN_ORG_ID", title: "API organization ID", type: "plain" },
|
|
40
|
+
{ key: "CLIENT_VERSION", title: "Client version", type: "plain" },
|
|
41
|
+
],
|
|
42
|
+
capabilities: ["http-status"],
|
|
43
|
+
async fetchUsage(ctx) {
|
|
44
|
+
const token = ctx.settings.getSecret("GITKRAKEN_API_TOKEN");
|
|
45
|
+
if (!token) throw ctx.fail.missingCredential("Set a GitKraken access token in Settings or GITKRAKEN_API_TOKEN.");
|
|
46
|
+
if (!/^[\x21-\x7e]{1,16384}$/.test(token)) {
|
|
47
|
+
throw ctx.fail.authenticationExpired("Invalid GitKraken token. Paste only the value after Bearer.");
|
|
48
|
+
}
|
|
49
|
+
const organization = _optionalChain([
|
|
50
|
+
ctx,
|
|
51
|
+
"access",
|
|
52
|
+
(_) => _.settings,
|
|
53
|
+
"access",
|
|
54
|
+
(_2) => _2.get,
|
|
55
|
+
"call",
|
|
56
|
+
(_3) => _3("GITKRAKEN_ORG_ID"),
|
|
57
|
+
"optionalAccess",
|
|
58
|
+
(_4) => _4.trim,
|
|
59
|
+
"call",
|
|
60
|
+
(_5) => _5(),
|
|
61
|
+
]);
|
|
62
|
+
if (organization && !/^[\x21-\x7e]{1,256}$/.test(organization)) {
|
|
63
|
+
throw ctx.fail.permissionDenied("Invalid GitKraken organization ID. Enter a single ID without whitespace.");
|
|
64
|
+
}
|
|
65
|
+
const headers = {
|
|
66
|
+
"Client-Name": "CodexBar",
|
|
67
|
+
"Client-Version": ctx.settings.get("CLIENT_VERSION") || "0.0.0",
|
|
68
|
+
"User-Agent": "CodexBar",
|
|
69
|
+
};
|
|
70
|
+
if (organization) headers["gk-org-id"] = organization;
|
|
71
|
+
const response = await ctx.http.get("https://api.gitkraken.dev/v1/ai-tasks/usage", { headers });
|
|
72
|
+
if (response.status === 401)
|
|
73
|
+
throw ctx.fail.authenticationExpired("GitKraken access token expired or was rejected.");
|
|
74
|
+
if (response.status === 403)
|
|
75
|
+
throw ctx.fail.permissionDenied("GitKraken denied access to this account or organization.");
|
|
76
|
+
if (response.status === 429) {
|
|
77
|
+
const delay = Number(_nullishCoalesce(response.headers["retry-after"], () => 1));
|
|
78
|
+
throw ctx.fail.rateLimited("GitKraken rate limited usage requests.", {
|
|
79
|
+
retryAfterSeconds: Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 10) : 1,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (response.status >= 500) throw ctx.fail.providerUnavailable("GitKraken usage is temporarily unavailable.");
|
|
83
|
+
if (response.status !== 200) throw ctx.fail.apiFailure(`GitKraken returned HTTP ${response.status}.`);
|
|
84
|
+
|
|
85
|
+
function fail() {
|
|
86
|
+
throw ctx.fail.parseFailure("GitKraken returned an unrecognized usage response.");
|
|
87
|
+
}
|
|
88
|
+
function quota(value) {
|
|
89
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
90
|
+
const { used, limit } = value;
|
|
91
|
+
if (
|
|
92
|
+
typeof used !== "number" ||
|
|
93
|
+
!Number.isFinite(used) ||
|
|
94
|
+
used < 0 ||
|
|
95
|
+
typeof limit !== "number" ||
|
|
96
|
+
!Number.isFinite(limit) ||
|
|
97
|
+
(limit < 0 && limit !== -1) ||
|
|
98
|
+
(limit > 0 && !Number.isFinite((used / limit) * 100))
|
|
99
|
+
)
|
|
100
|
+
return null;
|
|
101
|
+
return { used, limit };
|
|
102
|
+
}
|
|
103
|
+
let body;
|
|
104
|
+
try {
|
|
105
|
+
body = JSON.parse(response.bodyText);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
void error;
|
|
108
|
+
return fail();
|
|
109
|
+
}
|
|
110
|
+
const data = _optionalChain([body, "optionalAccess", (_6) => _6.data]);
|
|
111
|
+
const personal = quota(data);
|
|
112
|
+
if (_optionalChain([body, "optionalAccess", (_7) => _7.error]) != null || !personal) return fail();
|
|
113
|
+
if (
|
|
114
|
+
typeof data.resetsOn !== "string" ||
|
|
115
|
+
!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(data.resetsOn)
|
|
116
|
+
)
|
|
117
|
+
return fail();
|
|
118
|
+
const resetsAt = new Date(data.resetsOn);
|
|
119
|
+
if (!Number.isFinite(resetsAt.getTime())) return fail();
|
|
120
|
+
const pool = quota(data.organization);
|
|
121
|
+
const number = (value) => ctx.format.number(value, { maximumFractionDigits: 2 });
|
|
122
|
+
function description(value) {
|
|
123
|
+
if (value.limit <= 0)
|
|
124
|
+
return `${number(value.used)} credits used · ${value.limit === -1 ? "Unlimited" : "No allowance"}`;
|
|
125
|
+
return `${number(value.used)} / ${number(value.limit)} credits used`;
|
|
126
|
+
}
|
|
127
|
+
const rows = [{ label: "Personal", value: description(personal) }];
|
|
128
|
+
if (pool) {
|
|
129
|
+
rows.push({ label: "Shared pool", value: description(pool) });
|
|
130
|
+
// sharedUsed is a slice of the pool, never additional personal usage.
|
|
131
|
+
const shared = data.sharedUsed;
|
|
132
|
+
if (typeof shared === "number" && Number.isFinite(shared) && shared >= 0 && shared <= pool.used) {
|
|
133
|
+
rows.push({ label: "Your shared usage", value: `${number(shared)} credits` });
|
|
134
|
+
rows.push({ label: "Rest of organization", value: `${number(pool.used - shared)} credits` });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (personal.limit <= 0 && (!pool || pool.limit <= 0)) {
|
|
138
|
+
rows.push({ label: "Reset", value: resetsAt.toISOString() });
|
|
139
|
+
}
|
|
140
|
+
function window(value) {
|
|
141
|
+
return value && value.limit > 0
|
|
142
|
+
? { usedPercent: ctx.pct(value.used, value.limit), windowMinutes: 10080, resetsAt }
|
|
143
|
+
: null;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
primary: window(personal),
|
|
147
|
+
secondary: window(pool),
|
|
148
|
+
details: [{ title: "Weekly usage", rows }],
|
|
149
|
+
identity: { loginMethod: "API" },
|
|
150
|
+
dataConfidence: "exact",
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "gitkraken",
|
|
4
|
+
"name": "GitKraken AI",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"enabledByDefault": false,
|
|
8
|
+
"supportedModes": [
|
|
9
|
+
"api"
|
|
10
|
+
],
|
|
11
|
+
"autoMode": "api",
|
|
12
|
+
"usageDashboardUrl": "https://gitkraken.dev/account#ai-usage",
|
|
13
|
+
"envVars": [
|
|
14
|
+
"GITKRAKEN_API_TOKEN",
|
|
15
|
+
"GITKRAKEN_ORG_ID"
|
|
16
|
+
],
|
|
17
|
+
"upstream": {
|
|
18
|
+
"repository": "https://github.com/steipete/CodexBar",
|
|
19
|
+
"revision": "78ba5a1e65e6859da59a21b799df95ab91bc0b89",
|
|
20
|
+
"path": "Sources/CodexBarCore/Resources/Plugins/gitkraken.js",
|
|
21
|
+
"sha256": "c583890b74fc4c054cd818e560b30a17dd716c7611b554bd5b559f533a824ca2",
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Grok</title><path d="M6.469 8.776L16.512 23h-4.464L2.005 8.776H6.47zm-.004 7.9l2.233 3.164L6.467 23H2l4.465-6.324zM22 2.582V23h-3.659V7.764L22 2.582zM22 1l-9.952 14.095-2.233-3.163L17.533 1H22z"></path></svg>
|
package/plugins/grok/icon.svg
CHANGED
|
File without changes
|