openusage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/openusage +91 -0
- package/package.json +33 -0
- package/plugins/amp/icon.svg +6 -0
- package/plugins/amp/plugin.js +175 -0
- package/plugins/amp/plugin.json +20 -0
- package/plugins/amp/plugin.test.js +365 -0
- package/plugins/antigravity/icon.svg +3 -0
- package/plugins/antigravity/plugin.js +484 -0
- package/plugins/antigravity/plugin.json +17 -0
- package/plugins/antigravity/plugin.test.js +1356 -0
- package/plugins/claude/icon.svg +3 -0
- package/plugins/claude/plugin.js +565 -0
- package/plugins/claude/plugin.json +28 -0
- package/plugins/claude/plugin.test.js +1012 -0
- package/plugins/codex/icon.svg +3 -0
- package/plugins/codex/plugin.js +673 -0
- package/plugins/codex/plugin.json +30 -0
- package/plugins/codex/plugin.test.js +1071 -0
- package/plugins/copilot/icon.svg +3 -0
- package/plugins/copilot/plugin.js +264 -0
- package/plugins/copilot/plugin.json +20 -0
- package/plugins/copilot/plugin.test.js +529 -0
- package/plugins/cursor/icon.svg +3 -0
- package/plugins/cursor/plugin.js +526 -0
- package/plugins/cursor/plugin.json +24 -0
- package/plugins/cursor/plugin.test.js +1168 -0
- package/plugins/factory/icon.svg +1 -0
- package/plugins/factory/plugin.js +407 -0
- package/plugins/factory/plugin.json +19 -0
- package/plugins/factory/plugin.test.js +833 -0
- package/plugins/gemini/icon.svg +4 -0
- package/plugins/gemini/plugin.js +413 -0
- package/plugins/gemini/plugin.json +20 -0
- package/plugins/gemini/plugin.test.js +735 -0
- package/plugins/jetbrains-ai-assistant/icon.svg +3 -0
- package/plugins/jetbrains-ai-assistant/plugin.js +357 -0
- package/plugins/jetbrains-ai-assistant/plugin.json +17 -0
- package/plugins/jetbrains-ai-assistant/plugin.test.js +338 -0
- package/plugins/kimi/icon.svg +3 -0
- package/plugins/kimi/plugin.js +358 -0
- package/plugins/kimi/plugin.json +19 -0
- package/plugins/kimi/plugin.test.js +619 -0
- package/plugins/minimax/icon.svg +4 -0
- package/plugins/minimax/plugin.js +388 -0
- package/plugins/minimax/plugin.json +17 -0
- package/plugins/minimax/plugin.test.js +943 -0
- package/plugins/perplexity/icon.svg +1 -0
- package/plugins/perplexity/plugin.js +378 -0
- package/plugins/perplexity/plugin.json +15 -0
- package/plugins/perplexity/plugin.test.js +602 -0
- package/plugins/windsurf/icon.svg +3 -0
- package/plugins/windsurf/plugin.js +218 -0
- package/plugins/windsurf/plugin.json +16 -0
- package/plugins/windsurf/plugin.test.js +455 -0
- package/plugins/zai/icon.svg +5 -0
- package/plugins/zai/plugin.js +156 -0
- package/plugins/zai/plugin.json +18 -0
- package/plugins/zai/plugin.test.js +396 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
const GLOBAL_PRIMARY_USAGE_URL = "https://api.minimax.io/v1/api/openplatform/coding_plan/remains"
|
|
3
|
+
const GLOBAL_FALLBACK_USAGE_URLS = [
|
|
4
|
+
"https://api.minimax.io/v1/coding_plan/remains",
|
|
5
|
+
"https://www.minimax.io/v1/api/openplatform/coding_plan/remains",
|
|
6
|
+
]
|
|
7
|
+
const CN_PRIMARY_USAGE_URL = "https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains"
|
|
8
|
+
const CN_FALLBACK_USAGE_URLS = ["https://api.minimaxi.com/v1/coding_plan/remains"]
|
|
9
|
+
const GLOBAL_API_KEY_ENV_VARS = ["MINIMAX_API_KEY", "MINIMAX_API_TOKEN"]
|
|
10
|
+
const CN_API_KEY_ENV_VARS = ["MINIMAX_CN_API_KEY", "MINIMAX_API_KEY", "MINIMAX_API_TOKEN"]
|
|
11
|
+
const CODING_PLAN_WINDOW_MS = 5 * 60 * 60 * 1000
|
|
12
|
+
const CODING_PLAN_WINDOW_TOLERANCE_MS = 10 * 60 * 1000
|
|
13
|
+
// GLOBAL plan tiers (based on prompt limits)
|
|
14
|
+
const GLOBAL_PROMPT_LIMIT_TO_PLAN = {
|
|
15
|
+
100: "Starter",
|
|
16
|
+
300: "Plus",
|
|
17
|
+
1000: "Max",
|
|
18
|
+
2000: "Ultra",
|
|
19
|
+
}
|
|
20
|
+
// CN plan tiers (based on model call counts = prompts × 15)
|
|
21
|
+
// Starter: 40 prompts = 600, Plus: 100 prompts = 1500, Max: 300 prompts = 4500
|
|
22
|
+
const CN_PROMPT_LIMIT_TO_PLAN = {
|
|
23
|
+
600: "Starter",
|
|
24
|
+
1500: "Plus",
|
|
25
|
+
4500: "Max",
|
|
26
|
+
}
|
|
27
|
+
const MODEL_CALLS_PER_PROMPT = 15
|
|
28
|
+
|
|
29
|
+
function readString(value) {
|
|
30
|
+
if (typeof value !== "string") return null
|
|
31
|
+
const trimmed = value.trim()
|
|
32
|
+
return trimmed ? trimmed : null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readNumber(value) {
|
|
36
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null
|
|
37
|
+
if (typeof value !== "string") return null
|
|
38
|
+
const trimmed = value.trim()
|
|
39
|
+
if (!trimmed) return null
|
|
40
|
+
const n = Number(trimmed)
|
|
41
|
+
return Number.isFinite(n) ? n : null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function pickFirstString(values) {
|
|
45
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
46
|
+
const value = readString(values[i])
|
|
47
|
+
if (value) return value
|
|
48
|
+
}
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizePlanName(value) {
|
|
53
|
+
const raw = readString(value)
|
|
54
|
+
if (!raw) return null
|
|
55
|
+
const compact = raw.replace(/\s+/g, " ").trim()
|
|
56
|
+
const withoutPrefix = compact.replace(/^minimax\s+coding\s+plan\b[:\-]?\s*/i, "").trim()
|
|
57
|
+
if (withoutPrefix) return withoutPrefix
|
|
58
|
+
if (/coding\s+plan/i.test(compact)) return "Coding Plan"
|
|
59
|
+
return compact
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function inferPlanNameFromLimit(totalCount, endpointSelection) {
|
|
63
|
+
const n = readNumber(totalCount)
|
|
64
|
+
if (n === null || n <= 0) return null
|
|
65
|
+
|
|
66
|
+
const normalized = Math.round(n)
|
|
67
|
+
if (endpointSelection === "CN") {
|
|
68
|
+
// CN totals are model-call counts; only exact known CN tiers should infer.
|
|
69
|
+
return CN_PROMPT_LIMIT_TO_PLAN[normalized] || null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (GLOBAL_PROMPT_LIMIT_TO_PLAN[normalized]) return GLOBAL_PROMPT_LIMIT_TO_PLAN[normalized]
|
|
73
|
+
|
|
74
|
+
if (normalized % MODEL_CALLS_PER_PROMPT !== 0) return null
|
|
75
|
+
const inferredPromptLimit = normalized / MODEL_CALLS_PER_PROMPT
|
|
76
|
+
return GLOBAL_PROMPT_LIMIT_TO_PLAN[inferredPromptLimit] || null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function epochToMs(epoch) {
|
|
80
|
+
const n = readNumber(epoch)
|
|
81
|
+
if (n === null) return null
|
|
82
|
+
return Math.abs(n) < 1e10 ? n * 1000 : n
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function inferRemainsMs(remainsRaw, endMs, nowMs) {
|
|
86
|
+
if (remainsRaw === null || remainsRaw <= 0) return null
|
|
87
|
+
|
|
88
|
+
const asSecondsMs = remainsRaw * 1000
|
|
89
|
+
const asMillisecondsMs = remainsRaw
|
|
90
|
+
|
|
91
|
+
// If end_time exists, infer remains_time unit by whichever aligns best.
|
|
92
|
+
if (endMs !== null) {
|
|
93
|
+
const toEndMs = endMs - nowMs
|
|
94
|
+
if (toEndMs > 0) {
|
|
95
|
+
const secDelta = Math.abs(asSecondsMs - toEndMs)
|
|
96
|
+
const msDelta = Math.abs(asMillisecondsMs - toEndMs)
|
|
97
|
+
return secDelta <= msDelta ? asSecondsMs : asMillisecondsMs
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Coding Plan resets every 5h. Use that constraint before defaulting.
|
|
102
|
+
const maxExpectedMs = CODING_PLAN_WINDOW_MS + CODING_PLAN_WINDOW_TOLERANCE_MS
|
|
103
|
+
const secondsLooksValid = asSecondsMs <= maxExpectedMs
|
|
104
|
+
const millisecondsLooksValid = asMillisecondsMs <= maxExpectedMs
|
|
105
|
+
|
|
106
|
+
if (secondsLooksValid && !millisecondsLooksValid) return asSecondsMs
|
|
107
|
+
if (millisecondsLooksValid && !secondsLooksValid) return asMillisecondsMs
|
|
108
|
+
if (secondsLooksValid && millisecondsLooksValid) return asSecondsMs
|
|
109
|
+
|
|
110
|
+
const secOverflow = Math.abs(asSecondsMs - maxExpectedMs)
|
|
111
|
+
const msOverflow = Math.abs(asMillisecondsMs - maxExpectedMs)
|
|
112
|
+
return secOverflow <= msOverflow ? asSecondsMs : asMillisecondsMs
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function loadApiKey(ctx, endpointSelection) {
|
|
116
|
+
const envVars = endpointSelection === "CN" ? CN_API_KEY_ENV_VARS : GLOBAL_API_KEY_ENV_VARS
|
|
117
|
+
for (let i = 0; i < envVars.length; i += 1) {
|
|
118
|
+
const name = envVars[i]
|
|
119
|
+
let value = null
|
|
120
|
+
try {
|
|
121
|
+
value = ctx.host.env.get(name)
|
|
122
|
+
} catch (e) {
|
|
123
|
+
ctx.host.log.warn("env read failed for " + name + ": " + String(e))
|
|
124
|
+
}
|
|
125
|
+
const key = readString(value)
|
|
126
|
+
if (key) {
|
|
127
|
+
ctx.host.log.info("api key loaded from " + name)
|
|
128
|
+
return { value: key, source: name }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getUsageUrls(endpointSelection) {
|
|
135
|
+
if (endpointSelection === "CN") {
|
|
136
|
+
return [CN_PRIMARY_USAGE_URL].concat(CN_FALLBACK_USAGE_URLS)
|
|
137
|
+
}
|
|
138
|
+
return [GLOBAL_PRIMARY_USAGE_URL].concat(GLOBAL_FALLBACK_USAGE_URLS)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function endpointAttempts(ctx) {
|
|
142
|
+
// AUTO: if CN key exists, try CN first; otherwise try GLOBAL first.
|
|
143
|
+
let cnApiKeyValue = null
|
|
144
|
+
try {
|
|
145
|
+
cnApiKeyValue = ctx.host.env.get("MINIMAX_CN_API_KEY")
|
|
146
|
+
} catch (e) {
|
|
147
|
+
ctx.host.log.warn("env read failed for MINIMAX_CN_API_KEY: " + String(e))
|
|
148
|
+
}
|
|
149
|
+
if (readString(cnApiKeyValue)) return ["CN", "GLOBAL"]
|
|
150
|
+
return ["GLOBAL", "CN"]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatAuthError() {
|
|
154
|
+
return "Session expired. Check your MiniMax API key."
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Tries multiple URL candidates and returns the first successful response.
|
|
159
|
+
* @returns {object} parsed JSON response
|
|
160
|
+
* @throws {string} error message
|
|
161
|
+
*/
|
|
162
|
+
function tryUrls(ctx, urls, apiKey) {
|
|
163
|
+
let lastStatus = null
|
|
164
|
+
let hadNetworkError = false
|
|
165
|
+
let authStatusCount = 0
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < urls.length; i += 1) {
|
|
168
|
+
const url = urls[i]
|
|
169
|
+
let resp
|
|
170
|
+
try {
|
|
171
|
+
resp = ctx.util.request({
|
|
172
|
+
method: "GET",
|
|
173
|
+
url: url,
|
|
174
|
+
headers: {
|
|
175
|
+
Authorization: "Bearer " + apiKey,
|
|
176
|
+
"Content-Type": "application/json",
|
|
177
|
+
Accept: "application/json",
|
|
178
|
+
},
|
|
179
|
+
timeoutMs: 15000,
|
|
180
|
+
})
|
|
181
|
+
} catch (e) {
|
|
182
|
+
hadNetworkError = true
|
|
183
|
+
ctx.host.log.warn("request failed (" + url + "): " + String(e))
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (ctx.util.isAuthStatus(resp.status)) {
|
|
188
|
+
authStatusCount += 1
|
|
189
|
+
ctx.host.log.warn("request returned auth status " + resp.status + " (" + url + ")")
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
if (resp.status < 200 || resp.status >= 300) {
|
|
193
|
+
lastStatus = resp.status
|
|
194
|
+
ctx.host.log.warn("request returned status " + resp.status + " (" + url + ")")
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const parsed = ctx.util.tryParseJson(resp.bodyText)
|
|
199
|
+
if (!parsed || typeof parsed !== "object") {
|
|
200
|
+
ctx.host.log.warn("request returned invalid JSON (" + url + ")")
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return parsed
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (authStatusCount > 0 && lastStatus === null && !hadNetworkError) {
|
|
208
|
+
throw formatAuthError()
|
|
209
|
+
}
|
|
210
|
+
if (lastStatus !== null) throw "Request failed (HTTP " + lastStatus + "). Try again later."
|
|
211
|
+
if (hadNetworkError) throw "Request failed. Check your connection."
|
|
212
|
+
throw "Could not parse usage data."
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function parsePayloadShape(ctx, payload, endpointSelection) {
|
|
216
|
+
if (!payload || typeof payload !== "object") return null
|
|
217
|
+
|
|
218
|
+
const data = payload.data && typeof payload.data === "object" ? payload.data : payload
|
|
219
|
+
const baseResp = (data && data.base_resp) || payload.base_resp || null
|
|
220
|
+
const statusCode = readNumber(baseResp && baseResp.status_code)
|
|
221
|
+
const statusMessage = readString(baseResp && baseResp.status_msg)
|
|
222
|
+
|
|
223
|
+
if (statusCode !== null && statusCode !== 0) {
|
|
224
|
+
const normalized = (statusMessage || "").toLowerCase()
|
|
225
|
+
if (
|
|
226
|
+
statusCode === 1004 ||
|
|
227
|
+
normalized.includes("cookie") ||
|
|
228
|
+
normalized.includes("log in") ||
|
|
229
|
+
normalized.includes("login")
|
|
230
|
+
) {
|
|
231
|
+
throw formatAuthError()
|
|
232
|
+
}
|
|
233
|
+
throw statusMessage
|
|
234
|
+
? "MiniMax API error: " + statusMessage
|
|
235
|
+
: "MiniMax API error (status " + statusCode + ")."
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const modelRemains =
|
|
239
|
+
(Array.isArray(data.model_remains) && data.model_remains) ||
|
|
240
|
+
(Array.isArray(payload.model_remains) && payload.model_remains) ||
|
|
241
|
+
(Array.isArray(data.modelRemains) && data.modelRemains) ||
|
|
242
|
+
(Array.isArray(payload.modelRemains) && payload.modelRemains) ||
|
|
243
|
+
null
|
|
244
|
+
|
|
245
|
+
if (!modelRemains || modelRemains.length === 0) return null
|
|
246
|
+
|
|
247
|
+
let chosen = modelRemains[0]
|
|
248
|
+
for (let i = 0; i < modelRemains.length; i += 1) {
|
|
249
|
+
const item = modelRemains[i]
|
|
250
|
+
if (!item || typeof item !== "object") continue
|
|
251
|
+
const total = readNumber(item.current_interval_total_count ?? item.currentIntervalTotalCount)
|
|
252
|
+
if (total !== null && total > 0) {
|
|
253
|
+
chosen = item
|
|
254
|
+
break
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!chosen || typeof chosen !== "object") return null
|
|
259
|
+
|
|
260
|
+
const total = readNumber(chosen.current_interval_total_count ?? chosen.currentIntervalTotalCount)
|
|
261
|
+
if (total === null || total <= 0) return null
|
|
262
|
+
|
|
263
|
+
const usageFieldCount = readNumber(chosen.current_interval_usage_count ?? chosen.currentIntervalUsageCount)
|
|
264
|
+
const remainingCount = readNumber(
|
|
265
|
+
chosen.current_interval_remaining_count ??
|
|
266
|
+
chosen.currentIntervalRemainingCount ??
|
|
267
|
+
chosen.current_interval_remains_count ??
|
|
268
|
+
chosen.currentIntervalRemainsCount ??
|
|
269
|
+
chosen.current_interval_remain_count ??
|
|
270
|
+
chosen.currentIntervalRemainCount ??
|
|
271
|
+
chosen.remaining_count ??
|
|
272
|
+
chosen.remainingCount ??
|
|
273
|
+
chosen.remains_count ??
|
|
274
|
+
chosen.remainsCount ??
|
|
275
|
+
chosen.remaining ??
|
|
276
|
+
chosen.remains ??
|
|
277
|
+
chosen.left_count ??
|
|
278
|
+
chosen.leftCount
|
|
279
|
+
)
|
|
280
|
+
// MiniMax "coding_plan/remains" commonly returns remaining prompts in current_interval_usage_count.
|
|
281
|
+
const inferredRemainingCount = remainingCount !== null ? remainingCount : usageFieldCount
|
|
282
|
+
const explicitUsed = readNumber(
|
|
283
|
+
chosen.current_interval_used_count ??
|
|
284
|
+
chosen.currentIntervalUsedCount ??
|
|
285
|
+
chosen.used_count ??
|
|
286
|
+
chosen.used
|
|
287
|
+
)
|
|
288
|
+
let used = explicitUsed
|
|
289
|
+
|
|
290
|
+
if (used === null && inferredRemainingCount !== null) used = total - inferredRemainingCount
|
|
291
|
+
if (used === null) return null
|
|
292
|
+
if (used < 0) used = 0
|
|
293
|
+
if (used > total) used = total
|
|
294
|
+
|
|
295
|
+
const startMs = epochToMs(chosen.start_time ?? chosen.startTime)
|
|
296
|
+
const endMs = epochToMs(chosen.end_time ?? chosen.endTime)
|
|
297
|
+
const remainsRaw = readNumber(chosen.remains_time ?? chosen.remainsTime)
|
|
298
|
+
const nowMs = Date.now()
|
|
299
|
+
const remainsMs = inferRemainsMs(remainsRaw, endMs, nowMs)
|
|
300
|
+
|
|
301
|
+
let resetsAt = endMs !== null ? ctx.util.toIso(endMs) : null
|
|
302
|
+
if (!resetsAt && remainsMs !== null) {
|
|
303
|
+
resetsAt = ctx.util.toIso(nowMs + remainsMs)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
let periodDurationMs = null
|
|
307
|
+
if (startMs !== null && endMs !== null && endMs > startMs) {
|
|
308
|
+
periodDurationMs = endMs - startMs
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const explicitPlanName = normalizePlanName(pickFirstString([
|
|
312
|
+
data.current_subscribe_title,
|
|
313
|
+
data.plan_name,
|
|
314
|
+
data.plan,
|
|
315
|
+
data.current_plan_title,
|
|
316
|
+
data.combo_title,
|
|
317
|
+
payload.current_subscribe_title,
|
|
318
|
+
payload.plan_name,
|
|
319
|
+
payload.plan,
|
|
320
|
+
]))
|
|
321
|
+
const inferredPlanName = inferPlanNameFromLimit(total, endpointSelection)
|
|
322
|
+
const planName = explicitPlanName || inferredPlanName
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
planName,
|
|
326
|
+
used,
|
|
327
|
+
total,
|
|
328
|
+
resetsAt,
|
|
329
|
+
periodDurationMs,
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function fetchUsagePayload(ctx, apiKey, endpointSelection) {
|
|
334
|
+
return tryUrls(ctx, getUsageUrls(endpointSelection), apiKey)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function probe(ctx) {
|
|
338
|
+
const attempts = endpointAttempts(ctx)
|
|
339
|
+
let lastError = null
|
|
340
|
+
let parsed = null
|
|
341
|
+
let successfulEndpoint = null
|
|
342
|
+
|
|
343
|
+
for (let i = 0; i < attempts.length; i += 1) {
|
|
344
|
+
const endpoint = attempts[i]
|
|
345
|
+
const apiKeyInfo = loadApiKey(ctx, endpoint)
|
|
346
|
+
if (!apiKeyInfo) continue
|
|
347
|
+
try {
|
|
348
|
+
const payload = fetchUsagePayload(ctx, apiKeyInfo.value, endpoint)
|
|
349
|
+
parsed = parsePayloadShape(ctx, payload, endpoint)
|
|
350
|
+
if (parsed) {
|
|
351
|
+
successfulEndpoint = endpoint
|
|
352
|
+
break
|
|
353
|
+
}
|
|
354
|
+
if (!lastError) lastError = "Could not parse usage data."
|
|
355
|
+
} catch (e) {
|
|
356
|
+
if (!lastError) lastError = String(e)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!parsed) {
|
|
361
|
+
if (lastError) throw lastError
|
|
362
|
+
throw "MiniMax API key missing. Set MINIMAX_API_KEY or MINIMAX_CN_API_KEY."
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// CN API returns model call counts (needs division by 15 for prompts)
|
|
366
|
+
// GLOBAL API returns prompt counts directly
|
|
367
|
+
const isCnEndpoint = successfulEndpoint === "CN"
|
|
368
|
+
const displayMultiplier = isCnEndpoint ? 1 / MODEL_CALLS_PER_PROMPT : 1
|
|
369
|
+
|
|
370
|
+
const line = {
|
|
371
|
+
label: "Session",
|
|
372
|
+
used: Math.round(parsed.used * displayMultiplier),
|
|
373
|
+
limit: Math.round(parsed.total * displayMultiplier),
|
|
374
|
+
format: { kind: "count", suffix: "prompts" },
|
|
375
|
+
}
|
|
376
|
+
if (parsed.resetsAt) line.resetsAt = parsed.resetsAt
|
|
377
|
+
if (parsed.periodDurationMs !== null) line.periodDurationMs = parsed.periodDurationMs
|
|
378
|
+
|
|
379
|
+
const result = { lines: [ctx.line.progress(line)] }
|
|
380
|
+
if (parsed.planName) {
|
|
381
|
+
const regionLabel = successfulEndpoint === "CN" ? " (CN)" : " (GLOBAL)"
|
|
382
|
+
result.plan = parsed.planName + regionLabel
|
|
383
|
+
}
|
|
384
|
+
return result
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
globalThis.__openusage_plugin = { id: "minimax", probe }
|
|
388
|
+
})()
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
"cli": {
|
|
10
|
+
"category": "env",
|
|
11
|
+
"envVarNames": ["MINIMAX_API_KEY", "MINIMAX_CN_API_KEY"],
|
|
12
|
+
"envKeyLabel": "MiniMax API Key"
|
|
13
|
+
},
|
|
14
|
+
"lines": [
|
|
15
|
+
{ "type": "progress", "label": "Session", "scope": "overview", "primaryOrder": 1 }
|
|
16
|
+
]
|
|
17
|
+
}
|