dsh-connect-minimaxcode 0.5.7 → 0.5.9
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 +53 -37
- package/README.md +53 -37
- 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
|
@@ -10,19 +10,17 @@ Bring the models of the **MiniMax Code desktop app (China region)**, already sig
|
|
|
10
10
|
|
|
11
11
|
## Inspiration
|
|
12
12
|
|
|
13
|
-
The
|
|
13
|
+
The community established the route this plugin follows: **a model already signed in on a desktop app can be moved into DSH with zero configuration** — no handling of accounts or passwords, no new authentication scheme; it just attaches a provider to the login state the machine already has and reuses DSH's built-in provider adapter. This plugin applies the same approach to MiniMax Code:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| | dsh-workbuddy-connect | This plugin |
|
|
15
|
+
| | Sibling plugins | This plugin |
|
|
18
16
|
|---|---|---|
|
|
19
|
-
| Data source | Local login state of
|
|
17
|
+
| Data source | Local login state of their own desktop app | Local login state of the MiniMax Code desktop app |
|
|
20
18
|
| Integration | Registers a DSH provider and exposes the already-signed-in models | Same |
|
|
21
19
|
| Catalog source | Live model list from upstream | Same |
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
Several implementations exist in the same direction; for **how the plugin is structured and how the provider is registered**, this plugin follows **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** and the same author's [dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae).
|
|
24
22
|
|
|
25
|
-
>
|
|
23
|
+
> These projects are developed independently and are not affiliated with this one. This plugin's directory layout and `cordis.patch.yml` follow the existing conventions of the DSH plugin structure.
|
|
26
24
|
|
|
27
25
|
---
|
|
28
26
|
|
|
@@ -283,6 +281,27 @@ Two sets of data each cover one thing:
|
|
|
283
281
|
|
|
284
282
|
> 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
283
|
|
|
284
|
+
### Account usage profile
|
|
285
|
+
|
|
286
|
+
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.
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
GET https://www.minimaxi.com/backend/account/token_plan/usage_summary?token_type=5&heatmap_days=365&business=Agent&device_platform=desktop
|
|
290
|
+
Authorization: Bearer <OAuth 令牌>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The card shows the parts worth acting on:
|
|
294
|
+
|
|
295
|
+
| Field | Meaning |
|
|
296
|
+
| --- | --- |
|
|
297
|
+
| `current_consecutive_days` | Days used in a row |
|
|
298
|
+
| `active_days` / `total_days` | Days with usage in the window / the window's length |
|
|
299
|
+
| `total_token_consumed` | Tokens consumed in the window, in the upstream's own wording (`826.14M`) |
|
|
300
|
+
| `usage_ranking_percent` | Where the account ranks by usage |
|
|
301
|
+
| `most_active_day` | The busiest day and what it consumed |
|
|
302
|
+
|
|
303
|
+
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.
|
|
304
|
+
|
|
286
305
|
### About token refresh
|
|
287
306
|
|
|
288
307
|
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 +327,20 @@ The OAuth token is valid for about an hour and is refreshed automatically by the
|
|
|
308
327
|
|
|
309
328
|
## Changelog
|
|
310
329
|
|
|
330
|
+
### 0.5.9 (2026-11-10)
|
|
331
|
+
|
|
332
|
+
- **README**: every reference to a repository that no longer exists (it now answers 404) is gone — the inspiration section, the sibling-plugin table and its section, and the acknowledgements. The practice it established and the sibling projects that followed it are still described, and every repository link left in the README was checked to be alive.
|
|
333
|
+
- **README**: the marketplace section now states the situation as it stands — all twelve listing pull requests are closed, the forks opened for them are cleaned up, those directories are not being worked on, and distribution runs through npm and this repository's releases. The two registries that still carry an entry (the official registry and dshbase) are still listed. The npm package ships the README, so this change needs a release to reach the package page.
|
|
334
|
+
|
|
335
|
+
### 0.5.8 (2026-11-10)
|
|
336
|
+
|
|
337
|
+
- **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.
|
|
338
|
+
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.
|
|
339
|
+
- **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.
|
|
340
|
+
- **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.
|
|
341
|
+
- **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.
|
|
342
|
+
- 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.
|
|
343
|
+
|
|
311
344
|
### 0.5.7 (2026-11-10)
|
|
312
345
|
|
|
313
346
|
- **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.
|
|
@@ -395,30 +428,25 @@ For earlier versions, see [GitHub Releases](https://github.com/JeffTsuiCUG/dsh-c
|
|
|
395
428
|
|
|
396
429
|
## Marketplace listing and presentation
|
|
397
430
|
|
|
398
|
-
The plugin ships an installable `dsh.bundle` manifest and is published to npm
|
|
431
|
+
The plugin ships an installable `dsh.bundle` manifest and is published to npm — **npm is the installation path**:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
dsh plugin --profile desktop add dsh-connect-minimaxcode
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
The directory of the community plugin marketplace (`dshmarket`) is generated from the [Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry, and this plugin's entry is in it ([#8110](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8110) listed, [#8380](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8380) refreshed), so it also shows up in the DSH plugin centre.
|
|
399
438
|
|
|
400
|
-
**Other directories and
|
|
439
|
+
**Other directories**: entries were submitted to several awesome lists as well, but those projects see little maintenance; the entries and the forks opened for them have been cleaned up and are **no longer maintained**. Distribution runs through npm and this repository's Releases. The original draft and the submission notes are kept in the repository: [`awesome-dsh-plugin-submission/`](awesome-dsh-plugin-submission/), in case they are ever needed again.
|
|
401
440
|
|
|
402
441
|
| Channel | State |
|
|
403
442
|
|---|---|
|
|
404
|
-
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) (the registry) | Listed
|
|
405
|
-
| [
|
|
406
|
-
|
|
|
407
|
-
| [dsh-suite](https://github.com/whyihaveyou/dsh-suite) | PR [#66](https://github.com/whyihaveyou/dsh-suite/pull/66) |
|
|
408
|
-
| [awesome-dsh-plugins](https://github.com/kejixiaoliang/awesome-dsh-plugins) | PR [#143](https://github.com/kejixiaoliang/awesome-dsh-plugins/pull/143) |
|
|
409
|
-
| [awesome-dsh-plugins](https://github.com/dshworks/awesome-dsh-plugins) | PR [#176](https://github.com/dshworks/awesome-dsh-plugins/pull/176) |
|
|
410
|
-
| [awesome-deepseek-harness](https://github.com/awesome-deepseekharness/awesome-deepseek-harness) | PR [#177](https://github.com/awesome-deepseekharness/awesome-deepseek-harness/pull/177) |
|
|
411
|
-
| [dsh-plugin-radar](https://github.com/AdamPlatin123/dsh-plugin-radar) | PR [#964](https://github.com/AdamPlatin123/dsh-plugin-radar/pull/964) |
|
|
412
|
-
| [dsh-community-plugins](https://github.com/zhu1090093659/dsh-community-plugins) | PR [#46](https://github.com/zhu1090093659/dsh-community-plugins/pull/46) |
|
|
413
|
-
| [dsh-plugin.org](https://github.com/dshplugin/dsh-plugin-hub) | Listed (automatic scan; the npm package shows up in the DSH plugin centre) |
|
|
414
|
-
| [dshbase](https://github.com/ylwl1997/dshbase) | Listed |
|
|
415
|
-
| [awesome-deepseek-harness-plugins](https://github.com/walkinglabs/awesome-deepseek-harness-plugins) | PR [#117](https://github.com/walkinglabs/awesome-deepseek-harness-plugins/pull/117) |
|
|
416
|
-
| [awesome-deepseek-harness](https://github.com/Dominic789654/awesome-deepseek-harness) | PR [#663](https://github.com/Dominic789654/awesome-deepseek-harness/pull/663) |
|
|
417
|
-
| [awesome-deepseek-harness-plugins](https://github.com/imsai-sh/awesome-deepseek-harness-plugins) | PR [#660](https://github.com/imsai-sh/awesome-deepseek-harness-plugins/pull/660) |
|
|
443
|
+
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) (the registry behind `dshmarket`) | Listed |
|
|
444
|
+
| [dshbase](https://github.com/ylwl1997/dshbase) | Listed (automatic scan) |
|
|
445
|
+
| Other awesome lists | No longer maintained; entries and forks cleaned up |
|
|
418
446
|
|
|
419
447
|
Marketplace screenshots are declared, per the current convention, by the **`screenshots.json` in the plugin's own repository root** (the registry's `data/screenshots.json` is a fallback from the old convention, and no new keys are added there). This repository **does not ship screenshots yet**, so the file does not exist; once there are some, put `screenshots.json` in the repository root (an array of relative image paths) and the marketplace card will pick them up.
|
|
420
448
|
|
|
421
|
-
> **Since 0.5.1 the readout lives in the composer tool row (beside the model picker) and is gated by the host's `modelSelection` projection**: the balance belongs to the provider the next request will actually use, so it sits where that selection is made — and it hides entirely, polling included, when another connector's model (workbuddy, for example) is selected. This follows `dsh-connect-trae`, whose `ComposerPointsGate` returns null for every provider but its own.
|
|
449
|
+
> **Since 0.5.1 the readout lives in the composer tool row (beside the model picker) and is gated by the host's `modelSelection` projection**: the balance belongs to the provider the next request will actually use, so it sits where that selection is made — and it hides entirely, polling included, when another connector's model (dsh-connect-workbuddy, for example) is selected. This follows `dsh-connect-trae`, whose `ComposerPointsGate` returns null for every provider but its own.
|
|
422
450
|
>
|
|
423
451
|
> The tool row is a shared slot and can in principle be crowded out (a sibling plugin lost a reading this way in `workbuddy` issue #62; trae's answer is a second seat). **This plugin registers the tool-row seat only**; the ambient dock is a proven fallback should crowding ever occur.
|
|
424
452
|
|
|
@@ -455,7 +483,6 @@ These plugins follow the same idea — **reuse the login state your local deskto
|
|
|
455
483
|
|---|---|---|---|
|
|
456
484
|
| **dsh-connect-trae** | Trae (China + international) | ✅ China region | ✅ Work / general credit overview |
|
|
457
485
|
| **dsh-qoder-connect** | Qoder (China + international, PAT) | ✅ 100 Credits per day | ✅ Sidebar quota card + breakdown |
|
|
458
|
-
| **dsh-workbuddy-connect** | WorkBuddy (China + international) | — | ✅ Remaining credits + breakdown |
|
|
459
486
|
|
|
460
487
|
> Each plugin's check-in rules, quota basis and limitations are defined by its own documentation. The readings above come from the same machine and only represent the state at that time; they are not a promise.
|
|
461
488
|
|
|
@@ -475,16 +502,6 @@ Connects Qoder through a personal access token (PAT), with independent configura
|
|
|
475
502
|
dsh plugin --profile desktop add dsh-qoder-connect
|
|
476
503
|
```
|
|
477
504
|
|
|
478
|
-
### [dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)
|
|
479
|
-
|
|
480
|
-
Connects the WorkBuddy desktop app; the China and international versions can coexist, each using its own account and its own credits. It shows the account information, token validity and remaining credits, and ships a CLI:
|
|
481
|
-
|
|
482
|
-
```bash
|
|
483
|
-
dsh plugin --profile web exec dsh-workbuddy-connect status
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
> This plugin's version must match your DSH core exactly (each core line uses a different version); see the version table in its README before installing.
|
|
487
|
-
|
|
488
505
|
## Contributors
|
|
489
506
|
|
|
490
507
|
- **[eghrhegpe](https://github.com/eghrhegpe)** — raised the request for a plugin-page icon and Chinese/English copy ([#1](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/issues/1)). It became 0.5.2: the plugin page has its own SVG artwork, the title and the introduction come in both languages, and the interface follows the host's language.
|
|
@@ -494,8 +511,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect status
|
|
|
494
511
|
|
|
495
512
|
## Acknowledgements
|
|
496
513
|
|
|
497
|
-
- **[
|
|
498
|
-
- **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** — another implementation in the same direction; this plugin's **plugin structure and provider registration** follow it (its connection core also references corrinehu's implementation — see that project's acknowledgements).
|
|
514
|
+
- **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** — another implementation in the same direction; this plugin's **plugin structure and provider registration** follow it.
|
|
499
515
|
- [dingminhua/dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae) — the same author's DSH plugin, useful as a reference for the DSH plugin structure.
|
|
500
516
|
|
|
501
517
|
> The projects listed under "Similar plugins" are the work of independent developers. They are not affiliated with or endorsed by this project, and each one follows its own license and disclaimer.
|
package/README.md
CHANGED
|
@@ -10,19 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
## 灵感来源
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
社区先走通了这样一条路:**桌面 App 已经登录的模型,可以零配置地搬进 DSH** —— 不去碰账号密码,不引入新的鉴权体系,只是把本机已有的登录态接上一个 provider,并复用 DSH 自带的 provider 适配器。本插件把同一套做法用在了 MiniMax Code 上:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| | dsh-workbuddy-connect | 本插件 |
|
|
15
|
+
| | 同类插件 | 本插件 |
|
|
18
16
|
|---|---|---|
|
|
19
|
-
| 数据来源 |
|
|
17
|
+
| 数据来源 | 各自桌面 App 的本地登录态 | MiniMax Code 桌面 App 的本地登录态 |
|
|
20
18
|
| 接入方式 | 注册 DSH provider,暴露已登录模型 | 同左 |
|
|
21
19
|
| 目录来源 | 上游实时模型列表 | 同左 |
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
同方向已有多个实现,本插件在**插件结构与 provider 注册的组织方式**上参考了 **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** 与同一作者的 [dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae)。
|
|
24
22
|
|
|
25
|
-
>
|
|
23
|
+
> 同类项目各自独立开发,与本项目无隶属关系。本插件的目录布局与 `cordis.patch.yml` 写法沿用了 DSH 插件结构的既有约定。
|
|
26
24
|
|
|
27
25
|
---
|
|
28
26
|
|
|
@@ -319,6 +317,27 @@ Authorization: Bearer <API Key>
|
|
|
319
317
|
- **没有订阅就没有窗口,且这不算错误**:上游对未订阅 M Plan 的账户明确回 `2062 no active token plan subscription`,插件会如实标为「无订阅」而不是「读取失败」—— 积分包本身不带滚动窗口
|
|
320
318
|
- **接口异常时其余显示不受影响**:限额读不到,胶囊与卡片仍显示余额、签到与本机用量
|
|
321
319
|
- 上游还发 `current_*_count` 计数字段(无计数制时为 `-1`),插件只读百分比与起止时刻
|
|
320
|
+
### 账户用量画像
|
|
321
|
+
|
|
322
|
+
账户的**用量历史**由开放平台的用量汇总接口给出,与「本机累计 token」是两回事 —— 后者只统计这台机器发起的请求:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
GET https://www.minimaxi.com/backend/account/token_plan/usage_summary?token_type=5&heatmap_days=365&business=Agent&device_platform=desktop
|
|
326
|
+
Authorization: Bearer <OAuth 令牌>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
卡片显示的是其中有行动价值的几项:
|
|
330
|
+
|
|
331
|
+
| 字段 | 含义 |
|
|
332
|
+
| --- | --- |
|
|
333
|
+
| `current_consecutive_days` | 连续使用天数 |
|
|
334
|
+
| `active_days` / `total_days` | 统计窗口内有使用的天数 / 窗口长度 |
|
|
335
|
+
| `total_token_consumed` | 窗口内累计消耗(上游自己的写法,如 `826.14M`) |
|
|
336
|
+
| `usage_ranking_percent` | 用量排名分位 |
|
|
337
|
+
| `most_active_day` | 最活跃的一天及其消耗 |
|
|
338
|
+
|
|
339
|
+
这一节读到的数据**不需要订阅 M Plan**:该接口属于账户域,用 OAuth 令牌即可读取,与限额窗口(属于订阅)不同。
|
|
340
|
+
|
|
322
341
|
### 关于续期
|
|
323
342
|
|
|
324
343
|
OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自己的令牌已过期时**才用 App 已存的 refresh token 续一次,并把轮换后的新令牌按客户端**原格式原子写回**同一个文件(`generation` 保持不变),因此桌面 App 的登录态不受影响。插件不会发起登录、不会登出、也不会调用任何付费/订阅类接口。
|
|
@@ -344,6 +363,20 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
|
|
|
344
363
|
|
|
345
364
|
## 更新记录
|
|
346
365
|
|
|
366
|
+
### 0.5.9(2026-11-10)
|
|
367
|
+
|
|
368
|
+
- **README**:移除指向已不存在仓库的引用(该仓库现已 404),灵感来源、同类插件与致谢三处一并清理,保留的做法与仍有效的同类项目说明不变;README 中保留的每一个仓库链接都已逐一确认存活。
|
|
369
|
+
- **README**:市场收录章节改为如实描述现状 —— 十二个收录 PR 均已关闭、为收录开的分叉已清理、那些目录不再维护,分发以 npm 与本仓库 Releases 为准;仍收录本插件的两个注册表(官方注册表与 dshbase)照旧列出。npm 包内含 README,因此本次改动需要发版才能在包页生效。
|
|
370
|
+
|
|
371
|
+
### 0.5.8(2026-11-10)
|
|
372
|
+
|
|
373
|
+
- **修复「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 的行为不变,选项仍不出现。
|
|
374
|
+
上游的 `effort_options`(M3.1-Flash 有 default/low/medium/high/xhigh/max 六档)也一并解析存档,但**未接线**:pi-ai 的档位映射目前走 budget 形式,要用上这六档得切换整个请求形态,这一步不在本次改动里。
|
|
375
|
+
- **新增「账户用量画像」**:读取开放平台的 `GET /backend/account/token_plan/usage_summary`(该常量此前定义了却从未使用),卡片显示连续使用天数、统计窗口内的活跃天数、窗口内累计消耗、用量排名分位与最活跃的一天。它说的是**账户**的历史,与胶囊里「本机累计 token」是两回事。
|
|
376
|
+
- **说明「为什么没有限额窗口」**:账户未订阅 M Plan 时,上游回 `2062 no active token plan subscription`(0.5.7 已识别)。现在卡片直接写明「未订阅 M Plan(仅积分包),因此没有限额窗口」,而不是让人以为读取失败。
|
|
377
|
+
- **接口盘点**:把桌面 App 每天真实调用的 20 个账户域接口全部盘了一遍 —— 其中 **19 个用 OAuth 令牌即可读取,无需任何订阅**,只有「限额窗口」属于订阅本身。也就是说订阅 M Plan 换来的不是「更多接口」,而是**限额窗口**与**更高额度**这两样。
|
|
378
|
+
- 新增 `tests/usage-profile.spec.ts`(16 个用例)与 `tests/catalog.spec.ts` / `tests/adapter.spec.ts` 的推理模式用例。全量 236 个用例通过。
|
|
379
|
+
|
|
347
380
|
### 0.5.7(2026-11-10)
|
|
348
381
|
|
|
349
382
|
- **限额窗口改走官方文档接口**:原先读的是 `/backend/account/token_plan/remains_percent`,这条路径现在对任何账户都只回一个空的 `success`(连 `model_remains` 字段都没有),于是限额一栏对所有人都显示不出来,却看不出原因。改用开放平台官方文档(M Plan 用量说明页)给出的 `GET /v1/token_plan/remains`。新接口要求**开放平台 API 密钥**而非 OAuth 令牌,密钥取自 `token_plan_credit` 的响应并缓存复用。
|
|
@@ -387,7 +420,7 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
|
|
|
387
420
|
|
|
388
421
|
### 0.5.1(2026-10-20)
|
|
389
422
|
|
|
390
|
-
- **余额读数跟随所选模型**:从输入框下方的 dock 移到**输入框工具行(模型选择器旁)**,并按宿主 `modelSelection` 投影门控 —— **只有选中 `minimaxcode` 模型时才显示**;切到其他反代插件(如 workbuddy)的模型即整体隐藏,且不轮询(学习 `dsh-connect-trae` 的做法)。
|
|
423
|
+
- **余额读数跟随所选模型**:从输入框下方的 dock 移到**输入框工具行(模型选择器旁)**,并按宿主 `modelSelection` 投影门控 —— **只有选中 `minimaxcode` 模型时才显示**;切到其他反代插件(如 dsh-connect-workbuddy)的模型即整体隐藏,且不轮询(学习 `dsh-connect-trae` 的做法)。
|
|
391
424
|
- **`engines.dsh` 收窄为 `>=0.2.1-rc.2 <0.3.0-0`**:只声明实测过的档位,此前的 `>=0.1.7-rc.1` 覆盖过宽。
|
|
392
425
|
|
|
393
426
|
### 0.5.0(2026-10-20)
|
|
@@ -433,26 +466,21 @@ OAuth 令牌有效期约 1 小时,由客户端自动续期。插件**只在自
|
|
|
433
466
|
|
|
434
467
|
## 市场收录与展示
|
|
435
468
|
|
|
436
|
-
插件已包含可安装的 `dsh.bundle` manifest,并发布到 npm
|
|
469
|
+
插件已包含可安装的 `dsh.bundle` manifest,并发布到 npm —— **安装方式以 npm 为准**:
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
dsh plugin --profile desktop add dsh-connect-minimaxcode
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
社区插件市场(`dshmarket`)的目录由 [Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) 注册表生成,本插件的条目已在该注册表中([#8110](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8110) 收录、[#8380](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8380) 更新),因此也会出现在 DSH 的插件中心里。
|
|
437
476
|
|
|
438
|
-
|
|
477
|
+
**其它目录**:此前也向若干 awesome 清单提交过收录条目,但那些项目的更新活跃度很低,条目与为此开的分叉都已清理完毕,**不再维护**。插件的分发以 npm 与本仓库的 Releases 为准。仓库内保留了一份当初的草稿与提交说明:[`awesome-dsh-plugin-submission/`](awesome-dsh-plugin-submission/),供日后需要时参考。
|
|
439
478
|
|
|
440
479
|
| 渠道 | 状态 |
|
|
441
480
|
|---|---|
|
|
442
|
-
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
443
|
-
| [
|
|
444
|
-
|
|
|
445
|
-
| [dsh-suite](https://github.com/whyihaveyou/dsh-suite) | PR [#66](https://github.com/whyihaveyou/dsh-suite/pull/66) |
|
|
446
|
-
| [awesome-dsh-plugins](https://github.com/kejixiaoliang/awesome-dsh-plugins) | PR [#143](https://github.com/kejixiaoliang/awesome-dsh-plugins/pull/143) |
|
|
447
|
-
| [awesome-dsh-plugins](https://github.com/dshworks/awesome-dsh-plugins) | PR [#176](https://github.com/dshworks/awesome-dsh-plugins/pull/176) |
|
|
448
|
-
| [awesome-deepseek-harness](https://github.com/awesome-deepseekharness/awesome-deepseek-harness) | PR [#177](https://github.com/awesome-deepseekharness/awesome-deepseek-harness/pull/177) |
|
|
449
|
-
| [dsh-plugin-radar](https://github.com/AdamPlatin123/dsh-plugin-radar) | PR [#964](https://github.com/AdamPlatin123/dsh-plugin-radar/pull/964) |
|
|
450
|
-
| [dsh-community-plugins](https://github.com/zhu1090093659/dsh-community-plugins) | PR [#46](https://github.com/zhu1090093659/dsh-community-plugins/pull/46) |
|
|
451
|
-
| [dsh-plugin.org](https://github.com/dshplugin/dsh-plugin-hub) | ✅ 已收录(自助扫描,会随目录更新出现在 DSH「插件中心」) |
|
|
452
|
-
| [dshbase](https://github.com/ylwl1997/dshbase) | ✅ 已收录 |
|
|
453
|
-
| [awesome-deepseek-harness-plugins](https://github.com/walkinglabs/awesome-deepseek-harness-plugins) | PR [#117](https://github.com/walkinglabs/awesome-deepseek-harness-plugins/pull/117) |
|
|
454
|
-
| [awesome-deepseek-harness](https://github.com/Dominic789654/awesome-deepseek-harness) | PR [#663](https://github.com/Dominic789654/awesome-deepseek-harness/pull/663) |
|
|
455
|
-
| [awesome-deepseek-harness-plugins](https://github.com/imsai-sh/awesome-deepseek-harness-plugins) | PR [#660](https://github.com/imsai-sh/awesome-deepseek-harness-plugins/pull/660) |
|
|
481
|
+
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)(官方注册表,`dshmarket` 目录来源) | ✅ 已收录 |
|
|
482
|
+
| [dshbase](https://github.com/ylwl1997/dshbase) | ✅ 已收录(自动扫描) |
|
|
483
|
+
| 其它 awesome 清单 | 已停止维护,条目与分叉已清理 |
|
|
456
484
|
|
|
457
485
|
市场截图按当前约定由**插件自己仓库根目录的** `screenshots.json` 声明(注册表 `data/screenshots.json` 是旧约定下的回退,不再新增键)。本仓库目前**尚未提供截图**,所以还没有这个文件;补上截图后,在根目录放 `screenshots.json`(内容是图片相对路径的数组)即可被市场卡片取用。
|
|
458
486
|
|
|
@@ -493,7 +521,6 @@ pnpm build && git add lib
|
|
|
493
521
|
|---|---|---|---|
|
|
494
522
|
| **dsh-connect-trae** | Trae(国内版 + 国际版) | ✅ 国内版 | ✅ Work / 通用积分概览 |
|
|
495
523
|
| **dsh-qoder-connect** | Qoder(国内版 + 国际版,PAT) | ✅ 每日 100 Credits | ✅ 侧栏额度小卡 + 明细 |
|
|
496
|
-
| **dsh-workbuddy-connect** | WorkBuddy(国内版 + 国际版) | — | ✅ 剩余积分 + 明细 |
|
|
497
524
|
|
|
498
525
|
> 各插件的签到规则、额度口径与限制以各自文档为准。下表为同一台机器上的实测读数,仅代表当时状态,非承诺。
|
|
499
526
|
|
|
@@ -513,16 +540,6 @@ dsh plugin --profile desktop add dsh-connect-trae
|
|
|
513
540
|
dsh plugin --profile desktop add dsh-qoder-connect
|
|
514
541
|
```
|
|
515
542
|
|
|
516
|
-
### [dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)
|
|
517
|
-
|
|
518
|
-
接入 WorkBuddy 桌面 App,国内版与国际版可并存,各自使用自己的账号与积分。提供账号信息、令牌有效期与剩余积分查看,并带 CLI:
|
|
519
|
-
|
|
520
|
-
```bash
|
|
521
|
-
dsh plugin --profile web exec dsh-workbuddy-connect status
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
> 该插件的版本需与你的 DSH 内核严格对应(不同内核线用不同版本),安装前请见其 README 的版本对应表。
|
|
525
|
-
|
|
526
543
|
## 贡献者
|
|
527
544
|
|
|
528
545
|
- **[eghrhegpe](https://github.com/eghrhegpe)** —— 提出了插件页图标与中英文文案的建议([#1](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/issues/1))。这条建议促成了 0.5.2:插件页有了自己的 SVG 图标,标题与简介有了中英两版,界面文案也开始跟随宿主语言。
|
|
@@ -532,8 +549,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect status
|
|
|
532
549
|
|
|
533
550
|
## 致谢
|
|
534
551
|
|
|
535
|
-
- **[
|
|
536
|
-
- **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** — 同方向的另一实现,本插件的**插件结构与 provider 注册组织方式**参考了它(其连接内核亦参照 corrinehu 的实现,见该项目致谢)。
|
|
552
|
+
- **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)** — 同方向的另一实现,本插件的**插件结构与 provider 注册组织方式**参考了它。
|
|
537
553
|
- [dingminhua/dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae) — 同作者的 DSH 插件,可作为 DSH 插件结构的参照。
|
|
538
554
|
|
|
539
555
|
> 上方「同类插件」中的项目均为独立开发者的作品,与本项目无隶属或背书关系,各自遵循其自身的许可与免责声明。
|
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.9",
|
|
6
6
|
"author": "JeffTsuiCUG",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|