dsh-connect-minimaxcode 0.5.7 → 0.5.8
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/README.en.md +30 -0
- package/README.md +30 -0
- package/lib/client.js +60 -3
- package/lib/index.d.ts +105 -3
- package/lib/index.js +180 -16
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -283,6 +283,27 @@ Two sets of data each cover one thing:
|
|
|
283
283
|
|
|
284
284
|
> The official price list has no separate price for **cache writes** (`cache_creation_input_tokens`), so the plugin counts its tokens but **does not convert them to money**, to avoid quoting a number that has no public basis.
|
|
285
285
|
|
|
286
|
+
### Account usage profile
|
|
287
|
+
|
|
288
|
+
The account's **usage history** comes from the open platform's usage summary, and it is a different thing from the token count the pill reports: the latter counts only the requests this host made.
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
GET https://www.minimaxi.com/backend/account/token_plan/usage_summary?token_type=5&heatmap_days=365&business=Agent&device_platform=desktop
|
|
292
|
+
Authorization: Bearer <OAuth 令牌>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The card shows the parts worth acting on:
|
|
296
|
+
|
|
297
|
+
| Field | Meaning |
|
|
298
|
+
| --- | --- |
|
|
299
|
+
| `current_consecutive_days` | Days used in a row |
|
|
300
|
+
| `active_days` / `total_days` | Days with usage in the window / the window's length |
|
|
301
|
+
| `total_token_consumed` | Tokens consumed in the window, in the upstream's own wording (`826.14M`) |
|
|
302
|
+
| `usage_ranking_percent` | Where the account ranks by usage |
|
|
303
|
+
| `most_active_day` | The busiest day and what it consumed |
|
|
304
|
+
|
|
305
|
+
This data **needs no M Plan subscription**: the endpoint is an account one, read with the OAuth bearer, unlike the limit windows, which belong to the subscription.
|
|
306
|
+
|
|
286
307
|
### About token refresh
|
|
287
308
|
|
|
288
309
|
The OAuth token is valid for about an hour and is refreshed automatically by the client. The plugin **only refreshes it once, with the refresh token the app already stored, when its own token has expired**, and writes the rotated new tokens back to the same file atomically, in the client's **original format** (with `generation` unchanged), so the desktop app's login state is unaffected. The plugin never starts a login, never signs out, and never calls any paid or subscription endpoint.
|
|
@@ -308,6 +329,15 @@ The OAuth token is valid for about an hour and is refreshed automatically by the
|
|
|
308
329
|
|
|
309
330
|
## Changelog
|
|
310
331
|
|
|
332
|
+
### 0.5.8 (2026-11-10)
|
|
333
|
+
|
|
334
|
+
- **Fixed "M3's reasoning could not be turned off"**: the plugin used to declare "reasoning cannot be disabled" for *every* reasoning model, on the grounds that the gateway refuses `thinking: {type: "disabled"}` with `400 ... requires adaptive thinking`. That refusal only holds for a `forced_on` model: the upstream catalog states `thinking_config.mode` per model, and `MiniMax-M3` is `switchable` (measured: the disabled form is accepted, 200) while `MiniMax-M3.1-Flash` is `forced_on` (measured: 400). Each model is now handled as upstream describes it: **M3's reasoning levels now include "off"**, and picking it really does stop the reasoning — fewer tokens, lower latency. M3.1-Flash is unchanged and the option still does not appear.
|
|
335
|
+
Upstream's `effort_options` (six levels for M3.1-Flash: default/low/medium/high/xhigh/max) are parsed and recorded but **not wired**: pi-ai's level mapping still dispatches the budget form, and reaching those six levels would switch the whole request shape, which this change does not do.
|
|
336
|
+
- **New "account usage profile"**: reads the open platform's `GET /backend/account/token_plan/usage_summary` (a constant this plugin had defined and never used). The card shows the consecutive days of use, the active days in the window, the tokens consumed in it, the usage ranking and the busiest day. It describes the *account*'s history, which is a different thing from the token count the pill reports for this host.
|
|
337
|
+
- **Says why there are no limit windows**: an account without an M Plan subscription gets `2062 no active token plan subscription` from upstream (already recognised in 0.5.7). The card now states that outright instead of leaving the reader to assume a failed read.
|
|
338
|
+
- **Endpoint survey**: every one of the twenty account-domain endpoints the desktop app calls each day was mapped — **nineteen of them are readable with the OAuth bearer and need no subscription at all**. What a subscription buys is not more endpoints but two things: the limit windows and a larger allowance.
|
|
339
|
+
- Added `tests/usage-profile.spec.ts` (16 cases) plus reasoning-mode cases in `tests/catalog.spec.ts` / `tests/adapter.spec.ts`. All 236 cases pass.
|
|
340
|
+
|
|
311
341
|
### 0.5.7 (2026-11-10)
|
|
312
342
|
|
|
313
343
|
- **The quota readout now uses the open platform's documented endpoint**: the plugin read `/backend/account/token_plan/remains_percent`, and that path now answers every account with an empty `success` — it no longer even carries a `model_remains` field — so the readout showed nothing for anyone and said nothing about why. It now reads `GET /v1/token_plan/remains`, the endpoint the open platform's own M Plan documentation names. That endpoint asks for the **open platform's API key** rather than the OAuth bearer; the key comes from the `token_plan_credit` response and is remembered between reads.
|
package/README.md
CHANGED
|
@@ -319,6 +319,27 @@ Authorization: Bearer <API Key>
|
|
|
319
319
|
- **没有订阅就没有窗口,且这不算错误**:上游对未订阅 M Plan 的账户明确回 `2062 no active token plan subscription`,插件会如实标为「无订阅」而不是「读取失败」—— 积分包本身不带滚动窗口
|
|
320
320
|
- **接口异常时其余显示不受影响**:限额读不到,胶囊与卡片仍显示余额、签到与本机用量
|
|
321
321
|
- 上游还发 `current_*_count` 计数字段(无计数制时为 `-1`),插件只读百分比与起止时刻
|
|
322
|
+
### 账户用量画像
|
|
323
|
+
|
|
324
|
+
账户的**用量历史**由开放平台的用量汇总接口给出,与「本机累计 token」是两回事 —— 后者只统计这台机器发起的请求:
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
GET https://www.minimaxi.com/backend/account/token_plan/usage_summary?token_type=5&heatmap_days=365&business=Agent&device_platform=desktop
|
|
328
|
+
Authorization: Bearer <OAuth 令牌>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
卡片显示的是其中有行动价值的几项:
|
|
332
|
+
|
|
333
|
+
| 字段 | 含义 |
|
|
334
|
+
| --- | --- |
|
|
335
|
+
| `current_consecutive_days` | 连续使用天数 |
|
|
336
|
+
| `active_days` / `total_days` | 统计窗口内有使用的天数 / 窗口长度 |
|
|
337
|
+
| `total_token_consumed` | 窗口内累计消耗(上游自己的写法,如 `826.14M`) |
|
|
338
|
+
| `usage_ranking_percent` | 用量排名分位 |
|
|
339
|
+
| `most_active_day` | 最活跃的一天及其消耗 |
|
|
340
|
+
|
|
341
|
+
这一节读到的数据**不需要订阅 M Plan**:该接口属于账户域,用 OAuth 令牌即可读取,与限额窗口(属于订阅)不同。
|
|
342
|
+
|
|
322
343
|
### 关于续期
|
|
323
344
|
|
|
324
345
|
OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自己的令牌已过期时**才用 App 已存的 refresh token 续一次,并把轮换后的新令牌按客户端**原格式原子写回**同一个文件(`generation` 保持不变),因此桌面 App 的登录态不受影响。插件不会发起登录、不会登出、也不会调用任何付费/订阅类接口。
|
|
@@ -344,6 +365,15 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
|
|
|
344
365
|
|
|
345
366
|
## 更新记录
|
|
346
367
|
|
|
368
|
+
### 0.5.8(2026-11-10)
|
|
369
|
+
|
|
370
|
+
- **修复「M3 的推理关不掉」**:插件此前对**所有**推理模型都声明「不支持关闭推理」,理由是网关会以 `400 ... requires adaptive thinking` 拒绝 `thinking: {type: "disabled"}`。但该拒绝只对 `forced_on` 的模型成立 —— 上游模型目录逐模型声明 `thinking_config.mode`,`MiniMax-M3` 是 `switchable`(实测接受该形式,200),`MiniMax-M3.1-Flash` 才是 `forced_on`(实测 400)。现在按上游声明逐模型处理:**M3 的推理档位里出现了「关」**,选它就真的不发推理,既省 token 也更快;M3.1-Flash 的行为不变,选项仍不出现。
|
|
371
|
+
上游的 `effort_options`(M3.1-Flash 有 default/low/medium/high/xhigh/max 六档)也一并解析存档,但**未接线**:pi-ai 的档位映射目前走 budget 形式,要用上这六档得切换整个请求形态,这一步不在本次改动里。
|
|
372
|
+
- **新增「账户用量画像」**:读取开放平台的 `GET /backend/account/token_plan/usage_summary`(该常量此前定义了却从未使用),卡片显示连续使用天数、统计窗口内的活跃天数、窗口内累计消耗、用量排名分位与最活跃的一天。它说的是**账户**的历史,与胶囊里「本机累计 token」是两回事。
|
|
373
|
+
- **说明「为什么没有限额窗口」**:账户未订阅 M Plan 时,上游回 `2062 no active token plan subscription`(0.5.7 已识别)。现在卡片直接写明「未订阅 M Plan(仅积分包),因此没有限额窗口」,而不是让人以为读取失败。
|
|
374
|
+
- **接口盘点**:把桌面 App 每天真实调用的 20 个账户域接口全部盘了一遍 —— 其中 **19 个用 OAuth 令牌即可读取,无需任何订阅**,只有「限额窗口」属于订阅本身。也就是说订阅 M Plan 换来的不是「更多接口」,而是**限额窗口**与**更高额度**这两样。
|
|
375
|
+
- 新增 `tests/usage-profile.spec.ts`(16 个用例)与 `tests/catalog.spec.ts` / `tests/adapter.spec.ts` 的推理模式用例。全量 236 个用例通过。
|
|
376
|
+
|
|
347
377
|
### 0.5.7(2026-11-10)
|
|
348
378
|
|
|
349
379
|
- **限额窗口改走官方文档接口**:原先读的是 `/backend/account/token_plan/remains_percent`,这条路径现在对任何账户都只回一个空的 `success`(连 `model_remains` 字段都没有),于是限额一栏对所有人都显示不出来,却看不出原因。改用开放平台官方文档(M Plan 用量说明页)给出的 `GET /v1/token_plan/remains`。新接口要求**开放平台 API 密钥**而非 OAuth 令牌,密钥取自 `token_plan_credit` 的响应并缓存复用。
|
package/lib/client.js
CHANGED
|
@@ -68,6 +68,13 @@
|
|
|
68
68
|
claimableToday: '今日可签到',
|
|
69
69
|
usageWindow: '{duration}限额',
|
|
70
70
|
resetIn: '{duration}后',
|
|
71
|
+
accountUsage: '账户用量',
|
|
72
|
+
usageStreak: '连续使用 {days} 天',
|
|
73
|
+
usageActiveDays: '近 {total} 天中有 {active} 天用过',
|
|
74
|
+
usageTotalTokens: '累计消耗 {count} tokens',
|
|
75
|
+
usageRanking: '用量排名前 {percent}%',
|
|
76
|
+
usageBestDay: '最活跃的一天 {date}',
|
|
77
|
+
planNoSubscription: '未订阅 M Plan(仅积分包),因此没有限额窗口',
|
|
71
78
|
};
|
|
72
79
|
const en = {
|
|
73
80
|
title: 'MiniMax Code credits',
|
|
@@ -110,6 +117,13 @@
|
|
|
110
117
|
claimableToday: 'Claimable today',
|
|
111
118
|
usageWindow: '{duration} limit',
|
|
112
119
|
resetIn: 'resets in {duration}',
|
|
120
|
+
accountUsage: 'Account usage',
|
|
121
|
+
usageStreak: '{days} days in a row',
|
|
122
|
+
usageActiveDays: 'Used on {active} of the last {total} days',
|
|
123
|
+
usageTotalTokens: '{count} tokens consumed',
|
|
124
|
+
usageRanking: 'Top {percent}% by usage',
|
|
125
|
+
usageBestDay: 'Busiest day {date}',
|
|
126
|
+
planNoSubscription: 'No M Plan subscription (credits only), so there are no usage windows',
|
|
113
127
|
};
|
|
114
128
|
const loader = globalThis.__ModuleLoader__;
|
|
115
129
|
if (loader === undefined)
|
|
@@ -259,6 +273,49 @@
|
|
|
259
273
|
return null;
|
|
260
274
|
return h('div', { style: expiryBoxStyle, 'data-minimax-rate-limit': true }, row(t('usageWindow', { duration: windowLabel(rateLimit.interval) }), `${rateLimit.interval.usedPercent}%(${t('resetIn', { duration: countdown(rateLimit.interval.resetAtMs) })})`), row(t('usageWindow', { duration: windowLabel(rateLimit.weekly) }), `${rateLimit.weekly.usedPercent}%(${t('resetIn', { duration: countdown(rateLimit.weekly.resetAtMs) })})`));
|
|
261
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* The account's own usage history.
|
|
278
|
+
*
|
|
279
|
+
* This is not the token count of this host — the pill reports that
|
|
280
|
+
* separately — but the account's days, consumption and standing, which
|
|
281
|
+
* is what the upstream knows and nobody else does.
|
|
282
|
+
*/
|
|
283
|
+
function usageProfileBlock(t, profile) {
|
|
284
|
+
if (profile === undefined || profile.state !== 'ok')
|
|
285
|
+
return null;
|
|
286
|
+
const rows = [];
|
|
287
|
+
if (profile.consecutiveDays !== undefined && profile.consecutiveDays > 0) {
|
|
288
|
+
rows.push(row(t('usageStreak', { days: profile.consecutiveDays }), ''));
|
|
289
|
+
}
|
|
290
|
+
if (profile.activeDays !== undefined) {
|
|
291
|
+
rows.push(row(t('usageActiveDays', { active: profile.activeDays, total: profile.totalDays ?? '' }), ''));
|
|
292
|
+
}
|
|
293
|
+
if (profile.totalTokenConsumed !== undefined) {
|
|
294
|
+
rows.push(row(t('usageTotalTokens', { count: profile.totalTokenConsumed }), ''));
|
|
295
|
+
}
|
|
296
|
+
if (profile.rankingPercent !== undefined) {
|
|
297
|
+
rows.push(row(t('usageRanking', { percent: profile.rankingPercent.toFixed(1) }), ''));
|
|
298
|
+
}
|
|
299
|
+
if (profile.mostActiveDay?.date !== undefined) {
|
|
300
|
+
rows.push(row(t('usageBestDay', { date: profile.mostActiveDay.date }), ''));
|
|
301
|
+
}
|
|
302
|
+
if (rows.length === 0)
|
|
303
|
+
return null;
|
|
304
|
+
return h('div', { style: expiryBoxStyle, 'data-minimax-usage-profile': true }, ...rows);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Why there are no usage windows, when there are none because the plan
|
|
308
|
+
* has none to give.
|
|
309
|
+
*
|
|
310
|
+
* The rolling windows belong to the subscription; an account holding
|
|
311
|
+
* only a credit package has none, and saying so is more useful than
|
|
312
|
+
* leaving the reader to wonder whether a read failed.
|
|
313
|
+
*/
|
|
314
|
+
function planNotice(t, rateLimit) {
|
|
315
|
+
if (rateLimit?.reason !== 'no-subscription')
|
|
316
|
+
return null;
|
|
317
|
+
return h('div', { style: mutedStyle, 'data-minimax-plan-notice': true }, t('planNoSubscription'));
|
|
318
|
+
}
|
|
262
319
|
/** The pill itself; the gate decides whether it is drawn at all. */
|
|
263
320
|
function CreditPill(props) {
|
|
264
321
|
const [status] = useStatus();
|
|
@@ -291,7 +348,7 @@
|
|
|
291
348
|
const detail = available
|
|
292
349
|
? h('div', { style: detailStyle, 'data-minimax-credits-detail': true }, row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), credits_.consumed !== undefined && credits_.consumed > 0
|
|
293
350
|
? row(t('consumed'), credits(credits_.consumed, 3))
|
|
294
|
-
: null, expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), usage !== undefined
|
|
351
|
+
: null, expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), usageProfileBlock(t, status?.usageProfile), usage !== undefined
|
|
295
352
|
? h('div', { style: rowStyle }, h('span', null, t('tokens', { count: tokens(usage.inputTokens + usage.outputTokens + usage.cacheReadTokens) })), h('span', null, t('cost', { amount: money(usage.estimatedCny) })))
|
|
296
353
|
: null, credits_.updatedAtMs !== undefined
|
|
297
354
|
? row(t('updated', { time: new Date(credits_.updatedAtMs).toLocaleTimeString() }), '')
|
|
@@ -432,11 +489,11 @@
|
|
|
432
489
|
if (!available) {
|
|
433
490
|
return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: rowStyle }, h('span', null, t('unavailable')), h('span', null, credits_?.state === 'expired' ? t('expired') : '')), loginBlock(t, login, refresh));
|
|
434
491
|
}
|
|
435
|
-
return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), signinBlock(t, signin, refresh), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
|
|
492
|
+
return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), rateLimitBlock(t, status?.rateLimit), planNotice(t, status?.rateLimit), signinBlock(t, signin, refresh), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
|
|
436
493
|
? h('div', { style: mutedStyle }, t('noLots'))
|
|
437
494
|
: h('ul', { style: listStyle }, ...(credits_.upcoming ?? []).map((lot, index) => h('li', { key: `${lot.expireAtMs}-${index}`, style: rowStyle }, h('span', null, day(lot.expireAtMs)), h('span', null, credits(lot.amount, 2))))), usage !== undefined
|
|
438
495
|
? h('div', { style: usageBoxStyle }, row(t('tokens', { count: tokens(usage.inputTokens + usage.outputTokens + usage.cacheReadTokens) }), ''), row(t('cost', { amount: money(usage.estimatedCny) }), ''), usage.requests > 0 ? row(t('requests', { count: usage.requests }), usage.lastModel ?? '') : null)
|
|
439
|
-
: null, loginBlock(t, login, refresh), credits_.updatedAtMs !== undefined
|
|
496
|
+
: null, usageProfileBlock(t, status?.usageProfile), loginBlock(t, login, refresh), credits_.updatedAtMs !== undefined
|
|
440
497
|
? h('div', { style: mutedStyle }, t('updated', { time: new Date(credits_.updatedAtMs).toLocaleTimeString() }))
|
|
441
498
|
: null);
|
|
442
499
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -133,6 +133,28 @@ interface CatalogModel {
|
|
|
133
133
|
inputModalities: string[];
|
|
134
134
|
/** Whether the model accepts images at all. */
|
|
135
135
|
attachment: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Whether reasoning can be turned off for this model.
|
|
138
|
+
*
|
|
139
|
+
* Upstream states it per model in `thinking_config.mode`: a `switchable`
|
|
140
|
+
* model accepts `thinking: {type: "disabled"}`, a `forced_on` model refuses
|
|
141
|
+
* it with `400 ... requires adaptive thinking` (both measured against the live
|
|
142
|
+
* gateway on 2026-11-10). Absent means upstream did not say, which is read as
|
|
143
|
+
* "reasoning is mandatory".
|
|
144
|
+
*/
|
|
145
|
+
reasoningOptional?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* The reasoning effort levels upstream declares (`effort_options`).
|
|
148
|
+
*
|
|
149
|
+
* Recorded rather than acted on. pi-ai's own level mapping still dispatches
|
|
150
|
+
* the budget form (`thinking: {type: "enabled", budget_tokens}`) for these
|
|
151
|
+
* models, so naming the levels here changes no request; the names are kept
|
|
152
|
+
* so the information is not lost when it becomes usable. Reaching them on
|
|
153
|
+
* the wire would need pi-ai's `compat.forceAdaptiveThinking`, which switches
|
|
154
|
+
* the request to the adaptive form — a change of shape this plugin does not
|
|
155
|
+
* make on the strength of a catalog field alone.
|
|
156
|
+
*/
|
|
157
|
+
effortLevels?: string[];
|
|
136
158
|
}
|
|
137
159
|
/** Provider id registered with DSH, distinct from the upstream one. */
|
|
138
160
|
declare const MINIMAXCODE_PROVIDER_ID = "minimaxcode";
|
|
@@ -150,6 +172,11 @@ declare const MINIMAXCODE_PROVIDER_ID = "minimaxcode";
|
|
|
150
172
|
* reports: 512k is the billing tier boundary, and the gateway itself was
|
|
151
173
|
* measured serving 946,206 tokens before refusing ~1,050,000 with
|
|
152
174
|
* `invalid params, context window exceeds limit (2013)`.
|
|
175
|
+
*
|
|
176
|
+
* The reasoning modes are the upstream ones measured on 2026-11-10: `M3` is
|
|
177
|
+
* `switchable` and accepts `thinking: {type: "disabled"}`, `M3.1-Flash` is
|
|
178
|
+
* `forced_on` and refuses it. Encoding them here keeps the fallback honest
|
|
179
|
+
* about what the gateway accepts even when no catalog could be fetched.
|
|
153
180
|
*/
|
|
154
181
|
declare const FALLBACK_MODELS: readonly CatalogModel[];
|
|
155
182
|
/** Where the currently served list came from. */
|
|
@@ -385,6 +412,36 @@ declare function parseRateLimitWindows(document: unknown): RateLimitWindows | un
|
|
|
385
412
|
* @returns The windows, or `undefined` when the response carries none.
|
|
386
413
|
*/
|
|
387
414
|
declare function fetchRateLimitWindows(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch): Promise<RateLimitWindows | undefined>;
|
|
415
|
+
/**
|
|
416
|
+
* The account's usage profile, as the upstream summarizes it.
|
|
417
|
+
*
|
|
418
|
+
* The desktop app asks for this once a day and shows it as a yearly heatmap
|
|
419
|
+
* with a few headline numbers. It is the only place the upstream says how the
|
|
420
|
+
* account actually uses its tokens, and none of it is visible elsewhere: the
|
|
421
|
+
* balance says how much is left, not how much was spent or how the account
|
|
422
|
+
* compares with other users.
|
|
423
|
+
*/
|
|
424
|
+
interface UsageProfile {
|
|
425
|
+
/** Days the summary window covers. */
|
|
426
|
+
totalDays?: number;
|
|
427
|
+
/** Days in the window that saw any usage. */
|
|
428
|
+
activeDays?: number;
|
|
429
|
+
/** Consecutive days of use up to today. */
|
|
430
|
+
currentConsecutiveDays?: number;
|
|
431
|
+
/** Tokens consumed in the window, in the upstream's own wording ("826.14M"). */
|
|
432
|
+
totalTokenConsumed?: string;
|
|
433
|
+
/** Where the account sits among other users, in percent (10.3 = above 89.7% of users). */
|
|
434
|
+
usageRankingPercent?: number;
|
|
435
|
+
/** The busiest day in the window. */
|
|
436
|
+
mostActiveDay?: {
|
|
437
|
+
date?: string;
|
|
438
|
+
tokenCount?: string;
|
|
439
|
+
};
|
|
440
|
+
/** When the upstream last refreshed the summary. */
|
|
441
|
+
lastUpdateTime?: string;
|
|
442
|
+
/** Daily token usage, oldest first. */
|
|
443
|
+
dailyTokenUsage?: number[];
|
|
444
|
+
}
|
|
388
445
|
/** What a quota read found. */
|
|
389
446
|
interface RateLimitRead {
|
|
390
447
|
/** The windows, when the plan meters any. */
|
|
@@ -440,14 +497,18 @@ declare class MinimaxCredits {
|
|
|
440
497
|
get rateLimitAbsence(): 'no-subscription' | undefined;
|
|
441
498
|
/** When the windows were last read. */
|
|
442
499
|
get rateLimitAtMs(): number | undefined;
|
|
500
|
+
/** The account's usage profile as last read. */
|
|
501
|
+
get usageProfile(): UsageProfile | undefined;
|
|
502
|
+
/** Why the usage profile could not be read, when it could not. */
|
|
503
|
+
get usageProfileError(): string | undefined;
|
|
443
504
|
/**
|
|
444
505
|
* Read the balance and record how much was consumed since the last read.
|
|
445
506
|
*
|
|
446
507
|
* The credit ledger is read alongside it, but a ledger that cannot be read
|
|
447
508
|
* never hides the balance: the two come from different endpoints and only
|
|
448
|
-
* the balance is essential. The rate-limit windows
|
|
449
|
-
* same rule — they come from yet
|
|
450
|
-
* takes the others down with it.
|
|
509
|
+
* the balance is essential. The rate-limit windows and the usage profile
|
|
510
|
+
* ride along under the same rule — they come from yet other endpoints, and
|
|
511
|
+
* none of the four takes the others down with it.
|
|
451
512
|
*
|
|
452
513
|
* @returns Whether the balance changed.
|
|
453
514
|
*/
|
|
@@ -467,6 +528,13 @@ declare class MinimaxCredits {
|
|
|
467
528
|
* window that cannot be read leaves the last one on display.
|
|
468
529
|
*/
|
|
469
530
|
refreshRateLimit(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch, now?: number): Promise<void>;
|
|
531
|
+
/**
|
|
532
|
+
* Read the account's usage profile.
|
|
533
|
+
*
|
|
534
|
+
* Same rule as the ledger: a failure is remembered but never thrown, and a
|
|
535
|
+
* profile that cannot be read leaves the last one on display.
|
|
536
|
+
*/
|
|
537
|
+
refreshUsageProfile(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch): Promise<void>;
|
|
470
538
|
/**
|
|
471
539
|
* Read the windows from whichever endpoint can answer.
|
|
472
540
|
*
|
|
@@ -940,6 +1008,36 @@ interface RateLimitStatus {
|
|
|
940
1008
|
/** Why the windows could not be read. */
|
|
941
1009
|
error?: string;
|
|
942
1010
|
}
|
|
1011
|
+
/**
|
|
1012
|
+
* The account's usage profile, as the upstream summarizes it.
|
|
1013
|
+
*
|
|
1014
|
+
* This is the account's own history — how long it has been used, how much it
|
|
1015
|
+
* has consumed, how it ranks — and it is a different thing from the usage
|
|
1016
|
+
* this host has made, which the card reports separately.
|
|
1017
|
+
*/
|
|
1018
|
+
interface UsageProfileStatus {
|
|
1019
|
+
/** Whether the profile could be read. */
|
|
1020
|
+
state: 'ok' | 'unavailable';
|
|
1021
|
+
/** Days the summary window covers. */
|
|
1022
|
+
totalDays?: number;
|
|
1023
|
+
/** Days in the window that saw any usage. */
|
|
1024
|
+
activeDays?: number;
|
|
1025
|
+
/** Consecutive days of use up to today. */
|
|
1026
|
+
consecutiveDays?: number;
|
|
1027
|
+
/** Tokens consumed in the window, in the upstream's own wording. */
|
|
1028
|
+
totalTokenConsumed?: string;
|
|
1029
|
+
/** Where the account sits among other users, in percent. */
|
|
1030
|
+
rankingPercent?: number;
|
|
1031
|
+
/** The busiest day in the window. */
|
|
1032
|
+
mostActiveDay?: {
|
|
1033
|
+
date?: string;
|
|
1034
|
+
tokenCount?: string;
|
|
1035
|
+
};
|
|
1036
|
+
/** When the upstream last refreshed the summary. */
|
|
1037
|
+
updatedAt?: string;
|
|
1038
|
+
/** Why the profile could not be read. */
|
|
1039
|
+
error?: string;
|
|
1040
|
+
}
|
|
943
1041
|
/** What the card renders. */
|
|
944
1042
|
interface StatusDocument {
|
|
945
1043
|
state: 'signed-in' | 'signed-out' | 'expired';
|
|
@@ -970,6 +1068,8 @@ interface StatusDocument {
|
|
|
970
1068
|
signin?: SigninStatus;
|
|
971
1069
|
/** How the plugin is signed in. */
|
|
972
1070
|
login?: LoginStatus;
|
|
1071
|
+
/** The account's usage profile, when it could be read. */
|
|
1072
|
+
usageProfile?: UsageProfileStatus;
|
|
973
1073
|
}
|
|
974
1074
|
/** Inputs the document is derived from. */
|
|
975
1075
|
interface StatusDeps {
|
|
@@ -998,6 +1098,8 @@ interface StatusDeps {
|
|
|
998
1098
|
signin?: SigninStatus;
|
|
999
1099
|
/** How the plugin is signed in. */
|
|
1000
1100
|
login?: LoginStatus;
|
|
1101
|
+
/** The account's usage profile, when the account API could be reached. */
|
|
1102
|
+
usageProfile?: UsageProfileStatus;
|
|
1001
1103
|
}
|
|
1002
1104
|
/**
|
|
1003
1105
|
* Build the card's status document.
|
package/lib/index.js
CHANGED
|
@@ -243,6 +243,11 @@ const MINIMAXCODE_PROVIDER_ID = "minimaxcode";
|
|
|
243
243
|
* reports: 512k is the billing tier boundary, and the gateway itself was
|
|
244
244
|
* measured serving 946,206 tokens before refusing ~1,050,000 with
|
|
245
245
|
* `invalid params, context window exceeds limit (2013)`.
|
|
246
|
+
*
|
|
247
|
+
* The reasoning modes are the upstream ones measured on 2026-11-10: `M3` is
|
|
248
|
+
* `switchable` and accepts `thinking: {type: "disabled"}`, `M3.1-Flash` is
|
|
249
|
+
* `forced_on` and refuses it. Encoding them here keeps the fallback honest
|
|
250
|
+
* about what the gateway accepts even when no catalog could be fetched.
|
|
246
251
|
*/
|
|
247
252
|
const FALLBACK_MODELS = [{
|
|
248
253
|
id: "MiniMax-M3",
|
|
@@ -256,7 +261,8 @@ const FALLBACK_MODELS = [{
|
|
|
256
261
|
"image",
|
|
257
262
|
"video"
|
|
258
263
|
],
|
|
259
|
-
attachment: true
|
|
264
|
+
attachment: true,
|
|
265
|
+
reasoningOptional: true
|
|
260
266
|
}, {
|
|
261
267
|
id: "MiniMax-M3.1-Flash",
|
|
262
268
|
name: "M3.1-Flash",
|
|
@@ -269,14 +275,20 @@ const FALLBACK_MODELS = [{
|
|
|
269
275
|
"image",
|
|
270
276
|
"video"
|
|
271
277
|
],
|
|
272
|
-
attachment: true
|
|
278
|
+
attachment: true,
|
|
279
|
+
reasoningOptional: false
|
|
273
280
|
}];
|
|
281
|
+
/** Whether two effort level lists name the same levels in the same order. */
|
|
282
|
+
function sameEffortLevels(a, b) {
|
|
283
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
284
|
+
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
285
|
+
}
|
|
274
286
|
/** Model with the same id, from a newer snapshot. */
|
|
275
287
|
function sameCatalog(a, b) {
|
|
276
288
|
if (a.length !== b.length) return false;
|
|
277
289
|
return a.every((model, index) => {
|
|
278
290
|
const other = b[index];
|
|
279
|
-
return model.id === other.id && model.contextWindow === other.contextWindow && model.toolCall === other.toolCall && model.reasoning === other.reasoning && model.attachment === other.attachment;
|
|
291
|
+
return model.id === other.id && model.contextWindow === other.contextWindow && model.toolCall === other.toolCall && model.reasoning === other.reasoning && model.attachment === other.attachment && model.reasoningOptional === other.reasoningOptional && sameEffortLevels(model.effortLevels, other.effortLevels);
|
|
280
292
|
});
|
|
281
293
|
}
|
|
282
294
|
/** Keep only models whose id the upstream keys them by. */
|
|
@@ -313,6 +325,37 @@ function readContextWindow(raw, limit) {
|
|
|
313
325
|
}
|
|
314
326
|
return typeof limit.context === "number" ? limit.context : 2e5;
|
|
315
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Read upstream's `thinking_config.mode`.
|
|
330
|
+
*
|
|
331
|
+
* `switchable` is the only mode whose reasoning can be turned off; every
|
|
332
|
+
* other value — including a missing block, which the gateway treats as
|
|
333
|
+
* mandatory reasoning — leaves the model's reasoning required.
|
|
334
|
+
*
|
|
335
|
+
* @param raw - The upstream model entry.
|
|
336
|
+
* @returns Whether reasoning is optional, or undefined when upstream said nothing.
|
|
337
|
+
*/
|
|
338
|
+
function readReasoningOptional(raw) {
|
|
339
|
+
const mode = (raw.thinking_config ?? {}).mode;
|
|
340
|
+
if (mode === "switchable") return true;
|
|
341
|
+
if (mode === "forced_on") return false;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Read upstream's `effort_options`.
|
|
345
|
+
*
|
|
346
|
+
* The levels are recorded as upstream names them; nothing maps them onto a
|
|
347
|
+
* request yet, because pi-ai's level mapping dispatches the budget form for
|
|
348
|
+
* these models and the effort levels would need the adaptive form to travel.
|
|
349
|
+
*
|
|
350
|
+
* @param raw - The upstream model entry.
|
|
351
|
+
* @returns The declared levels, or undefined when upstream declares none.
|
|
352
|
+
*/
|
|
353
|
+
function readEffortLevels(raw) {
|
|
354
|
+
const options = raw.effort_options;
|
|
355
|
+
if (!Array.isArray(options)) return void 0;
|
|
356
|
+
const names = options.filter((value) => typeof value === "string");
|
|
357
|
+
return names.length > 0 ? names : void 0;
|
|
358
|
+
}
|
|
316
359
|
/** Read the fields the upstream sends, tolerating their absence. */
|
|
317
360
|
function readModel(id, raw) {
|
|
318
361
|
const limit = raw.limit ?? {};
|
|
@@ -327,7 +370,9 @@ function readModel(id, raw) {
|
|
|
327
370
|
toolCall: raw.tool_call === true,
|
|
328
371
|
reasoning: raw.reasoning === true,
|
|
329
372
|
inputModalities: input,
|
|
330
|
-
attachment: raw.attachment === true || input.includes("image")
|
|
373
|
+
attachment: raw.attachment === true || input.includes("image"),
|
|
374
|
+
reasoningOptional: readReasoningOptional(raw),
|
|
375
|
+
effortLevels: readEffortLevels(raw)
|
|
331
376
|
};
|
|
332
377
|
}
|
|
333
378
|
/**
|
|
@@ -500,12 +545,16 @@ const NO_COST = {
|
|
|
500
545
|
* anthropic client sends the resolved apiKey as `x-api-key`, and MiniMax
|
|
501
546
|
* rejects that alone with 401 "token is required"; it accepts both headers, so
|
|
502
547
|
* the bearer value is added alongside rather than replacing it.
|
|
503
|
-
* - `thinkingLevelMap.off
|
|
504
|
-
*
|
|
505
|
-
* `
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
548
|
+
* - `thinkingLevelMap.off` is how a model says "reasoning cannot be turned
|
|
549
|
+
* off". pi-ai answers an explicit `thinkingEnabled: false` with
|
|
550
|
+
* `thinking: {type: "disabled"}` whenever `off` is not `null`, and omits the
|
|
551
|
+
* field entirely when it is. Which of the two a model needs is upstream's
|
|
552
|
+
* call, not this plugin's: `thinking_config.mode` is `switchable` for a model
|
|
553
|
+
* that accepts the disabled form and `forced_on` for one that refuses it with
|
|
554
|
+
* `400 ... requires adaptive thinking` (both measured on 2026-11-10). So the
|
|
555
|
+
* restriction is declared only where it is real — pinning `off` to `null` for
|
|
556
|
+
* a switchable model would make its reasoning impossible to turn off, and
|
|
557
|
+
* leaving it unset for a forced_on model would make the gateway answer 400.
|
|
509
558
|
*
|
|
510
559
|
* @param model - Catalog entry to describe.
|
|
511
560
|
* @param baseUrl - Region gateway base the anthropic client extends.
|
|
@@ -526,7 +575,7 @@ function toPiModel(model, baseUrl, authorization) {
|
|
|
526
575
|
contextWindow: model.contextWindow,
|
|
527
576
|
maxTokens: model.maxOutputTokens ?? 128e3,
|
|
528
577
|
...authorization === void 0 ? {} : { headers: { authorization } },
|
|
529
|
-
...model.reasoning ? { thinkingLevelMap: { off: null } } : {}
|
|
578
|
+
...model.reasoning ? model.reasoningOptional === true ? {} : { thinkingLevelMap: { off: null } } : {}
|
|
530
579
|
};
|
|
531
580
|
}
|
|
532
581
|
/**
|
|
@@ -717,7 +766,8 @@ function statusDocument(deps) {
|
|
|
717
766
|
...deps.rateLimit === void 0 ? {} : { rateLimit: deps.rateLimit },
|
|
718
767
|
...deps.usage === void 0 ? {} : { usage: deps.usage },
|
|
719
768
|
...deps.signin === void 0 ? {} : { signin: deps.signin },
|
|
720
|
-
...deps.login === void 0 ? {} : { login: deps.login }
|
|
769
|
+
...deps.login === void 0 ? {} : { login: deps.login },
|
|
770
|
+
...deps.usageProfile === void 0 ? {} : { usageProfile: deps.usageProfile }
|
|
721
771
|
};
|
|
722
772
|
if (auth.state === "expired") return {
|
|
723
773
|
...base,
|
|
@@ -793,6 +843,8 @@ const MINIMAX_OPEN_HOSTS = {
|
|
|
793
843
|
const MEMBERSHIP_PATH = "/matrix/api/v1/commerce/get_membership_info";
|
|
794
844
|
/** Credit ledger endpoint: per-grant amounts and expiry. */
|
|
795
845
|
const CREDIT_DETAILS_PATH = "/minimax-cloud/api/v1/credit/details";
|
|
846
|
+
/** Per-model token usage summary (open platform). */
|
|
847
|
+
const USAGE_SUMMARY_PATH = "/backend/account/token_plan/usage_summary";
|
|
796
848
|
/** Open-platform credit endpoint that carries the account's plan key. */
|
|
797
849
|
const TOKEN_PLAN_CREDIT_PATH = "/backend/account/token_plan_credit";
|
|
798
850
|
/**
|
|
@@ -1213,6 +1265,66 @@ async function fetchTokenPlanApiKey(credential, region, fetchImpl = fetch) {
|
|
|
1213
1265
|
const key = (await response.json()).api_key;
|
|
1214
1266
|
return typeof key === "string" && key.length > 0 ? key : void 0;
|
|
1215
1267
|
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Read a string field, ignoring anything that is not a string.
|
|
1270
|
+
*
|
|
1271
|
+
* The upstream's token counts carry their own unit ("826.14M"), so they are
|
|
1272
|
+
* kept as the wording it used rather than pulled apart into a number.
|
|
1273
|
+
*/
|
|
1274
|
+
function toText(value) {
|
|
1275
|
+
return typeof value === "string" ? value : void 0;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Read the usage profile out of a summary response.
|
|
1279
|
+
*
|
|
1280
|
+
* Every field is optional in practice: the upstream adds fields over time, and
|
|
1281
|
+
* an account with no history at all answers with a body that carries none of
|
|
1282
|
+
* them. A field that is missing — or that arrives in a shape this does not
|
|
1283
|
+
* recognise — is therefore reported as absent rather than as an error, so one
|
|
1284
|
+
* odd field never costs the whole profile. A body that is not an object at
|
|
1285
|
+
* all carries no profile and yields none.
|
|
1286
|
+
*/
|
|
1287
|
+
function parseUsageProfile(document) {
|
|
1288
|
+
if (typeof document !== "object" || document === null || Array.isArray(document)) return void 0;
|
|
1289
|
+
const record = document;
|
|
1290
|
+
const day = record.most_active_day;
|
|
1291
|
+
const mostActiveDay = typeof day === "object" && day !== null ? day : void 0;
|
|
1292
|
+
const date = toText(mostActiveDay?.date);
|
|
1293
|
+
const tokenCount = toText(mostActiveDay?.token_count);
|
|
1294
|
+
const daily = Array.isArray(record.daily_token_usage) ? record.daily_token_usage.filter((entry) => typeof entry === "number" && Number.isFinite(entry)) : void 0;
|
|
1295
|
+
return {
|
|
1296
|
+
totalDays: toNumber$1(record.total_days),
|
|
1297
|
+
activeDays: toNumber$1(record.active_days),
|
|
1298
|
+
currentConsecutiveDays: toNumber$1(record.current_consecutive_days),
|
|
1299
|
+
totalTokenConsumed: toText(record.total_token_consumed),
|
|
1300
|
+
usageRankingPercent: toNumber$1(record.usage_ranking_percent),
|
|
1301
|
+
mostActiveDay: date === void 0 && tokenCount === void 0 ? void 0 : {
|
|
1302
|
+
date,
|
|
1303
|
+
tokenCount
|
|
1304
|
+
},
|
|
1305
|
+
lastUpdateTime: toText(record.last_update_time),
|
|
1306
|
+
dailyTokenUsage: daily
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Fetch the account's usage profile.
|
|
1311
|
+
*
|
|
1312
|
+
* The endpoint lives on the open-platform host and takes the same OAuth
|
|
1313
|
+
* bearer the membership endpoint takes; the JWT the model gateway accepts
|
|
1314
|
+
* does not work here. The query is the one the desktop app sends, and it is
|
|
1315
|
+
* not trimmed down: `token_type` picks which token kind is counted and
|
|
1316
|
+
* `heatmap_days` sets how far back the daily series reaches.
|
|
1317
|
+
*
|
|
1318
|
+
* @returns The profile, or `undefined` when the response carries no object.
|
|
1319
|
+
*/
|
|
1320
|
+
async function fetchUsageProfile(credential, region, fetchImpl = fetch) {
|
|
1321
|
+
const response = await fetchImpl(`${MINIMAX_OPEN_HOSTS[region]}${USAGE_SUMMARY_PATH}?token_type=5&heatmap_days=365&business=Agent&device_platform=desktop`, { headers: {
|
|
1322
|
+
accept: "application/json",
|
|
1323
|
+
authorization: `Bearer ${credential.token}`
|
|
1324
|
+
} });
|
|
1325
|
+
if (!response.ok) throw new Error(`usage summary request failed: HTTP ${response.status}`);
|
|
1326
|
+
return parseUsageProfile(await response.json());
|
|
1327
|
+
}
|
|
1216
1328
|
/** The upstream's own code for "this account has no plan subscription". */
|
|
1217
1329
|
const NO_SUBSCRIPTION_CODE = 2062;
|
|
1218
1330
|
/** The upstream's own code for "the request carried no usable API key". */
|
|
@@ -1289,6 +1401,8 @@ var MinimaxCredits = class {
|
|
|
1289
1401
|
#rateLimitError;
|
|
1290
1402
|
#rateLimitAbsence;
|
|
1291
1403
|
#rateLimitAtMs;
|
|
1404
|
+
#usageProfile;
|
|
1405
|
+
#usageProfileError;
|
|
1292
1406
|
#apiKey;
|
|
1293
1407
|
/** The balance as last read. */
|
|
1294
1408
|
get snapshot() {
|
|
@@ -1330,14 +1444,22 @@ var MinimaxCredits = class {
|
|
|
1330
1444
|
get rateLimitAtMs() {
|
|
1331
1445
|
return this.#rateLimitAtMs;
|
|
1332
1446
|
}
|
|
1447
|
+
/** The account's usage profile as last read. */
|
|
1448
|
+
get usageProfile() {
|
|
1449
|
+
return this.#usageProfile;
|
|
1450
|
+
}
|
|
1451
|
+
/** Why the usage profile could not be read, when it could not. */
|
|
1452
|
+
get usageProfileError() {
|
|
1453
|
+
return this.#usageProfileError;
|
|
1454
|
+
}
|
|
1333
1455
|
/**
|
|
1334
1456
|
* Read the balance and record how much was consumed since the last read.
|
|
1335
1457
|
*
|
|
1336
1458
|
* The credit ledger is read alongside it, but a ledger that cannot be read
|
|
1337
1459
|
* never hides the balance: the two come from different endpoints and only
|
|
1338
|
-
* the balance is essential. The rate-limit windows
|
|
1339
|
-
* same rule — they come from yet
|
|
1340
|
-
* takes the others down with it.
|
|
1460
|
+
* the balance is essential. The rate-limit windows and the usage profile
|
|
1461
|
+
* ride along under the same rule — they come from yet other endpoints, and
|
|
1462
|
+
* none of the four takes the others down with it.
|
|
1341
1463
|
*
|
|
1342
1464
|
* @returns Whether the balance changed.
|
|
1343
1465
|
*/
|
|
@@ -1361,6 +1483,7 @@ var MinimaxCredits = class {
|
|
|
1361
1483
|
}
|
|
1362
1484
|
await this.refreshLedger(credential, region, fetchImpl, now);
|
|
1363
1485
|
await this.refreshRateLimit(credential, region, fetchImpl, now);
|
|
1486
|
+
await this.refreshUsageProfile(credential, region, fetchImpl);
|
|
1364
1487
|
return changed;
|
|
1365
1488
|
}
|
|
1366
1489
|
/**
|
|
@@ -1399,6 +1522,21 @@ var MinimaxCredits = class {
|
|
|
1399
1522
|
}
|
|
1400
1523
|
}
|
|
1401
1524
|
/**
|
|
1525
|
+
* Read the account's usage profile.
|
|
1526
|
+
*
|
|
1527
|
+
* Same rule as the ledger: a failure is remembered but never thrown, and a
|
|
1528
|
+
* profile that cannot be read leaves the last one on display.
|
|
1529
|
+
*/
|
|
1530
|
+
async refreshUsageProfile(credential, region, fetchImpl = fetch) {
|
|
1531
|
+
try {
|
|
1532
|
+
const profile = await fetchUsageProfile(credential, region, fetchImpl);
|
|
1533
|
+
this.#usageProfile = profile;
|
|
1534
|
+
this.#usageProfileError = void 0;
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
this.#usageProfileError = error instanceof Error ? error.message : String(error);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1402
1540
|
* Read the windows from whichever endpoint can answer.
|
|
1403
1541
|
*
|
|
1404
1542
|
* The documented endpoint needs the open platform's plan key, which the
|
|
@@ -2472,6 +2610,31 @@ function apply(ctx) {
|
|
|
2472
2610
|
...credits.ledgerError === void 0 ? {} : { ledgerError: credits.ledgerError }
|
|
2473
2611
|
};
|
|
2474
2612
|
};
|
|
2613
|
+
/**
|
|
2614
|
+
* The account's usage profile as the card reads it.
|
|
2615
|
+
*
|
|
2616
|
+
* The upstream's own summary of the account's history: how many days it has
|
|
2617
|
+
* been used in a row, how much it has consumed, and where it ranks. It is
|
|
2618
|
+
* about the account rather than about this host, which is what the pill's
|
|
2619
|
+
* own token count reports.
|
|
2620
|
+
*/
|
|
2621
|
+
const buildUsageProfileStatus = () => {
|
|
2622
|
+
const profile = credits.usageProfile;
|
|
2623
|
+
if (profile === void 0) return credits.usageProfileError === void 0 ? void 0 : {
|
|
2624
|
+
state: "unavailable",
|
|
2625
|
+
error: credits.usageProfileError
|
|
2626
|
+
};
|
|
2627
|
+
return {
|
|
2628
|
+
state: "ok",
|
|
2629
|
+
totalDays: profile.totalDays,
|
|
2630
|
+
activeDays: profile.activeDays,
|
|
2631
|
+
consecutiveDays: profile.currentConsecutiveDays,
|
|
2632
|
+
totalTokenConsumed: profile.totalTokenConsumed,
|
|
2633
|
+
rankingPercent: profile.usageRankingPercent,
|
|
2634
|
+
mostActiveDay: profile.mostActiveDay,
|
|
2635
|
+
updatedAt: profile.lastUpdateTime
|
|
2636
|
+
};
|
|
2637
|
+
};
|
|
2475
2638
|
/** The plan's rolling usage windows as the card reads them. */
|
|
2476
2639
|
const buildRateLimitStatus = () => {
|
|
2477
2640
|
if (creditState === "signed-out" || creditState === "expired") return { state: creditState };
|
|
@@ -2619,7 +2782,8 @@ function apply(ctx) {
|
|
|
2619
2782
|
rateLimit: buildRateLimitStatus(),
|
|
2620
2783
|
usage: buildUsageStatus(),
|
|
2621
2784
|
signin: buildSigninStatus(),
|
|
2622
|
-
login: buildLoginStatus()
|
|
2785
|
+
login: buildLoginStatus(),
|
|
2786
|
+
usageProfile: buildUsageProfileStatus()
|
|
2623
2787
|
});
|
|
2624
2788
|
};
|
|
2625
2789
|
(async () => {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-minimaxcode",
|
|
3
3
|
"displayName": "DSH Connect MiniMax Code",
|
|
4
4
|
"description": "把本机已登录的 MiniMax Code 桌面 App 模型接入 DeepSeek Harness:零配置、Anthropic Messages 兼容、动态模型目录,并显示积分余额与到期 — bring MiniMax Code desktop app models into DSH with a dynamic model catalog and a credit balance with expiry details.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.8",
|
|
6
6
|
"author": "JeffTsuiCUG",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|