@hashimkarim/usagestat-linux-x64-gnu 2.0.0-alpha.1
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 +57744 -0
- package/native-manifest.json +1414 -0
- package/package.json +254 -0
- package/plugins/abacus-ai/icon.svg +18 -0
- package/plugins/abacus-ai/plugin.js +121 -0
- package/plugins/abacus-ai/plugin.json +17 -0
- package/plugins/alibaba/icon-color.svg +1 -0
- package/plugins/alibaba/icon.svg +1 -0
- package/plugins/alibaba/plugin.js +322 -0
- package/plugins/alibaba/plugin.json +20 -0
- package/plugins/alibaba-token-plan/icon-color.svg +1 -0
- package/plugins/alibaba-token-plan/icon.svg +1 -0
- package/plugins/alibaba-token-plan/plugin.js +544 -0
- package/plugins/alibaba-token-plan/plugin.json +19 -0
- package/plugins/amp/icon-color.svg +1 -0
- package/plugins/amp/icon-conflict.svg +6 -0
- package/plugins/amp/icon.svg +1 -0
- package/plugins/amp/plugin.js +175 -0
- package/plugins/amp/plugin.json +16 -0
- package/plugins/antigravity/icon-color.svg +1 -0
- package/plugins/antigravity/icon-conflict.svg +3 -0
- package/plugins/antigravity/icon.svg +1 -0
- package/plugins/antigravity/plugin.js +739 -0
- package/plugins/antigravity/plugin.json +31 -0
- package/plugins/antigravity-cli/icon.svg +3 -0
- package/plugins/antigravity-cli/plugin.js +570 -0
- package/plugins/antigravity-cli/plugin.json +16 -0
- package/plugins/antigravity-ide/icon.svg +3 -0
- package/plugins/antigravity-ide/plugin.js +257 -0
- package/plugins/antigravity-ide/plugin.json +29 -0
- package/plugins/augment/icon.svg +14 -0
- package/plugins/augment/plugin.js +59 -0
- package/plugins/augment/plugin.json +18 -0
- package/plugins/aws-bedrock/icon-color.svg +1 -0
- package/plugins/aws-bedrock/icon.svg +1 -0
- package/plugins/aws-bedrock/plugin.js +206 -0
- package/plugins/aws-bedrock/plugin.json +18 -0
- package/plugins/azure-openai/icon-color.svg +1 -0
- package/plugins/azure-openai/icon.svg +1 -0
- package/plugins/azure-openai/plugin.js +145 -0
- package/plugins/azure-openai/plugin.json +17 -0
- package/plugins/chutes/icon.svg +7 -0
- package/plugins/chutes/plugin.js +236 -0
- package/plugins/chutes/plugin.json +19 -0
- package/plugins/claude/icon-color.svg +1 -0
- package/plugins/claude/icon.svg +1 -0
- package/plugins/claude/plugin.js +1723 -0
- package/plugins/claude/plugin.json +30 -0
- package/plugins/codebuff/icon.svg +92 -0
- package/plugins/codebuff/plugin.js +113 -0
- package/plugins/codebuff/plugin.json +17 -0
- package/plugins/codex/icon-color.svg +5 -0
- package/plugins/codex/icon.svg +1 -0
- package/plugins/codex/plugin.js +1009 -0
- package/plugins/codex/plugin.json +82 -0
- package/plugins/command-code/icon.svg +3 -0
- package/plugins/command-code/plugin.js +184 -0
- package/plugins/command-code/plugin.json +20 -0
- package/plugins/copilot/icon-color.svg +1 -0
- package/plugins/copilot/icon.svg +1 -0
- package/plugins/copilot/plugin.js +684 -0
- package/plugins/copilot/plugin.json +21 -0
- package/plugins/crof/icon.svg +3 -0
- package/plugins/crof/plugin.js +53 -0
- package/plugins/crof/plugin.json +17 -0
- package/plugins/crofai/icon.svg +16 -0
- package/plugins/crofai/plugin.js +81 -0
- package/plugins/crofai/plugin.json +18 -0
- package/plugins/cursor/icon-conflict.svg +3 -0
- package/plugins/cursor/icon.svg +1 -0
- package/plugins/cursor/plugin.js +1379 -0
- package/plugins/cursor/plugin.json +23 -0
- package/plugins/cursor-nightly/icon.png +0 -0
- package/plugins/cursor-nightly/plugin.js +1379 -0
- package/plugins/cursor-nightly/plugin.json +23 -0
- package/plugins/deepgram/icon.svg +1 -0
- package/plugins/deepgram/plugin.js +20 -0
- package/plugins/deepgram/plugin.json +17 -0
- package/plugins/deepseek/icon-color.svg +1 -0
- package/plugins/deepseek/icon.svg +1 -0
- package/plugins/deepseek/plugin.js +71 -0
- package/plugins/deepseek/plugin.json +17 -0
- package/plugins/devin/icon.svg +5 -0
- package/plugins/devin/plugin.js +341 -0
- package/plugins/devin/plugin.json +16 -0
- package/plugins/doubao/icon-color.svg +1 -0
- package/plugins/doubao/icon.svg +1 -0
- package/plugins/doubao/plugin.js +92 -0
- package/plugins/doubao/plugin.json +17 -0
- package/plugins/droid/icon.svg +1 -0
- package/plugins/droid/plugin.json +20 -0
- package/plugins/elevenlabs/icon.svg +4 -0
- package/plugins/elevenlabs/plugin.js +48 -0
- package/plugins/elevenlabs/plugin.json +18 -0
- package/plugins/factory/icon-conflict.svg +1 -0
- package/plugins/factory/icon.svg +1 -0
- package/plugins/factory/plugin.js +487 -0
- package/plugins/factory/plugin.json +15 -0
- package/plugins/fireworks-ai/icon.svg +6 -0
- package/plugins/fireworks-ai/plugin.js +370 -0
- package/plugins/fireworks-ai/plugin.json +19 -0
- package/plugins/gemini/icon-color.svg +1 -0
- package/plugins/gemini/icon.svg +1 -0
- package/plugins/gemini/plugin.js +460 -0
- package/plugins/gemini/plugin.json +16 -0
- package/plugins/grok/icon.svg +1 -0
- package/plugins/grok/plugin.js +297 -0
- package/plugins/grok/plugin.json +19 -0
- package/plugins/groqcloud/icon.svg +1 -0
- package/plugins/groqcloud/plugin.js +117 -0
- package/plugins/groqcloud/plugin.json +19 -0
- package/plugins/jetbrains-ai-assistant/icon-color.svg +14 -0
- package/plugins/jetbrains-ai-assistant/icon-conflict.svg +3 -0
- package/plugins/jetbrains-ai-assistant/icon.svg +14 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +313 -0
- package/plugins/jetbrains-ai-assistant/plugin.json +16 -0
- package/plugins/kilo/icon.svg +1 -0
- package/plugins/kilo/plugin.js +111 -0
- package/plugins/kilo/plugin.json +19 -0
- package/plugins/kimi/icon-color.svg +1 -0
- package/plugins/kimi/icon-conflict.svg +3 -0
- package/plugins/kimi/icon.svg +1 -0
- package/plugins/kimi/plugin.js +358 -0
- package/plugins/kimi/plugin.json +15 -0
- package/plugins/kimi-k2/icon-color.svg +1 -0
- package/plugins/kimi-k2/icon.svg +1 -0
- package/plugins/kimi-k2/plugin.js +58 -0
- package/plugins/kimi-k2/plugin.json +18 -0
- package/plugins/kiro/icon-color.svg +5 -0
- package/plugins/kiro/icon-conflict.svg +1 -0
- package/plugins/kiro/icon.svg +4 -0
- package/plugins/kiro/plugin.js +390 -0
- package/plugins/kiro/plugin.json +16 -0
- package/plugins/litellm/icon.svg +16 -0
- package/plugins/litellm/plugin.js +199 -0
- package/plugins/litellm/plugin.json +16 -0
- package/plugins/llm-proxy/icon.svg +1 -0
- package/plugins/llm-proxy/plugin.js +40 -0
- package/plugins/llm-proxy/plugin.json +17 -0
- package/plugins/manus/icon.svg +6 -0
- package/plugins/manus/plugin.js +50 -0
- package/plugins/manus/plugin.json +17 -0
- package/plugins/mimo/icon.svg +1 -0
- package/plugins/mimo/plugin.js +177 -0
- package/plugins/mimo/plugin.json +19 -0
- package/plugins/minimax/icon-color.svg +1 -0
- package/plugins/minimax/icon-conflict.svg +4 -0
- package/plugins/minimax/icon.svg +1 -0
- package/plugins/minimax/plugin.js +453 -0
- package/plugins/minimax/plugin.json +14 -0
- package/plugins/mistral/icon-color.svg +1 -0
- package/plugins/mistral/icon.svg +1 -0
- package/plugins/mistral/plugin.js +152 -0
- package/plugins/mistral/plugin.json +18 -0
- package/plugins/moonshot/icon.svg +1 -0
- package/plugins/moonshot/plugin.js +43 -0
- package/plugins/moonshot/plugin.json +16 -0
- package/plugins/nanogpt/icon-color.svg +17 -0
- package/plugins/nanogpt/icon.svg +10 -0
- package/plugins/nanogpt/plugin.js +80 -0
- package/plugins/nanogpt/plugin.json +17 -0
- package/plugins/neuralwatt/icon.svg +3 -0
- package/plugins/neuralwatt/plugin.js +152 -0
- package/plugins/neuralwatt/plugin.json +17 -0
- package/plugins/ollama/icon.svg +1 -0
- package/plugins/ollama/plugin.js +92 -0
- package/plugins/ollama/plugin.json +20 -0
- package/plugins/openai-api/icon.svg +1 -0
- package/plugins/openai-api/plugin.js +533 -0
- package/plugins/openai-api/plugin.json +26 -0
- package/plugins/opencode/icon.svg +1 -0
- package/plugins/opencode/plugin.js +342 -0
- package/plugins/opencode/plugin.json +19 -0
- package/plugins/opencode-go/icon-conflict.svg +1 -0
- package/plugins/opencode-go/icon.svg +1 -0
- package/plugins/opencode-go/plugin.js +617 -0
- package/plugins/opencode-go/plugin.json +50 -0
- package/plugins/openrouter/icon.svg +1 -0
- package/plugins/openrouter/plugin.js +156 -0
- package/plugins/openrouter/plugin.json +8 -0
- package/plugins/perplexity/icon-color.svg +1 -0
- package/plugins/perplexity/icon-conflict.svg +1 -0
- package/plugins/perplexity/icon.svg +1 -0
- package/plugins/perplexity/plugin.js +425 -0
- package/plugins/perplexity/plugin.json +19 -0
- package/plugins/poe/icon.svg +1 -0
- package/plugins/poe/plugin.js +190 -0
- package/plugins/poe/plugin.json +19 -0
- package/plugins/stepfun/icon-color.svg +1 -0
- package/plugins/stepfun/icon.svg +1 -0
- package/plugins/stepfun/plugin.js +155 -0
- package/plugins/stepfun/plugin.json +18 -0
- package/plugins/synthetic/icon-conflict.svg +12 -0
- package/plugins/synthetic/icon.svg +14 -0
- package/plugins/synthetic/plugin.js +331 -0
- package/plugins/synthetic/plugin.json +23 -0
- package/plugins/t3chat/icon-color.svg +28 -0
- package/plugins/t3chat/icon.svg +4 -0
- package/plugins/t3chat/plugin.js +243 -0
- package/plugins/t3chat/plugin.json +20 -0
- package/plugins/venice/icon-color.svg +1 -0
- package/plugins/venice/icon.svg +1 -0
- package/plugins/venice/plugin.js +69 -0
- package/plugins/venice/plugin.json +16 -0
- package/plugins/vertex-ai/icon-color.svg +1 -0
- package/plugins/vertex-ai/icon.svg +1 -0
- package/plugins/vertex-ai/plugin.js +172 -0
- package/plugins/vertex-ai/plugin.json +18 -0
- package/plugins/warp/icon-color.svg +10 -0
- package/plugins/warp/icon.svg +6 -0
- package/plugins/warp/plugin.js +108 -0
- package/plugins/warp/plugin.json +18 -0
- package/plugins/windsurf/icon-conflict.svg +3 -0
- package/plugins/windsurf/icon.svg +1 -0
- package/plugins/windsurf/plugin.js +208 -0
- package/plugins/windsurf/plugin.json +16 -0
- package/plugins/zai/icon-conflict.svg +5 -0
- package/plugins/zai/icon.svg +1 -0
- package/plugins/zai/plugin.js +188 -0
- package/plugins/zai/plugin.json +16 -0
- package/plugins/zed/icon.svg +4 -0
- package/plugins/zed/plugin.js +270 -0
- package/plugins/zed/plugin.json +19 -0
- package/usagestat +0 -0
- package/usagestatd +0 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const AUTH_PATH = "~/.grok/auth.json";
|
|
3
|
+
const BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing";
|
|
4
|
+
const SETTINGS_URL = "https://cli-chat-proxy.grok.com/v1/settings";
|
|
5
|
+
const REFRESH_URL = "https://auth.x.ai/oauth2/token";
|
|
6
|
+
const DEFAULT_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
|
|
7
|
+
const TOKEN_AUTH_HEADER = "xai-grok-cli";
|
|
8
|
+
const AUTH_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
9
|
+
const LOGIN_HINT = "Grok auth expired. Run `grok login` again.";
|
|
10
|
+
|
|
11
|
+
function readJson(ctx, path) {
|
|
12
|
+
if (!ctx.host.fs.exists(path)) return null;
|
|
13
|
+
try {
|
|
14
|
+
return ctx.util.tryParseJson(ctx.host.fs.readText(path));
|
|
15
|
+
} catch (_) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function entryExpiresAtMs(ctx, entry) {
|
|
21
|
+
if (!entry || typeof entry !== "object") return null;
|
|
22
|
+
if (entry.expires_at) return ctx.util.parseDateMs(entry.expires_at);
|
|
23
|
+
if (entry.expires) return ctx.util.parseDateMs(entry.expires);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function tokenExpiresAtMs(ctx, token) {
|
|
28
|
+
const payload = ctx.jwt.decodePayload(token);
|
|
29
|
+
if (!payload || typeof payload.exp !== "number") return null;
|
|
30
|
+
return payload.exp * 1000;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function needsRefresh(ctx, entry, token, nowMs) {
|
|
34
|
+
const entryMs = entryExpiresAtMs(ctx, entry);
|
|
35
|
+
const tokenMs = tokenExpiresAtMs(ctx, token);
|
|
36
|
+
const entryNeedsRefresh = entryMs !== null && ctx.util.needsRefreshByExpiry({
|
|
37
|
+
nowMs,
|
|
38
|
+
expiresAtMs: entryMs,
|
|
39
|
+
bufferMs: AUTH_REFRESH_BUFFER_MS,
|
|
40
|
+
});
|
|
41
|
+
const tokenNeedsRefresh = tokenMs !== null && ctx.util.needsRefreshByExpiry({
|
|
42
|
+
nowMs,
|
|
43
|
+
expiresAtMs: tokenMs,
|
|
44
|
+
bufferMs: AUTH_REFRESH_BUFFER_MS,
|
|
45
|
+
});
|
|
46
|
+
return entryNeedsRefresh || tokenNeedsRefresh;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isExpired(ctx, entry, token, nowMs) {
|
|
50
|
+
const entryMs = entryExpiresAtMs(ctx, entry);
|
|
51
|
+
const tokenMs = tokenExpiresAtMs(ctx, token);
|
|
52
|
+
const expiresAtMs = tokenMs !== null ? tokenMs : entryMs;
|
|
53
|
+
if (expiresAtMs === null) return false;
|
|
54
|
+
return nowMs >= expiresAtMs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readRefreshToken(entry) {
|
|
58
|
+
if (!entry || typeof entry !== "object") return "";
|
|
59
|
+
const refreshToken = typeof entry.refresh_token === "string" ? entry.refresh_token.trim() : "";
|
|
60
|
+
if (refreshToken) return refreshToken;
|
|
61
|
+
return typeof entry.refresh === "string" ? entry.refresh.trim() : "";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function readClientId(entryKey, entry) {
|
|
65
|
+
if (entry && typeof entry.oidc_client_id === "string" && entry.oidc_client_id.trim()) {
|
|
66
|
+
return entry.oidc_client_id.trim();
|
|
67
|
+
}
|
|
68
|
+
const parts = String(entryKey || "").split("::");
|
|
69
|
+
const fromKey = parts.length > 1 ? parts[parts.length - 1].trim() : "";
|
|
70
|
+
return fromKey || DEFAULT_CLIENT_ID;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function nowMs(ctx) {
|
|
74
|
+
return ctx.util.parseDateMs(ctx.nowIso) || Date.now();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function refreshAuth(ctx, auth, entryKey, entry) {
|
|
78
|
+
const refreshToken = readRefreshToken(entry);
|
|
79
|
+
if (!refreshToken) {
|
|
80
|
+
ctx.host.log.warn("Grok refresh skipped: no refresh token");
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ctx.host.log.info("attempting Grok auth refresh");
|
|
85
|
+
try {
|
|
86
|
+
const resp = ctx.util.request({
|
|
87
|
+
method: "POST",
|
|
88
|
+
url: REFRESH_URL,
|
|
89
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
90
|
+
bodyText:
|
|
91
|
+
"grant_type=refresh_token" +
|
|
92
|
+
"&client_id=" + encodeURIComponent(readClientId(entryKey, entry)) +
|
|
93
|
+
"&refresh_token=" + encodeURIComponent(refreshToken),
|
|
94
|
+
timeoutMs: 15000,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (resp.status === 400 || resp.status === 401 || resp.status === 403) {
|
|
98
|
+
const body = ctx.util.tryParseJson(resp.bodyText);
|
|
99
|
+
const code = body && ((body.error && body.error.code) || body.error || body.code);
|
|
100
|
+
ctx.host.log.error("Grok auth refresh failed: status=" + resp.status + " code=" + String(code));
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
104
|
+
ctx.host.log.warn("Grok auth refresh returned status: " + resp.status);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const body = ctx.util.tryParseJson(resp.bodyText);
|
|
109
|
+
if (!body || typeof body.access_token !== "string" || !body.access_token.trim()) {
|
|
110
|
+
ctx.host.log.warn("Grok auth refresh response missing access_token");
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const accessToken = body.access_token.trim();
|
|
115
|
+
entry.key = accessToken;
|
|
116
|
+
if (typeof body.refresh_token === "string" && body.refresh_token.trim()) {
|
|
117
|
+
entry.refresh_token = body.refresh_token.trim();
|
|
118
|
+
}
|
|
119
|
+
if (typeof body.id_token === "string" && body.id_token.trim()) {
|
|
120
|
+
entry.id_token = body.id_token.trim();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const refreshedAtMs = nowMs(ctx);
|
|
124
|
+
const expiresIn = Number(body.expires_in);
|
|
125
|
+
const tokenExpiryMs = tokenExpiresAtMs(ctx, accessToken);
|
|
126
|
+
const expiresAtMs = Number.isFinite(expiresIn) && expiresIn > 0
|
|
127
|
+
? refreshedAtMs + expiresIn * 1000
|
|
128
|
+
: tokenExpiryMs || refreshedAtMs + 3600 * 1000;
|
|
129
|
+
entry.expires_at = new Date(expiresAtMs).toISOString();
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
ctx.host.fs.writeText(AUTH_PATH, JSON.stringify(auth, null, 2));
|
|
133
|
+
ctx.host.log.info("Grok auth refresh succeeded, token persisted");
|
|
134
|
+
} catch (e) {
|
|
135
|
+
ctx.host.log.warn("Grok auth refresh succeeded but failed to save auth: " + String(e));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return accessToken;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
if (typeof e === "string") throw e;
|
|
141
|
+
ctx.host.log.error("Grok auth refresh exception: " + String(e));
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function loadAuth(ctx) {
|
|
147
|
+
const auth = readJson(ctx, AUTH_PATH);
|
|
148
|
+
if (!auth || typeof auth !== "object") {
|
|
149
|
+
throw "Grok not logged in. Run `grok login`.";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const currentMs = nowMs(ctx);
|
|
153
|
+
let expiredCandidate = false;
|
|
154
|
+
const keys = Object.keys(auth);
|
|
155
|
+
for (let i = 0; i < keys.length; i++) {
|
|
156
|
+
const entryKey = keys[i];
|
|
157
|
+
const entry = auth[entryKey];
|
|
158
|
+
if (!entry || typeof entry !== "object") continue;
|
|
159
|
+
const token = typeof entry.key === "string" ? entry.key.trim() : "";
|
|
160
|
+
if (!token) continue;
|
|
161
|
+
if (needsRefresh(ctx, entry, token, currentMs)) {
|
|
162
|
+
const refreshed = refreshAuth(ctx, auth, entryKey, entry);
|
|
163
|
+
if (refreshed) return { auth, entryKey, entry, token: refreshed };
|
|
164
|
+
if (!isExpired(ctx, entry, token, currentMs)) {
|
|
165
|
+
ctx.host.log.warn("Grok refresh failed, trying existing access token");
|
|
166
|
+
return { auth, entryKey, entry, token };
|
|
167
|
+
}
|
|
168
|
+
expiredCandidate = true;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
return { auth, entryKey, entry, token };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (expiredCandidate) {
|
|
175
|
+
throw LOGIN_HINT;
|
|
176
|
+
}
|
|
177
|
+
throw "Grok auth invalid. Run `grok login` again.";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function unitsValue(obj) {
|
|
181
|
+
if (!obj || typeof obj !== "object") return null;
|
|
182
|
+
const n = Number(obj.val);
|
|
183
|
+
return Number.isFinite(n) ? n : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function clampPercent(value) {
|
|
187
|
+
const n = Number(value);
|
|
188
|
+
if (!Number.isFinite(n)) return 0;
|
|
189
|
+
if (n < 0) return 0;
|
|
190
|
+
if (n > 100) return 100;
|
|
191
|
+
return n;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function fetchBillingResponse(ctx, token) {
|
|
195
|
+
try {
|
|
196
|
+
return ctx.util.request({
|
|
197
|
+
method: "GET",
|
|
198
|
+
url: BILLING_URL,
|
|
199
|
+
headers: {
|
|
200
|
+
Authorization: "Bearer " + token,
|
|
201
|
+
"X-XAI-Token-Auth": TOKEN_AUTH_HEADER,
|
|
202
|
+
Accept: "application/json",
|
|
203
|
+
"User-Agent": "OpenUsage",
|
|
204
|
+
},
|
|
205
|
+
timeoutMs: 10000,
|
|
206
|
+
});
|
|
207
|
+
} catch (_) {
|
|
208
|
+
throw "Grok billing request failed. Check your connection.";
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function parseBilling(ctx, resp) {
|
|
213
|
+
if (ctx.util.isAuthStatus(resp.status)) {
|
|
214
|
+
throw LOGIN_HINT;
|
|
215
|
+
}
|
|
216
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
217
|
+
throw "Grok billing request failed (HTTP " + String(resp.status) + "). Try again later.";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const data = ctx.util.tryParseJson(resp.bodyText);
|
|
221
|
+
if (!data) {
|
|
222
|
+
throw "Grok billing response changed.";
|
|
223
|
+
}
|
|
224
|
+
return data;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function fetchPlanName(ctx, token) {
|
|
228
|
+
try {
|
|
229
|
+
const resp = ctx.util.request({
|
|
230
|
+
method: "GET",
|
|
231
|
+
url: SETTINGS_URL,
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: "Bearer " + token,
|
|
234
|
+
"X-XAI-Token-Auth": TOKEN_AUTH_HEADER,
|
|
235
|
+
Accept: "application/json",
|
|
236
|
+
"User-Agent": "OpenUsage",
|
|
237
|
+
},
|
|
238
|
+
timeoutMs: 10000,
|
|
239
|
+
});
|
|
240
|
+
if (resp.status < 200 || resp.status >= 300) return null;
|
|
241
|
+
const data = ctx.util.tryParseJson(resp.bodyText);
|
|
242
|
+
const plan = data && data.subscription_tier_display;
|
|
243
|
+
return typeof plan === "string" && plan.trim() ? plan.trim() : null;
|
|
244
|
+
} catch (_) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function probe(ctx) {
|
|
250
|
+
const auth = loadAuth(ctx);
|
|
251
|
+
const billingResp = ctx.util.retryOnceOnAuth({
|
|
252
|
+
request: (token) => fetchBillingResponse(ctx, token || auth.token),
|
|
253
|
+
refresh: () => {
|
|
254
|
+
const refreshed = refreshAuth(ctx, auth.auth, auth.entryKey, auth.entry);
|
|
255
|
+
if (refreshed) auth.token = refreshed;
|
|
256
|
+
return refreshed;
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
const data = parseBilling(ctx, billingResp);
|
|
260
|
+
const config = data && data.config;
|
|
261
|
+
if (!config || typeof config !== "object") {
|
|
262
|
+
throw "Grok billing response changed.";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const usedUnits = unitsValue(config.used);
|
|
266
|
+
const limitUnits = unitsValue(config.monthlyLimit);
|
|
267
|
+
const onDemandCapUnits = unitsValue(config.onDemandCap);
|
|
268
|
+
if (usedUnits === null || limitUnits === null || limitUnits <= 0 || onDemandCapUnits === null) {
|
|
269
|
+
throw "Grok billing response changed.";
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const resetsAt = ctx.util.toIso(config.billingPeriodEnd);
|
|
273
|
+
if (!resetsAt) {
|
|
274
|
+
throw "Grok billing response changed.";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const usedPercent = clampPercent((usedUnits / limitUnits) * 100);
|
|
278
|
+
const lines = [
|
|
279
|
+
ctx.line.progress({
|
|
280
|
+
label: "Credits used",
|
|
281
|
+
used: usedPercent,
|
|
282
|
+
limit: 100,
|
|
283
|
+
format: { kind: "percent" },
|
|
284
|
+
resetsAt,
|
|
285
|
+
}),
|
|
286
|
+
ctx.line.badge({
|
|
287
|
+
label: "Pay as you go",
|
|
288
|
+
text: onDemandCapUnits > 0 ? String(onDemandCapUnits) + " cap" : "Disabled",
|
|
289
|
+
color: onDemandCapUnits > 0 ? "#22c55e" : "#a3a3a3",
|
|
290
|
+
}),
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
return { plan: fetchPlanName(ctx, auth.token), lines };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
globalThis.__openusage_plugin = { id: "grok", probe };
|
|
297
|
+
})();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "grok",
|
|
4
|
+
"name": "Grok",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"icon": "icon.svg",
|
|
8
|
+
"brandColor": "#000000",
|
|
9
|
+
"links": [
|
|
10
|
+
{ "label": "Usage", "url": "https://grok.com/?_s=usage" }
|
|
11
|
+
],
|
|
12
|
+
"supportedModes": ["cli", "local"],
|
|
13
|
+
"autoMode": "cli",
|
|
14
|
+
"webUrl": "https://grok.com",
|
|
15
|
+
"lines": [
|
|
16
|
+
{ "type": "progress", "label": "Credits used", "scope": "overview", "primaryOrder": 1 },
|
|
17
|
+
{ "type": "badge", "label": "Pay as you go", "scope": "detail" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -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>Groq</title><path d="M12.036 2c-3.853-.035-7 3-7.036 6.781-.035 3.782 3.055 6.872 6.908 6.907h2.42v-2.566h-2.292c-2.407.028-4.38-1.866-4.408-4.23-.029-2.362 1.901-4.298 4.308-4.326h.1c2.407 0 4.358 1.915 4.365 4.278v6.305c0 2.342-1.944 4.25-4.323 4.279a4.375 4.375 0 01-3.033-1.252l-1.851 1.818A7 7 0 0012.029 22h.092c3.803-.056 6.858-3.083 6.879-6.816v-6.5C18.907 4.963 15.817 2 12.036 2z"></path></svg>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var DEFAULT_API_BASE = "https://api.groq.com/openai/v1";
|
|
3
|
+
var QUERIES = {
|
|
4
|
+
requests: "sum(model_project_id_status_code:requests:rate5m)",
|
|
5
|
+
inputTokens: "sum(model_project_id:tokens_in:rate5m)",
|
|
6
|
+
outputTokens: "sum(model_project_id:tokens_out:rate5m)",
|
|
7
|
+
cacheHits: "sum(model_project_id:prompt_cache_hits:rate5m)",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function trim(value) {
|
|
11
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function env(ctx, name) {
|
|
15
|
+
try {
|
|
16
|
+
return trim(ctx.host.env.get(name));
|
|
17
|
+
} catch (_) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setting(ctx, names) {
|
|
23
|
+
var settings = ctx.provider && ctx.provider.settings ? ctx.provider.settings : {};
|
|
24
|
+
for (var i = 0; i < names.length; i++) {
|
|
25
|
+
var value = trim(settings[names[i]]);
|
|
26
|
+
if (value) return value;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function apiKey(ctx) {
|
|
32
|
+
var key = setting(ctx, ["apiKey", "token"]) ||
|
|
33
|
+
(ctx.provider && trim(ctx.provider.apiKey)) ||
|
|
34
|
+
env(ctx, "GROQ_API_KEY") ||
|
|
35
|
+
env(ctx, "GROQCLOUD_API_KEY");
|
|
36
|
+
if (!key) throw "GroqCloud API key not found. Set GROQ_API_KEY or GROQCLOUD_API_KEY.";
|
|
37
|
+
return key;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function apiBase(ctx) {
|
|
41
|
+
return (setting(ctx, ["apiUrl", "apiBase", "baseUrl"]) || env(ctx, "GROQ_API_URL") || DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function prometheusValue(value) {
|
|
45
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
46
|
+
if (typeof value === "string" && value.trim()) {
|
|
47
|
+
var parsed = Number(value);
|
|
48
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseScalar(ctx, bodyText) {
|
|
54
|
+
var decoded = ctx.util.tryParseJson(bodyText);
|
|
55
|
+
if (!decoded || typeof decoded !== "object") throw "Failed to parse Groq metrics.";
|
|
56
|
+
if (decoded.status !== "success") {
|
|
57
|
+
throw (typeof decoded.error === "string" && decoded.error.trim()) || "Groq metrics query failed.";
|
|
58
|
+
}
|
|
59
|
+
var result = decoded.data && Array.isArray(decoded.data.result) ? decoded.data.result : [];
|
|
60
|
+
var total = 0;
|
|
61
|
+
for (var i = 0; i < result.length; i++) {
|
|
62
|
+
var series = result[i];
|
|
63
|
+
var values = series && Array.isArray(series.value) ? series.value : null;
|
|
64
|
+
if (!values || values.length === 0) continue;
|
|
65
|
+
var n = prometheusValue(values[values.length - 1]);
|
|
66
|
+
if (n !== null) total += n;
|
|
67
|
+
}
|
|
68
|
+
return total;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function queryScalar(ctx, base, key, query) {
|
|
72
|
+
var resp = ctx.util.request({
|
|
73
|
+
method: "GET",
|
|
74
|
+
url: base + "/metrics/prometheus/api/v1/query?query=" + encodeURIComponent(query),
|
|
75
|
+
headers: {
|
|
76
|
+
Authorization: "Bearer " + key,
|
|
77
|
+
Accept: "application/json",
|
|
78
|
+
},
|
|
79
|
+
timeoutMs: 15000,
|
|
80
|
+
});
|
|
81
|
+
if (ctx.util.isAuthStatus(resp.status)) throw "GroqCloud API key is invalid or unauthorized.";
|
|
82
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
83
|
+
throw "Groq metrics API returned HTTP " + resp.status + ".";
|
|
84
|
+
}
|
|
85
|
+
return parseScalar(ctx, resp.bodyText);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function fmt(value) {
|
|
89
|
+
if (value >= 100) return value.toFixed(0);
|
|
90
|
+
if (value >= 10) return value.toFixed(1);
|
|
91
|
+
return value.toFixed(2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function probe(ctx) {
|
|
95
|
+
var key = apiKey(ctx);
|
|
96
|
+
var base = apiBase(ctx);
|
|
97
|
+
var requestRate = queryScalar(ctx, base, key, QUERIES.requests);
|
|
98
|
+
var inputRate = queryScalar(ctx, base, key, QUERIES.inputTokens);
|
|
99
|
+
var outputRate = queryScalar(ctx, base, key, QUERIES.outputTokens);
|
|
100
|
+
var cacheRate = queryScalar(ctx, base, key, QUERIES.cacheHits);
|
|
101
|
+
var requestsPerMinute = requestRate * 60;
|
|
102
|
+
var tokensPerMinute = (inputRate + outputRate) * 60;
|
|
103
|
+
var cacheHitsPerMinute = cacheRate * 60;
|
|
104
|
+
return {
|
|
105
|
+
displayName: "GroqCloud",
|
|
106
|
+
source: "api",
|
|
107
|
+
plan: "Prometheus metrics",
|
|
108
|
+
lines: [
|
|
109
|
+
ctx.line.text({ label: "Requests", value: fmt(requestsPerMinute) + " req/min", subtitle: "5 minute rate" }),
|
|
110
|
+
ctx.line.text({ label: "Tokens", value: fmt(tokensPerMinute) + " tok/min", subtitle: "Input + output" }),
|
|
111
|
+
ctx.line.text({ label: "Cache hits", value: fmt(cacheHitsPerMinute) + " cache/min", subtitle: "Prompt cache" }),
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
globalThis.__openusage_plugin = { id: "groqcloud", probe: probe };
|
|
117
|
+
})();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "groqcloud",
|
|
4
|
+
"name": "GroqCloud",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"brandColor": "#F55036",
|
|
8
|
+
"links": [
|
|
9
|
+
{ "label": "Console", "url": "https://console.groq.com" },
|
|
10
|
+
{ "label": "Status", "url": "https://status.groq.com" }
|
|
11
|
+
],
|
|
12
|
+
"supportedModes": ["api"],
|
|
13
|
+
"autoMode": "api",
|
|
14
|
+
"lines": [
|
|
15
|
+
{ "type": "text", "label": "Requests", "scope": "overview", "primaryOrder": 1 },
|
|
16
|
+
{ "type": "text", "label": "Tokens", "scope": "detail" },
|
|
17
|
+
{ "type": "text", "label": "Cache hits", "scope": "detail" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 48 48">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="b" x1="4.75407" x2="70.6146" y1="8.63622" y2="59.748" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#955AE0"/>
|
|
5
|
+
<stop offset="1" stop-color="#4D67F0"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<clipPath id="a">
|
|
8
|
+
<path fill="#fff" d="M0 0h48v48H0z"/>
|
|
9
|
+
</clipPath>
|
|
10
|
+
</defs>
|
|
11
|
+
<g clip-path="url(#a)">
|
|
12
|
+
<path fill="url(#b)" d="M3.72781 8.16C8.04781 5.7 13.9878 4.5 19.0878 4.5c14.94 0 24.42 6.96 24.42 16.38 0 7.5-6.54 12.12-15.24 12.12-7.02 0-12.24-2.58-12.24-6 0-3.24 3.3-4.8 7.98-3l1.2-3.66c-7.26-2.34-13.26.96-13.26 6.66s7.08 9.78 16.56 9.78c11.28 0 19.5-6.24 19.5-15.9 0-12.06-11.4-20.88-28.92-20.88-5.64 0-11.99999 1.44-17.21999 4.26l1.86 3.9ZM44.2878 39.84c-4.32 2.46-10.26 3.66-15.36 3.66-14.94 0-24.41999-6.96-24.41999-16.38 0-7.5 6.53999-12.12 15.23999-12.12 7.02 0 12.24 2.58 12.24 6 0 3.24-3.3 4.8-7.98 3l-1.2 3.66c7.26 2.34 13.26-.96 13.26-6.66s-7.08-9.78-16.56-9.78C8.22781 11.22.007813 17.46.007813 27.12.007813 39.18 11.4078 48 28.9278 48c5.64 0 12-1.44 17.22-4.26l-1.86-3.9Z"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
|
2
|
+
<path fill="currentColor" d="M13.476 21.473c7.78-4.43 18.478-6.592 27.663-6.592 26.907 0 43.98 12.535 43.98 29.5 0 13.507-11.778 21.828-27.447 21.828-12.643 0-22.044-4.647-22.044-10.806 0-5.835 5.943-8.645 14.372-5.403l2.161-6.592c-13.075-4.214-23.88 1.73-23.88 11.995s12.75 17.613 29.823 17.613c20.315 0 35.12-11.238 35.12-28.635 0-21.72-20.532-37.604-52.085-37.604-10.157 0-21.611 2.593-31.013 7.672zm73.048 57.054c-7.78 4.43-18.478 6.592-27.663 6.592-26.907 0-43.98-12.535-43.98-29.5 0-13.507 11.778-21.828 27.447-21.828 12.643 0 22.044 4.647 22.044 10.806 0 5.835-5.943 8.645-14.372 5.403l-2.161 6.592c13.075 4.214 23.88-1.73 23.88-11.995S58.97 26.984 41.897 26.984c-20.315 0-35.12 11.238-35.12 28.635 0 21.72 20.532 37.604 52.085 37.604 10.157 0 21.611-2.593 31.013-7.672z" />
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 48 48">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="b" x1="4.75407" x2="70.6146" y1="8.63622" y2="59.748" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="currentColor"/>
|
|
5
|
+
<stop offset="1" stop-color="currentColor"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<clipPath id="a">
|
|
8
|
+
<path fill="currentColor" d="M0 0h48v48H0z"/>
|
|
9
|
+
</clipPath>
|
|
10
|
+
</defs>
|
|
11
|
+
<g clip-path="url(#a)">
|
|
12
|
+
<path fill="currentColor" d="M3.72781 8.16C8.04781 5.7 13.9878 4.5 19.0878 4.5c14.94 0 24.42 6.96 24.42 16.38 0 7.5-6.54 12.12-15.24 12.12-7.02 0-12.24-2.58-12.24-6 0-3.24 3.3-4.8 7.98-3l1.2-3.66c-7.26-2.34-13.26.96-13.26 6.66s7.08 9.78 16.56 9.78c11.28 0 19.5-6.24 19.5-15.9 0-12.06-11.4-20.88-28.92-20.88-5.64 0-11.99999 1.44-17.21999 4.26l1.86 3.9ZM44.2878 39.84c-4.32 2.46-10.26 3.66-15.36 3.66-14.94 0-24.41999-6.96-24.41999-16.38 0-7.5 6.53999-12.12 15.23999-12.12 7.02 0 12.24 2.58 12.24 6 0 3.24-3.3 4.8-7.98 3l-1.2 3.66c7.26 2.34 13.26-.96 13.26-6.66s-7.08-9.78-16.56-9.78C8.22781 11.22.007813 17.46.007813 27.12.007813 39.18 11.4078 48 28.9278 48c5.64 0 12-1.44 17.22-4.26l-1.86-3.9Z"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|