@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,453 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const GLOBAL_PRIMARY_USAGE_URL = "https://www.minimax.io/v1/token_plan/remains"
|
|
3
|
+
const GLOBAL_FALLBACK_USAGE_URLS = [
|
|
4
|
+
"https://www.minimax.io/v1/token_plan/remains",
|
|
5
|
+
]
|
|
6
|
+
const CN_PRIMARY_USAGE_URL = "https://api.minimaxi.com/v1/token_plan/remains"
|
|
7
|
+
const CN_FALLBACK_USAGE_URLS = ["https://api.minimaxi.com/v1/token_plan/remains"]
|
|
8
|
+
const GLOBAL_API_KEY_ENV_VARS = ["MINIMAX_API_KEY", "MINIMAX_API_TOKEN"]
|
|
9
|
+
const CN_API_KEY_ENV_VARS = ["MINIMAX_CN_API_KEY", "MINIMAX_API_KEY", "MINIMAX_API_TOKEN"]
|
|
10
|
+
const CODING_PLAN_WINDOW_MS = 5 * 60 * 60 * 1000
|
|
11
|
+
const CODING_PLAN_WINDOW_TOLERANCE_MS = 10 * 60 * 1000
|
|
12
|
+
// GLOBAL plan tiers (based on prompt limits)
|
|
13
|
+
const GLOBAL_PROMPT_LIMIT_TO_PLAN = {
|
|
14
|
+
100: "Starter",
|
|
15
|
+
300: "Plus",
|
|
16
|
+
1000: "Max",
|
|
17
|
+
2000: "Ultra",
|
|
18
|
+
}
|
|
19
|
+
// CN plan tiers (based on model call counts = prompts × 15)
|
|
20
|
+
// Starter: 40 prompts = 600, Plus: 100 prompts = 1500, Max: 300 prompts = 4500
|
|
21
|
+
const CN_PROMPT_LIMIT_TO_PLAN = {
|
|
22
|
+
600: "Starter",
|
|
23
|
+
1500: "Plus",
|
|
24
|
+
4500: "Max",
|
|
25
|
+
}
|
|
26
|
+
const MODEL_CALLS_PER_PROMPT = 15
|
|
27
|
+
|
|
28
|
+
function readString(value) {
|
|
29
|
+
if (typeof value !== "string") return null
|
|
30
|
+
const trimmed = value.trim()
|
|
31
|
+
return trimmed ? trimmed : null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readNumber(value) {
|
|
35
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
|
36
|
+
if (typeof value !== "string") return null
|
|
37
|
+
const trimmed = value.trim()
|
|
38
|
+
if (!trimmed) return null
|
|
39
|
+
const n = Number(trimmed)
|
|
40
|
+
return Number.isFinite(n) ? n : null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function pickFirstString(values) {
|
|
44
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
45
|
+
const value = readString(values[i])
|
|
46
|
+
if (value) return value
|
|
47
|
+
}
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizePlanName(value) {
|
|
52
|
+
const raw = readString(value)
|
|
53
|
+
if (!raw) return null
|
|
54
|
+
const compact = raw.replace(/\s+/g, " ").trim()
|
|
55
|
+
const withoutPrefix = compact.replace(/^minimax\s+coding\s+plan\b[:\-]?\s*/i, "").trim()
|
|
56
|
+
if (withoutPrefix) return withoutPrefix
|
|
57
|
+
if (/coding\s+plan/i.test(compact)) return "Coding Plan"
|
|
58
|
+
return compact
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function inferPlanNameFromLimit(totalCount, endpointSelection) {
|
|
62
|
+
const n = readNumber(totalCount)
|
|
63
|
+
if (n === null || n <= 0) return null
|
|
64
|
+
|
|
65
|
+
const normalized = Math.round(n)
|
|
66
|
+
if (endpointSelection === "CN") {
|
|
67
|
+
// CN totals are model-call counts; only exact known CN tiers should infer.
|
|
68
|
+
return CN_PROMPT_LIMIT_TO_PLAN[normalized] || null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (GLOBAL_PROMPT_LIMIT_TO_PLAN[normalized]) return GLOBAL_PROMPT_LIMIT_TO_PLAN[normalized]
|
|
72
|
+
|
|
73
|
+
if (normalized % MODEL_CALLS_PER_PROMPT !== 0) return null
|
|
74
|
+
const inferredPromptLimit = normalized / MODEL_CALLS_PER_PROMPT
|
|
75
|
+
return GLOBAL_PROMPT_LIMIT_TO_PLAN[inferredPromptLimit] || null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function epochToMs(epoch) {
|
|
79
|
+
const n = readNumber(epoch)
|
|
80
|
+
if (n === null) return null
|
|
81
|
+
return Math.abs(n) < 1e10 ? n * 1000 : n
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function inferRemainsMs(remainsRaw, endMs, nowMs) {
|
|
85
|
+
if (remainsRaw === null || remainsRaw <= 0) return null
|
|
86
|
+
|
|
87
|
+
const asSecondsMs = remainsRaw * 1000
|
|
88
|
+
const asMillisecondsMs = remainsRaw
|
|
89
|
+
|
|
90
|
+
// If end_time exists, infer remains_time unit by whichever aligns best.
|
|
91
|
+
if (endMs !== null) {
|
|
92
|
+
const toEndMs = endMs - nowMs
|
|
93
|
+
if (toEndMs > 0) {
|
|
94
|
+
const secDelta = Math.abs(asSecondsMs - toEndMs)
|
|
95
|
+
const msDelta = Math.abs(asMillisecondsMs - toEndMs)
|
|
96
|
+
return secDelta <= msDelta ? asSecondsMs : asMillisecondsMs
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Coding Plan resets every 5h. Use that constraint before defaulting.
|
|
101
|
+
const maxExpectedMs = CODING_PLAN_WINDOW_MS + CODING_PLAN_WINDOW_TOLERANCE_MS
|
|
102
|
+
const secondsLooksValid = asSecondsMs <= maxExpectedMs
|
|
103
|
+
const millisecondsLooksValid = asMillisecondsMs <= maxExpectedMs
|
|
104
|
+
|
|
105
|
+
if (secondsLooksValid && !millisecondsLooksValid) return asSecondsMs
|
|
106
|
+
if (millisecondsLooksValid && !secondsLooksValid) return asMillisecondsMs
|
|
107
|
+
if (secondsLooksValid && millisecondsLooksValid) return asSecondsMs
|
|
108
|
+
|
|
109
|
+
const secOverflow = Math.abs(asSecondsMs - maxExpectedMs)
|
|
110
|
+
const msOverflow = Math.abs(asMillisecondsMs - maxExpectedMs)
|
|
111
|
+
return secOverflow <= msOverflow ? asSecondsMs : asMillisecondsMs
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function loadApiKey(ctx, endpointSelection) {
|
|
115
|
+
const envVars = endpointSelection === "CN" ? CN_API_KEY_ENV_VARS : GLOBAL_API_KEY_ENV_VARS
|
|
116
|
+
for (let i = 0; i < envVars.length; i += 1) {
|
|
117
|
+
const name = envVars[i]
|
|
118
|
+
let value = null
|
|
119
|
+
try {
|
|
120
|
+
value = ctx.host.env.get(name)
|
|
121
|
+
} catch (e) {
|
|
122
|
+
ctx.host.log.warn("env read failed for " + name + ": " + String(e))
|
|
123
|
+
}
|
|
124
|
+
const key = readString(value)
|
|
125
|
+
if (key) {
|
|
126
|
+
ctx.host.log.info("api key loaded from " + name)
|
|
127
|
+
return { value: key, source: name }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getUsageUrls(endpointSelection) {
|
|
134
|
+
if (endpointSelection === "CN") {
|
|
135
|
+
return [CN_PRIMARY_USAGE_URL].concat(CN_FALLBACK_USAGE_URLS)
|
|
136
|
+
}
|
|
137
|
+
return [GLOBAL_PRIMARY_USAGE_URL].concat(GLOBAL_FALLBACK_USAGE_URLS)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function endpointAttempts(ctx) {
|
|
141
|
+
// AUTO: if CN key exists, try CN first; otherwise try GLOBAL first.
|
|
142
|
+
let cnApiKeyValue = null
|
|
143
|
+
try {
|
|
144
|
+
cnApiKeyValue = ctx.host.env.get("MINIMAX_CN_API_KEY")
|
|
145
|
+
} catch (e) {
|
|
146
|
+
ctx.host.log.warn("env read failed for MINIMAX_CN_API_KEY: " + String(e))
|
|
147
|
+
}
|
|
148
|
+
if (readString(cnApiKeyValue)) return ["CN", "GLOBAL"]
|
|
149
|
+
return ["GLOBAL", "CN"]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatAuthError() {
|
|
153
|
+
return "Session expired. Check your MiniMax API key."
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Tries multiple URL candidates and returns the first successful response.
|
|
158
|
+
* @returns {object} parsed JSON response
|
|
159
|
+
* @throws {string} error message
|
|
160
|
+
*/
|
|
161
|
+
function tryUrls(ctx, urls, apiKey) {
|
|
162
|
+
let lastStatus = null
|
|
163
|
+
let hadNetworkError = false
|
|
164
|
+
let authStatusCount = 0
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < urls.length; i += 1) {
|
|
167
|
+
const url = urls[i]
|
|
168
|
+
let resp
|
|
169
|
+
try {
|
|
170
|
+
resp = ctx.util.request({
|
|
171
|
+
method: "GET",
|
|
172
|
+
url: url,
|
|
173
|
+
headers: {
|
|
174
|
+
Authorization: "Bearer " + apiKey,
|
|
175
|
+
"Content-Type": "application/json",
|
|
176
|
+
Accept: "application/json",
|
|
177
|
+
},
|
|
178
|
+
timeoutMs: 15000,
|
|
179
|
+
})
|
|
180
|
+
} catch (e) {
|
|
181
|
+
hadNetworkError = true
|
|
182
|
+
ctx.host.log.warn("request failed (" + url + "): " + String(e))
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (ctx.util.isAuthStatus(resp.status)) {
|
|
187
|
+
authStatusCount += 1
|
|
188
|
+
ctx.host.log.warn("request returned auth status " + resp.status + " (" + url + ")")
|
|
189
|
+
continue
|
|
190
|
+
}
|
|
191
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
192
|
+
lastStatus = resp.status
|
|
193
|
+
ctx.host.log.warn("request returned status " + resp.status + " (" + url + ")")
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const parsed = ctx.util.tryParseJson(resp.bodyText)
|
|
198
|
+
if (!parsed || typeof parsed !== "object") {
|
|
199
|
+
ctx.host.log.warn("request returned invalid JSON (" + url + ")")
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return parsed
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (authStatusCount > 0 && lastStatus === null && !hadNetworkError) {
|
|
207
|
+
throw formatAuthError()
|
|
208
|
+
}
|
|
209
|
+
if (lastStatus !== null) throw "Request failed (HTTP " + lastStatus + "). Try again later."
|
|
210
|
+
if (hadNetworkError) throw "Request failed. Check your connection."
|
|
211
|
+
throw "Could not parse usage data."
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parsePayloadShape(ctx, payload, endpointSelection) {
|
|
215
|
+
if (!payload || typeof payload !== "object") return null
|
|
216
|
+
|
|
217
|
+
const data = payload.data && typeof payload.data === "object" ? payload.data : payload
|
|
218
|
+
const baseResp = (data && data.base_resp) || payload.base_resp || null
|
|
219
|
+
const statusCode = readNumber(baseResp && baseResp.status_code)
|
|
220
|
+
const statusMessage = readString(baseResp && baseResp.status_msg)
|
|
221
|
+
|
|
222
|
+
if (statusCode !== null && statusCode !== 0) {
|
|
223
|
+
const normalized = (statusMessage || "").toLowerCase()
|
|
224
|
+
if (
|
|
225
|
+
statusCode === 1004 ||
|
|
226
|
+
normalized.includes("cookie") ||
|
|
227
|
+
normalized.includes("log in") ||
|
|
228
|
+
normalized.includes("login")
|
|
229
|
+
) {
|
|
230
|
+
throw formatAuthError()
|
|
231
|
+
}
|
|
232
|
+
throw statusMessage
|
|
233
|
+
? "MiniMax API error: " + statusMessage
|
|
234
|
+
: "MiniMax API error (status " + statusCode + ")."
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const modelRemains =
|
|
238
|
+
(Array.isArray(data.model_remains) && data.model_remains) ||
|
|
239
|
+
(Array.isArray(payload.model_remains) && payload.model_remains) ||
|
|
240
|
+
(Array.isArray(data.modelRemains) && data.modelRemains) ||
|
|
241
|
+
(Array.isArray(payload.modelRemains) && payload.modelRemains) ||
|
|
242
|
+
null
|
|
243
|
+
|
|
244
|
+
if (!modelRemains || modelRemains.length === 0) return null
|
|
245
|
+
|
|
246
|
+
const displayMultiplierForSelection = endpointSelection === "CN" ? 1 / MODEL_CALLS_PER_PROMPT : 1
|
|
247
|
+
let chosen = null
|
|
248
|
+
let percentFallbackCandidate = null
|
|
249
|
+
let generalPercentFallbackCandidate = null
|
|
250
|
+
for (let i = 0; i < modelRemains.length; i += 1) {
|
|
251
|
+
const item = modelRemains[i]
|
|
252
|
+
if (!item || typeof item !== "object") continue
|
|
253
|
+
const total = readNumber(item.current_interval_total_count ?? item.currentIntervalTotalCount)
|
|
254
|
+
if (total !== null && total > 0 && Math.round(total * displayMultiplierForSelection) > 0) {
|
|
255
|
+
chosen = item
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
const remainingPercent = readNumber(
|
|
259
|
+
item.current_interval_remaining_percent ??
|
|
260
|
+
item.currentIntervalRemainingPercent
|
|
261
|
+
)
|
|
262
|
+
if (remainingPercent !== null && remainingPercent >= 0 && remainingPercent <= 100) {
|
|
263
|
+
const modelName = readString(item.model_name ?? item.modelName)
|
|
264
|
+
if (!percentFallbackCandidate) percentFallbackCandidate = item
|
|
265
|
+
if (!generalPercentFallbackCandidate && modelName === "general") {
|
|
266
|
+
generalPercentFallbackCandidate = item
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!chosen) chosen = generalPercentFallbackCandidate || percentFallbackCandidate
|
|
271
|
+
|
|
272
|
+
if (!chosen || typeof chosen !== "object") return null
|
|
273
|
+
|
|
274
|
+
const total = readNumber(chosen.current_interval_total_count ?? chosen.currentIntervalTotalCount)
|
|
275
|
+
const remainingPercent = readNumber(
|
|
276
|
+
chosen.current_interval_remaining_percent ??
|
|
277
|
+
chosen.currentIntervalRemainingPercent
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
const hasDisplayableCount =
|
|
281
|
+
total !== null && total > 0 && Math.round(total * displayMultiplierForSelection) > 0
|
|
282
|
+
|
|
283
|
+
if (!hasDisplayableCount && remainingPercent !== null) {
|
|
284
|
+
const percentUsed = 100 - remainingPercent
|
|
285
|
+
const startMs = epochToMs(chosen.start_time ?? chosen.startTime)
|
|
286
|
+
const endMs = epochToMs(chosen.end_time ?? chosen.endTime)
|
|
287
|
+
const remainsRaw = readNumber(chosen.remains_time ?? chosen.remainsTime)
|
|
288
|
+
const nowMs = Date.now()
|
|
289
|
+
const remainsMs = inferRemainsMs(remainsRaw, endMs, nowMs)
|
|
290
|
+
|
|
291
|
+
let resetsAt = endMs !== null ? ctx.util.toIso(endMs) : null
|
|
292
|
+
if (!resetsAt && remainsMs !== null) {
|
|
293
|
+
resetsAt = ctx.util.toIso(nowMs + remainsMs)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let periodDurationMs = null
|
|
297
|
+
if (startMs !== null && endMs !== null && endMs > startMs) {
|
|
298
|
+
periodDurationMs = endMs - startMs
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const explicitPlanName = normalizePlanName(pickFirstString([
|
|
302
|
+
data.current_subscribe_title,
|
|
303
|
+
data.plan_name,
|
|
304
|
+
data.plan,
|
|
305
|
+
data.current_plan_title,
|
|
306
|
+
data.combo_title,
|
|
307
|
+
payload.current_subscribe_title,
|
|
308
|
+
payload.plan_name,
|
|
309
|
+
payload.plan,
|
|
310
|
+
]))
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
planName: explicitPlanName || null,
|
|
314
|
+
used: percentUsed,
|
|
315
|
+
total: 100,
|
|
316
|
+
resetsAt,
|
|
317
|
+
periodDurationMs,
|
|
318
|
+
isPercent: true,
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!hasDisplayableCount) return null
|
|
323
|
+
|
|
324
|
+
const usageFieldCount = readNumber(chosen.current_interval_usage_count ?? chosen.currentIntervalUsageCount)
|
|
325
|
+
const remainingCount = readNumber(
|
|
326
|
+
chosen.current_interval_remaining_count ??
|
|
327
|
+
chosen.currentIntervalRemainingCount ??
|
|
328
|
+
chosen.current_interval_remains_count ??
|
|
329
|
+
chosen.currentIntervalRemainsCount ??
|
|
330
|
+
chosen.current_interval_remain_count ??
|
|
331
|
+
chosen.currentIntervalRemainCount ??
|
|
332
|
+
chosen.remaining_count ??
|
|
333
|
+
chosen.remainingCount ??
|
|
334
|
+
chosen.remains_count ??
|
|
335
|
+
chosen.remainsCount ??
|
|
336
|
+
chosen.remaining ??
|
|
337
|
+
chosen.remains ??
|
|
338
|
+
chosen.left_count ??
|
|
339
|
+
chosen.leftCount
|
|
340
|
+
)
|
|
341
|
+
// MiniMax "coding_plan/remains" commonly returns remaining prompts in current_interval_usage_count.
|
|
342
|
+
const inferredRemainingCount = remainingCount !== null ? remainingCount : usageFieldCount
|
|
343
|
+
const explicitUsed = readNumber(
|
|
344
|
+
chosen.current_interval_used_count ??
|
|
345
|
+
chosen.currentIntervalUsedCount ??
|
|
346
|
+
chosen.used_count ??
|
|
347
|
+
chosen.used
|
|
348
|
+
)
|
|
349
|
+
let used = explicitUsed
|
|
350
|
+
|
|
351
|
+
if (used === null && inferredRemainingCount !== null) used = total - inferredRemainingCount
|
|
352
|
+
if (used === null) return null
|
|
353
|
+
if (used < 0) used = 0
|
|
354
|
+
if (used > total) used = total
|
|
355
|
+
|
|
356
|
+
const startMs = epochToMs(chosen.start_time ?? chosen.startTime)
|
|
357
|
+
const endMs = epochToMs(chosen.end_time ?? chosen.endTime)
|
|
358
|
+
const remainsRaw = readNumber(chosen.remains_time ?? chosen.remainsTime)
|
|
359
|
+
const nowMs = Date.now()
|
|
360
|
+
const remainsMs = inferRemainsMs(remainsRaw, endMs, nowMs)
|
|
361
|
+
|
|
362
|
+
let resetsAt = endMs !== null ? ctx.util.toIso(endMs) : null
|
|
363
|
+
if (!resetsAt && remainsMs !== null) {
|
|
364
|
+
resetsAt = ctx.util.toIso(nowMs + remainsMs)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
let periodDurationMs = null
|
|
368
|
+
if (startMs !== null && endMs !== null && endMs > startMs) {
|
|
369
|
+
periodDurationMs = endMs - startMs
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const explicitPlanName = normalizePlanName(pickFirstString([
|
|
373
|
+
data.current_subscribe_title,
|
|
374
|
+
data.plan_name,
|
|
375
|
+
data.plan,
|
|
376
|
+
data.current_plan_title,
|
|
377
|
+
data.combo_title,
|
|
378
|
+
payload.current_subscribe_title,
|
|
379
|
+
payload.plan_name,
|
|
380
|
+
payload.plan,
|
|
381
|
+
]))
|
|
382
|
+
const inferredPlanName = inferPlanNameFromLimit(total, endpointSelection)
|
|
383
|
+
const planName = explicitPlanName || inferredPlanName
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
planName,
|
|
387
|
+
used,
|
|
388
|
+
total,
|
|
389
|
+
resetsAt,
|
|
390
|
+
periodDurationMs,
|
|
391
|
+
isPercent: false,
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function fetchUsagePayload(ctx, apiKey, endpointSelection) {
|
|
396
|
+
return tryUrls(ctx, getUsageUrls(endpointSelection), apiKey)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function probe(ctx) {
|
|
400
|
+
const attempts = endpointAttempts(ctx)
|
|
401
|
+
let lastError = null
|
|
402
|
+
let parsed = null
|
|
403
|
+
let successfulEndpoint = null
|
|
404
|
+
|
|
405
|
+
for (let i = 0; i < attempts.length; i += 1) {
|
|
406
|
+
const endpoint = attempts[i]
|
|
407
|
+
const apiKeyInfo = loadApiKey(ctx, endpoint)
|
|
408
|
+
if (!apiKeyInfo) continue
|
|
409
|
+
try {
|
|
410
|
+
const payload = fetchUsagePayload(ctx, apiKeyInfo.value, endpoint)
|
|
411
|
+
parsed = parsePayloadShape(ctx, payload, endpoint)
|
|
412
|
+
if (parsed) {
|
|
413
|
+
successfulEndpoint = endpoint
|
|
414
|
+
break
|
|
415
|
+
}
|
|
416
|
+
if (!lastError) lastError = "Could not parse usage data."
|
|
417
|
+
} catch (e) {
|
|
418
|
+
if (!lastError) lastError = String(e)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (!parsed) {
|
|
423
|
+
if (lastError) throw lastError
|
|
424
|
+
throw "MiniMax API key missing. Set MINIMAX_API_KEY or MINIMAX_CN_API_KEY."
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// CN API returns model call counts (needs division by 15 for prompts)
|
|
428
|
+
// GLOBAL API returns prompt counts directly
|
|
429
|
+
const isCnEndpoint = successfulEndpoint === "CN"
|
|
430
|
+
const displayMultiplier = isCnEndpoint ? 1 / MODEL_CALLS_PER_PROMPT : 1
|
|
431
|
+
const valueMultiplier = parsed.isPercent ? 1 : displayMultiplier
|
|
432
|
+
|
|
433
|
+
const line = {
|
|
434
|
+
label: "Session",
|
|
435
|
+
used: Math.round(parsed.used * valueMultiplier),
|
|
436
|
+
limit: Math.round(parsed.total * valueMultiplier),
|
|
437
|
+
format: parsed.isPercent
|
|
438
|
+
? { kind: "percent" }
|
|
439
|
+
: { kind: "count", suffix: "prompts" },
|
|
440
|
+
}
|
|
441
|
+
if (parsed.resetsAt) line.resetsAt = parsed.resetsAt
|
|
442
|
+
if (parsed.periodDurationMs !== null) line.periodDurationMs = parsed.periodDurationMs
|
|
443
|
+
|
|
444
|
+
const result = { lines: [ctx.line.progress(line)] }
|
|
445
|
+
if (parsed.planName) {
|
|
446
|
+
const regionLabel = successfulEndpoint === "CN" ? " (CN)" : " (GLOBAL)"
|
|
447
|
+
result.plan = parsed.planName + regionLabel
|
|
448
|
+
}
|
|
449
|
+
return result
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
globalThis.__openusage_plugin = { id: "minimax", probe }
|
|
453
|
+
})()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "minimax",
|
|
4
|
+
"name": "MiniMax",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"icon": "icon.svg",
|
|
8
|
+
"brandColor": "#F5433C",
|
|
9
|
+
"supportedModes": ["api"],
|
|
10
|
+
"autoMode": "api",
|
|
11
|
+
"lines": [
|
|
12
|
+
{ "type": "progress", "label": "Session", "scope": "overview", "primaryOrder": 1 }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Mistral</title><path d="M3.428 3.4h3.429v3.428H3.428V3.4zm13.714 0h3.43v3.428h-3.43V3.4z" fill="gold"></path><path d="M3.428 6.828h6.857v3.429H3.429V6.828zm10.286 0h6.857v3.429h-6.857V6.828z" fill="#FFAF00"></path><path d="M3.428 10.258h17.144v3.428H3.428v-3.428z" fill="#FF8205"></path><path d="M3.428 13.686h3.429v3.428H3.428v-3.428zm6.858 0h3.429v3.428h-3.429v-3.428zm6.856 0h3.43v3.428h-3.43v-3.428z" fill="#FA500F"></path><path d="M0 17.114h10.286v3.429H0v-3.429zm13.714 0H24v3.429H13.714v-3.429z" fill="#E10500"></path></svg>
|
|
@@ -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>Mistral</title><path clip-rule="evenodd" d="M3.428 3.4h3.429v3.428h3.429v3.429h-.002 3.431V6.828h3.427V3.4h3.43v13.714H24v3.429H13.714v-3.428h-3.428v-3.429h-3.43v3.428h3.43v3.429H0v-3.429h3.428V3.4zm10.286 13.715h3.428v-3.429h-3.427v3.429z"></path></svg>
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var BASE_URL = "https://admin.mistral.ai"
|
|
3
|
+
var USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36"
|
|
4
|
+
|
|
5
|
+
function getCookieHeader(ctx) {
|
|
6
|
+
var raw = ctx.host.env.get("MISTRAL_COOKIE")
|
|
7
|
+
if (!raw || !raw.trim()) {
|
|
8
|
+
throw "Set MISTRAL_COOKIE to your cookie header from admin.mistral.ai. Include csrftoken if present."
|
|
9
|
+
}
|
|
10
|
+
return raw.trim()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function extractCsrf(cookieHeader) {
|
|
14
|
+
var parts = cookieHeader.split(";")
|
|
15
|
+
for (var i = 0; i < parts.length; i++) {
|
|
16
|
+
var pair = parts[i].trim()
|
|
17
|
+
var eq = pair.indexOf("=")
|
|
18
|
+
if (eq < 0) continue
|
|
19
|
+
var name = pair.slice(0, eq).trim()
|
|
20
|
+
if (name === "csrftoken") return pair.slice(eq + 1).trim()
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function currentMonthYear() {
|
|
26
|
+
var now = new Date()
|
|
27
|
+
return { month: now.getUTCMonth() + 1, year: now.getUTCFullYear() }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function aggregateModels(models, prices) {
|
|
31
|
+
var total = 0
|
|
32
|
+
var inputTokens = 0
|
|
33
|
+
var outputTokens = 0
|
|
34
|
+
if (!models) return { cost: 0, inputTokens: 0, outputTokens: 0 }
|
|
35
|
+
var keys = Object.keys(models)
|
|
36
|
+
for (var i = 0; i < keys.length; i++) {
|
|
37
|
+
var data = models[keys[i]]
|
|
38
|
+
total += sumEntries(data.input, prices)
|
|
39
|
+
total += sumEntries(data.output, prices)
|
|
40
|
+
total += sumEntries(data.cached, prices)
|
|
41
|
+
inputTokens += countEntries(data.input)
|
|
42
|
+
outputTokens += countEntries(data.output)
|
|
43
|
+
}
|
|
44
|
+
return { cost: total, inputTokens: inputTokens, outputTokens: outputTokens }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function sumEntries(entries, prices) {
|
|
48
|
+
if (!entries) return 0
|
|
49
|
+
var total = 0
|
|
50
|
+
for (var i = 0; i < entries.length; i++) {
|
|
51
|
+
var e = entries[i]
|
|
52
|
+
var paid = e.value_paid != null ? e.value_paid : (e.value || 0)
|
|
53
|
+
var key = e.billing_metric + "::" + e.billing_group
|
|
54
|
+
var price = prices[key] || 0
|
|
55
|
+
total += paid * price
|
|
56
|
+
}
|
|
57
|
+
return total
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function countEntries(entries) {
|
|
61
|
+
if (!entries) return 0
|
|
62
|
+
var total = 0
|
|
63
|
+
for (var i = 0; i < entries.length; i++) {
|
|
64
|
+
var e = entries[i]
|
|
65
|
+
total += e.value_paid != null ? e.value_paid : (e.value || 0)
|
|
66
|
+
}
|
|
67
|
+
return total
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildPriceIndex(prices) {
|
|
71
|
+
var index = {}
|
|
72
|
+
if (!prices) return index
|
|
73
|
+
for (var i = 0; i < prices.length; i++) {
|
|
74
|
+
var p = prices[i]
|
|
75
|
+
if (p.billing_metric && p.billing_group && p.price) {
|
|
76
|
+
var key = p.billing_metric + "::" + p.billing_group
|
|
77
|
+
index[key] = parseFloat(p.price) || 0
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return index
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function probe(ctx) {
|
|
84
|
+
var cookieHeader = getCookieHeader(ctx)
|
|
85
|
+
var csrf = extractCsrf(cookieHeader)
|
|
86
|
+
var my = currentMonthYear()
|
|
87
|
+
|
|
88
|
+
var headers = {
|
|
89
|
+
"Cookie": cookieHeader,
|
|
90
|
+
"Accept": "*/*",
|
|
91
|
+
"Origin": BASE_URL,
|
|
92
|
+
"Referer": BASE_URL + "/organization/usage",
|
|
93
|
+
"User-Agent": USER_AGENT,
|
|
94
|
+
}
|
|
95
|
+
if (csrf) headers["X-CSRFTOKEN"] = csrf
|
|
96
|
+
|
|
97
|
+
var result = ctx.util.requestJson({
|
|
98
|
+
method: "GET",
|
|
99
|
+
url: BASE_URL + "/api/billing/v2/usage?month=" + my.month + "&year=" + my.year,
|
|
100
|
+
headers: headers,
|
|
101
|
+
timeoutMs: 20000,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
if (result.resp.status === 401 || result.resp.status === 403) {
|
|
105
|
+
throw "Session expired. Update MISTRAL_COOKIE with fresh cookies from admin.mistral.ai."
|
|
106
|
+
}
|
|
107
|
+
if (result.resp.status < 200 || result.resp.status >= 300) {
|
|
108
|
+
throw "Mistral API returned HTTP " + result.resp.status + "."
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var billing = result.json
|
|
112
|
+
if (!billing) throw "Could not parse Mistral billing response."
|
|
113
|
+
|
|
114
|
+
var prices = buildPriceIndex(billing.prices)
|
|
115
|
+
var currency = billing.currency || "EUR"
|
|
116
|
+
var symbol = billing.currency_symbol || "€"
|
|
117
|
+
|
|
118
|
+
var totalCost = 0
|
|
119
|
+
var totalInput = 0
|
|
120
|
+
var totalOutput = 0
|
|
121
|
+
|
|
122
|
+
if (billing.completion && billing.completion.models) {
|
|
123
|
+
var r = aggregateModels(billing.completion.models, prices)
|
|
124
|
+
totalCost += r.cost; totalInput += r.inputTokens; totalOutput += r.outputTokens
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var extras = [billing.ocr, billing.connectors, billing.audio]
|
|
128
|
+
for (var i = 0; i < extras.length; i++) {
|
|
129
|
+
if (extras[i] && extras[i].models) {
|
|
130
|
+
totalCost += aggregateModels(extras[i].models, prices).cost
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (billing.libraries_api) {
|
|
135
|
+
var lib = billing.libraries_api
|
|
136
|
+
if (lib.pages && lib.pages.models) totalCost += aggregateModels(lib.pages.models, prices).cost
|
|
137
|
+
if (lib.tokens && lib.tokens.models) totalCost += aggregateModels(lib.tokens.models, prices).cost
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
var costStr = symbol + totalCost.toFixed(4) + " this month (" + currency + ")"
|
|
141
|
+
var tokenDetail = totalInput + " in / " + totalOutput + " out tokens"
|
|
142
|
+
|
|
143
|
+
var lines = [
|
|
144
|
+
ctx.line.text({ label: "Monthly spend", value: costStr }),
|
|
145
|
+
ctx.line.text({ label: "Tokens", value: tokenDetail }),
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
return { lines: lines }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
globalThis.__openusage_plugin = { id: "mistral", probe: probe }
|
|
152
|
+
})()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "mistral",
|
|
4
|
+
"name": "Mistral",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"entry": "plugin.js",
|
|
7
|
+
"brandColor": "#FF7000",
|
|
8
|
+
"links": [
|
|
9
|
+
{ "label": "Usage", "url": "https://admin.mistral.ai/organization/usage" }
|
|
10
|
+
],
|
|
11
|
+
"envVars": ["MISTRAL_COOKIE"],
|
|
12
|
+
"supportedModes": ["web"],
|
|
13
|
+
"autoMode": "web",
|
|
14
|
+
"webUrl": "https://console.mistral.ai",
|
|
15
|
+
"lines": [
|
|
16
|
+
{ "type": "text", "label": "Monthly spend", "scope": "overview" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -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>MoonshotAI</title><path d="M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z"></path></svg>
|