dsh-connect-minimaxcode 0.5.5 → 0.5.6
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 +497 -490
- package/README.md +8 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.js +150 -18
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -1,490 +1,497 @@
|
|
|
1
|
-
# DSH Connect MiniMax Code (`dsh-connect-minimaxcode`)
|
|
2
|
-
|
|
3
|
-
Bring the models of the **MiniMax Code desktop app (China region)**, already signed in on this machine, into **DeepSeek Harness (DSH)**. Install it and it works — there is no API key to configure.
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
[](https://www.npmjs.com/package/dsh-connect-minimaxcode)
|
|
7
|
-
[](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/actions/workflows/ci.yml)
|
|
8
|
-
|
|
9
|
-
[简体中文](README.md) | English
|
|
10
|
-
|
|
11
|
-
## Inspiration
|
|
12
|
-
|
|
13
|
-
The idea for this plugin came from **[corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)** (by Corrine Hu).
|
|
14
|
-
|
|
15
|
-
That project was the first to show that **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:
|
|
16
|
-
|
|
17
|
-
| | dsh-workbuddy-connect | This plugin |
|
|
18
|
-
|---|---|---|
|
|
19
|
-
| Data source | Local login state of the WorkBuddy desktop app | Local login state of the MiniMax Code desktop app |
|
|
20
|
-
| Integration | Registers a DSH provider and exposes the already-signed-in models | Same |
|
|
21
|
-
| Catalog source | Live model list from upstream | Same |
|
|
22
|
-
|
|
23
|
-
Another implementation in the same direction is **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)**; for **how the plugin is structured and how the provider is registered**, this plugin also follows the same author's [dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae) (whose connection core itself references corrinehu's implementation — see the acknowledgements in its README).
|
|
24
|
-
|
|
25
|
-
> The two differ in this way: **corrinehu was the original pioneer** — the "reuse the local login state" route was established by that project; **dingminhua's implementation follows the existing conventions of the DSH plugin structure more closely**, and this plugin's directory layout and `cordis.patch.yml` follow the latter. Both are credited at the end of this document.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## ⚠️ Important notice (read this first)
|
|
30
|
-
|
|
31
|
-
- **This project is for learning and research only.** It is not aimed at production use and comes with no commercial support.
|
|
32
|
-
- **This is an unofficial plugin.** It is not affiliated with MiniMax or DeepSeek, and is not endorsed or authorized by either of them.
|
|
33
|
-
- The plugin only **reads the credentials of the MiniMax Code account already signed in on your own machine**, and only drives **your own account on your own computer**.
|
|
34
|
-
- **Do not** use this plugin for commercial purposes, beyond reasonable personal use, or for any bulk / multi-account / limit-evasion purpose.
|
|
35
|
-
- The plugin depends on **undocumented (non-public) MiniMax client APIs**. When MiniMax updates its client, those APIs may change and the plugin may stop working.
|
|
36
|
-
- You are responsible for complying with the [MiniMax Terms of Service](https://agent.minimax.cn/doc/zh/terms-of-service.html). You bear all consequences of using this plugin (account restrictions, quota resets, service interruption, and so on).
|
|
37
|
-
- The authors of this project are not liable for any direct or indirect loss caused by the use or misuse of this plugin.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Highlights
|
|
42
|
-
|
|
43
|
-
- **Zero configuration**: the login state of the MiniMax Code desktop app is read automatically, and no plaintext token is ever stored in DSH
|
|
44
|
-
- **Dynamic model catalog**: pulled live from the upstream API, so upstream adding or removing models needs no plugin update
|
|
45
|
-
- **Credit balance at a glance**: the pill in the composer tool row (beside the model picker) shows the remaining MiniMax Code credits and the consumption accumulated on this machine
|
|
46
|
-
- **Live usage accounting**: the token usage of every request is taken from the upstream response itself, and can be converted to an equivalent cost using the official price list
|
|
47
|
-
- **Reuses the official adapter**: upstream speaks the standard Anthropic Messages protocol, so DSH's built-in provider is reused as is; tool calls, reasoning blocks and context compaction are all handled by DSH
|
|
48
|
-
- **Works without the desktop app**: the plugin can sign in on its own with the OAuth device code flow; you confirm in your own browser and the plugin never sees a password
|
|
49
|
-
- **Daily check-in**: MiniMax Code grants credits every day and the desktop app does **not** claim them for you; the plugin shows the seven-day cycle and can claim the day's grant
|
|
50
|
-
- **Read-mostly**: apart from "refreshing an expired desktop app login state" and the sign-in you start yourself, the plugin does not write to or modify any MiniMax file
|
|
51
|
-
- **Follows the selected model**: the balance sits in the **composer tool row beside the model picker**, and is drawn **only while a MiniMax model is selected** — picking another connector's model hides it entirely instead of misleading you with MiniMax's balance
|
|
52
|
-
- **An icon and two languages of its own**: a 48×48 SVG ships with the package, so the plugin page no longer shows the default placeholder; the title and the introduction come in Chinese and English, and the interface follows the host's language
|
|
53
|
-
- **Image input**: models whose catalog entry declares the `image` input modality (`M3`, `M3.1-Flash`) take pictures directly; the host's attachment service reads the image into request bytes, and the plugin only has to connect the durable reference to it
|
|
54
|
-
|
|
55
|
-
## Prerequisites
|
|
56
|
-
|
|
57
|
-
Either:
|
|
58
|
-
|
|
59
|
-
1. **The MiniMax Code desktop app (China region) installed and signed in** — the plugin reuses its sign-in (the easy way)
|
|
60
|
-
2. **Nothing installed** — press "Sign in to MiniMax Code" on the plugin page; the plugin starts an OAuth device code flow and you confirm with your own MiniMax account in a browser (this needs `account.minimax.cn` to be reachable)
|
|
61
|
-
|
|
62
|
-
Plus DSH desktop (built-in core `>=0.2.1-rc.2 <0.3.0`, see `engines.dsh` in `package.json`; tested on `0.2.1-rc.2`) or the Web profile.
|
|
63
|
-
|
|
64
|
-
> **Sending a picture needs the host's attachment service**: the plugin does not read images itself — the host's durable attachment service turns a stored reference into request bytes, and the plugin only connects the two. On a host without that service the plugin still loads and text still works; only image requests are refused (`pi-ai image input requires the durable attachment service`).
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
67
|
-
|
|
68
|
-
**Option 1: install from the Git repository** (recommended, gets the latest code)
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
dsh plugin --profile desktop add github:JeffTsuiCUG/dsh-connect-minimaxcode
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**Option 2: install from npm** (available once published)
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
dsh plugin --profile desktop add dsh-connect-minimaxcode
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
For the Web profile, replace `--profile desktop` with `--profile web`.
|
|
81
|
-
|
|
82
|
-
> **Installing a locally modified clone**: do not pass a directory path to `dsh plugin add` — pnpm installs it as a link and nests dependency symlinks inside the plugin's own `node_modules`; the host's plugin sandbox gets `EPERM` stat-ing those links and the plugin fails to activate (`failed to import`). Pack a tarball first, then install the tarball so dependencies resolve in the profile root `node_modules` like a registry install:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
pnpm pack
|
|
86
|
-
dsh plugin --profile desktop add ./dsh-connect-minimaxcode-0.5.
|
|
87
|
-
```
|
|
88
|
-
> The desktop profile is owned exclusively by the Electron app. If the `dsh` command is not on your PATH, use the carrier CLI bundled with the DSH app (the path depends on the install location, for example `<DSH install dir>\resources\runtime\cli\bin\dsh.cmd`).
|
|
89
|
-
|
|
90
|
-
After installing, the plugin hot-reloads, and the models then show up under the **MiniMax Code** group in the model picker.
|
|
91
|
-
|
|
92
|
-
> The credit pill and the plugin page card are browser-side plugins, so **the first install, or an upgrade to 0.3.0 or later, requires one DSH restart** before they appear (the model route itself does not need a restart).
|
|
93
|
-
|
|
94
|
-
### If the install is blocked by `allowBuilds`
|
|
95
|
-
|
|
96
|
-
This repository commits the build output `lib/`, so a normal install does not trigger a build. If you see:
|
|
97
|
-
|
|
98
|
-
```text
|
|
99
|
-
[ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED] Failed to prepare git-hosted package
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
it means you are on **a commit older than `0.2.1`** (those versions did not commit `lib/` yet). Two ways out:
|
|
103
|
-
|
|
104
|
-
- Pin a version with the syntax above: `github:JeffTsuiCUG/dsh-connect-minimaxcode#v0.2.1`
|
|
105
|
-
- Or add the `allowBuilds` entry pnpm suggests to that profile's `pnpm-workspace.yaml` and retry
|
|
106
|
-
|
|
107
|
-
## Usage
|
|
108
|
-
|
|
109
|
-
1. **Confirm the desktop app is signed in**
|
|
110
|
-
Open the MiniMax Code desktop app and check that you are signed in. The plugin reads the local login state that the app writes.
|
|
111
|
-
|
|
112
|
-
2. **Install the plugin** (with the command above) and wait for the hot reload to finish.
|
|
113
|
-
|
|
114
|
-
3. **Pick a model**
|
|
115
|
-
Open the DSH model picker and choose a model under the **MiniMax Code** group. The available models come from the live upstream catalog, for example `M3`, `M3.1-Flash`.
|
|
116
|
-
|
|
117
|
-
4. **Chat as usual**
|
|
118
|
-
Just use it. Tool calls are executed by DSH's local tools; the plugin only provides the model channel.
|
|
119
|
-
|
|
120
|
-
5. **Sign in again when the token expires**
|
|
121
|
-
MiniMax Code has **two login states that are not interchangeable**, and this plugin uses one of each:
|
|
122
|
-
|
|
123
|
-
| Credential | Location | Lifetime | Used for |
|
|
124
|
-
|---|---|---|---|
|
|
125
|
-
| JWT | `~/.minimax/local-runtime.auth.json` | about 15 days | Model gateway (`/mavis/api/v1/llm/v1/messages`) |
|
|
126
|
-
| OAuth (`mmoat_`) | `~/.minimax/auth/prod/<region>/mcode-public/auth.json` | about 1 hour | Account and credit reads (`/matrix/api/v1/commerce/get_membership_info`) |
|
|
127
|
-
|
|
128
|
-
The two credentials are not interchangeable: the JWT gets `401` on every account API call, and the OAuth token does not work against the model gateway. **Model traffic** uses the JWT, **the credit display** uses OAuth.
|
|
129
|
-
|
|
130
|
-
| Symptom | Meaning | What to do |
|
|
131
|
-
|---|---|---|
|
|
132
|
-
| Everything works | Both credentials are valid | — |
|
|
133
|
-
| The model reports an auth error | The JWT has expired | **Sign in again** in MiniMax Code, then restart DSH |
|
|
134
|
-
| The credit pill says "login state expired" | The OAuth token expired | The plugin refreshes it with the app's own refresh token and writes it back; if the refresh fails, open the MiniMax Code desktop app once and let it refresh itself |
|
|
135
|
-
|
|
136
|
-
> An expired OAuth token does **not** affect model traffic: the JWT is still valid and the model works as usual — only the credit pill cannot fetch a balance for a while.
|
|
137
|
-
>
|
|
138
|
-
> The refresh **rotates** the refresh token. The plugin therefore writes the new token pair back to the same file atomically, in the client's **original format**, and leaves `generation` unchanged — the client relies on it to decide "does this credential still belong to the current login", and raising it on its own would make the desktop app think the login came from another machine.
|
|
139
|
-
|
|
140
|
-
### About response latency
|
|
141
|
-
|
|
142
|
-
These MiniMax models **require reasoning before any output and cannot turn it off**. Therefore:
|
|
143
|
-
|
|
144
|
-
- There is a wait before the first character appears (measured: about 3 seconds for M3.1-Flash)
|
|
145
|
-
- The reasoning is streamed back live as `thinking` blocks, so you can see the model "thinking" in the interface
|
|
146
|
-
- This is the model's own behaviour, **not latency caused by the plugin**
|
|
147
|
-
|
|
148
|
-
If you care more about the immediacy of streaming feedback, keep one of DSH's official models or another provider as a fallback.
|
|
149
|
-
|
|
150
|
-
## How it works
|
|
151
|
-
|
|
152
|
-
1. Read `~/.minimax/local-runtime.auth.json` (the plaintext login information the MiniMax Code app writes) and **parse its JWT in memory only**
|
|
153
|
-
2. Call `GET /mavis/api/v1/models` to fetch the live model catalog (including context length, multimodal support, tool calling and other capability declarations)
|
|
154
|
-
3. Register the `minimaxcode` provider, pointing at `POST /mavis/api/v1/llm/v1/messages` (the standard Anthropic Messages protocol)
|
|
155
|
-
4. Read the OAuth token from `~/.minimax/auth/prod/<region>/mcode-public/auth.json` and refresh it when needed
|
|
156
|
-
5. Call `POST /matrix/api/v1/commerce/get_membership_info` for the credit balance, and hand it to the UI pill through the plugin's own status endpoint `/plugins/dsh-connect-minimaxcode/status`
|
|
157
|
-
|
|
158
|
-
### Three compatibility fixes for the gateway
|
|
159
|
-
|
|
160
|
-
These are the differences that had to be handled in practice, and they are the main reason this plugin is not "just a different URL and it works":
|
|
161
|
-
|
|
162
|
-
| # | Symptom | Fix |
|
|
163
|
-
|---|---|---|
|
|
164
|
-
| 1 | The gateway only accepts `Authorization: Bearer`, while pi-ai sends `x-api-key` by default; the latter on its own returns `401 token is required` | Attach an `Authorization` header to the model descriptor (the gateway accepts both headers) |
|
|
165
|
-
| 2 | The Anthropic SDK appends `/v1/messages` to `baseUrl` itself; if `baseUrl` already ends with `/v1`, the request path becomes `/v1/v1/messages` and the gateway returns `503 direct_route_not_configured` | Cut `baseUrl` before the version segment and let the SDK complete it |
|
|
166
|
-
| 3 | Every model requires reasoning, and "disable reasoning" makes pi-ai send `thinking: {type: "disabled"}`, which the gateway rejects with `400 ... requires adaptive thinking` | Mark the "off" level as unsupported so the field is omitted (which the gateway accepts) |
|
|
167
|
-
|
|
168
|
-
The plugin contains **no** hardcoded token, account or API key.
|
|
169
|
-
|
|
170
|
-
## Signing in without the desktop app
|
|
171
|
-
|
|
172
|
-
The plugin can sign in on its own, using the same **OAuth 2.0 device authorization grant** (RFC 8628) the desktop app uses:
|
|
173
|
-
|
|
174
|
-
1. The plugin asks `account.minimax.cn/oauth2/device/code` for a device code and gets an eight-character user code and the page to authorize it.
|
|
175
|
-
2. You open that page, sign in to **your own** MiniMax account, and approve the request.
|
|
176
|
-
3. The plugin polls `oauth2/token` and receives the `mmoat_` token and a refresh token.
|
|
177
|
-
|
|
178
|
-
What this means in practice:
|
|
179
|
-
|
|
180
|
-
- **The plugin never sees a password or a verification code** — the only human step happens in your own browser, on the account site's own page.
|
|
181
|
-
- **PKCE is used**, so a leaked device code cannot be redeemed without the verifier that never leaves the process.
|
|
182
|
-
- **One token is enough**: the `mmoat_` token works against the model gateway (`agent.minimax.cn/mavis/api/v1/llm`) *and* the account endpoints (credits, check-in), so one sign-in covers all of it.
|
|
183
|
-
- **The desktop app's files are left alone.** The plugin keeps its own credential in `~/.dsh/.minimaxcode-auth.json` (mode 0600, atomic write). The app's two files carry `generation` counters that must stay in step, and writing there would look like a sign-in somewhere else and force a new one.
|
|
184
|
-
- **The refresh token rotates**, so the plugin writes the new pair back to its own file and the sign-in keeps working.
|
|
185
|
-
|
|
186
|
-
## The daily check-in
|
|
187
|
-
|
|
188
|
-
The free credits are granted by a **daily check-in**, not by signing in. The desktop app has a "每日签到" card, but it does **not** claim on its own — the card opens and you press the button.
|
|
189
|
-
|
|
190
|
-
The upstream endpoints are:
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
GET /minimax-cloud/api/v1/signin/status?timezone_id=Asia/Shanghai # the seven-day cycle
|
|
194
|
-
POST /minimax-cloud/api/v1/signin/claim?timezone_id=Asia/Shanghai # claim today's grant
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Measured behaviour:
|
|
198
|
-
|
|
199
|
-
- **The time zone must be an IANA name** (`Asia/Shanghai`). An offset, or none at all, is answered with `invalid timezone_id`.
|
|
200
|
-
- **A cycle runs seven days**: 400 / 400 / 400 / 1000 / 400 / 400 / 1000 = **4000 credits**.
|
|
201
|
-
- **Claimed credits are valid for thirty days** (the response's `expire_at_ms` is exactly thirty days later, at midnight in the account's time zone).
|
|
202
|
-
- **The claim is idempotent per day**: claiming again answers `claim_result: 2` (already claimed) and grants nothing.
|
|
203
|
-
|
|
204
|
-
The plugin page shows which day of the cycle it is, what that day is worth, and which days are left, and offers two actions:
|
|
205
|
-
|
|
206
|
-
- **A "Claim" button** — you press it and the plugin claims the day's grant.
|
|
207
|
-
- **A "Claim daily on its own" switch** (off by default) — with it on, the plugin claims once a day, and only when the server itself says the day can be claimed.
|
|
208
|
-
|
|
209
|
-
The automatic claim is deliberately restrained: it only asks when the upstream says the day is claimable, at most once a day, never concurrently, never around any verification, and it shows the reason when something goes wrong rather than retrying quietly. Please check that this is within MiniMax's terms of service yourself.
|
|
210
|
-
|
|
211
|
-
## Credit balance and usage
|
|
212
|
-
|
|
213
|
-
The pill in the composer tool row (beside the model picker) shows the **remaining MiniMax Code credits**; expanding it also shows the granted/purchased split, the consumption between the two reads, and the tokens and equivalent cost accumulated on this machine. The pill appears **only while a `minimaxcode` model is selected**.
|
|
214
|
-
|
|
215
|
-
### Where the balance comes from
|
|
216
|
-
|
|
217
|
-
The MiniMax Code desktop app calls a "membership info" API every day itself, and the response carries the three numbers shown on the app's settings page:
|
|
218
|
-
|
|
219
|
-
```
|
|
220
|
-
POST https://agent.minimax.cn/matrix/api/v1/commerce/get_membership_info
|
|
221
|
-
Authorization: Bearer <the desktop app's mmoat_ OAuth token>
|
|
222
|
-
Content-Type: application/json
|
|
223
|
-
|
|
224
|
-
{}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
```json
|
|
228
|
-
"op_credit_summary": {
|
|
229
|
-
"total_remaining_amount": "11459.454", // total remaining credits
|
|
230
|
-
"purchased_remaining_amount": "0", // of which purchased
|
|
231
|
-
"free_remaining_amount": "11459.454" // of which granted
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
What the plugin reads is `op_credit_summary.total_remaining_amount`. **Mind the field**: `total_remains_credit` and `opcredit_balance` in the same response are legacy balances; on accounts that have already migrated to the credit pool they are always `0`, so reading them shows "0 credits".
|
|
236
|
-
|
|
237
|
-
Incidentally, there are three **different units of "money"** here — do not mix them up:
|
|
238
|
-
|
|
239
|
-
| Value | Meaning | Shown by this plugin |
|
|
240
|
-
|---|---|---|
|
|
241
|
-
| `op_credit_summary.total_remaining_amount` | Code credit balance | ✅ This is the one |
|
|
242
|
-
| `token_plan_credit.remaining_credits` | Purchased credit pool | ❌ |
|
|
243
|
-
| `query_balance.available_amount` | Open Platform wallet (CNY) | ❌ |
|
|
244
|
-
|
|
245
|
-
### Do credits expire? Yes
|
|
246
|
-
|
|
247
|
-
Credits are not a single pool but **lots granted one at a time**, each with its own expiry date. The client grants one lot per day, and each lot expires on **the grant date + 30 calendar days** (it lapses at 00:00 on that day, in the account's time zone), which forms a 30-day ladder staggered by one day:
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
GET https://agent.minimax.cn/minimax-cloud/api/v1/credit/details?page_number=1&page_size=100
|
|
251
|
-
→ details[] { granted_amount, remaining_amount, consumed_amount, granted_at_ms, expire_at_ms, credit_type }
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Measured on this machine, 23 lots: the one granted on 09-20 expires on 10-20, the one granted on 09-21 expires on 10-21, and so on; one granted on 08-25 expires on 2027-08-25 (a one-year lot; `credit_type` 1 = purchased / 2 = check-in). **Always read the expiry date from `expire_at_ms`** instead of hardcoding a number of days — there are at least a 30-day and a 365-day scheme.
|
|
255
|
-
|
|
256
|
-
Based on that, the plugin shows, in the **plugin page** (Settings → Plugins → DSH Connect MiniMax Code) and in the pill in the composer tool row:
|
|
257
|
-
|
|
258
|
-
- The remaining credits **expiring within 7 days**, and the expiry date of the most recent lot
|
|
259
|
-
- The remaining credits **expiring within 30 days**
|
|
260
|
-
- A list of the **upcoming expiring lots** (date + amount)
|
|
261
|
-
- The balance bar and the consumption on this machine (token count, equivalent cost)
|
|
262
|
-
|
|
263
|
-
Only lots that **still have a balance** are counted: a lot that was granted but already spent has nothing left to lose when it expires, and counting it would make every account look "about to expire". Consumption is roughly first-in, first-out, so **the "expiring within 7 days" amount is often 0** (the lots that are about to expire are basically used up already), which is why the interface also shows the **earliest expiry time** — that is the signal that actually means something.
|
|
264
|
-
|
|
265
|
-
> Expiry does not zero out a lot's `remaining_amount` (in practice a negative residue such as -0.31 is left behind); it excludes the expired lots **from the balance** instead, so the total comes from the balance API.
|
|
266
|
-
>
|
|
267
|
-
> The sum of `remaining_amount` in the ledger is digit-for-digit equal to `total_remaining_amount` from the balance API **while nothing is being spent**; the difference seen between two samples is what the account really spent during that window.
|
|
268
|
-
|
|
269
|
-
### How usage is computed
|
|
270
|
-
|
|
271
|
-
Two sets of data each cover one thing:
|
|
272
|
-
|
|
273
|
-
- **Real credit consumption**: the balance is a live value, so the difference between two reads is the credits actually spent in that window (the client spends concurrently as well, so the difference includes spend from the other clients).
|
|
274
|
-
- **Consumption on this machine**: every response carries its own `usage` (`input_tokens` / `output_tokens` / `cache_creation_input_tokens` / `cache_read_input_tokens`), which the plugin accumulates as is. When converting to money, **compute from these four components alone** — never use any `total_token` aggregate, because upstream's two aggregation levels use different bases (they differ by 1.94×, and the difference is exactly the cache-read part). The prices come from the [official MiniMax price list](https://platform.minimax.cn/docs/pricing/overview):
|
|
275
|
-
|
|
276
|
-
| Model | Input | Output | Cache read |
|
|
277
|
-
|---|---|---|---|
|
|
278
|
-
| `MiniMax-M3.1-Flash` | ¥1.00 / 1M tokens | 4.00 | 0.10 |
|
|
279
|
-
| `MiniMax-M3` (≤512k input) | 2.10 | 8.40 | 0.42 |
|
|
280
|
-
| `MiniMax-M3` (>512k input) | 4.20 | 16.80 | 0.84 |
|
|
281
|
-
|
|
282
|
-
The converted amount is an **equivalent cost, not a charge**: MiniMax publishes no exchange rate between credits and money (the plans only publish relative multipliers of 1×/3×/12×), so the plugin does not present it as "how many credits were spent".
|
|
283
|
-
|
|
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
|
-
|
|
286
|
-
### About token refresh
|
|
287
|
-
|
|
288
|
-
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.
|
|
289
|
-
|
|
290
|
-
> The conclusion of this section is the opposite of what 0.2.x reported ("the quota cannot be displayed"). The investigation at the time guessed endpoint paths from English semantics like `credits` / `quota` / `summary`, but the real API uses the `commerce` / `membership` vocabulary of the billing domain, so every guess missed; the path was later pinned down by reading the URL the desktop app really calls every day out of its own runtime logs (`%APPDATA%\MiniMax\logs\api-*.log`).
|
|
291
|
-
|
|
292
|
-
## Known limitations
|
|
293
|
-
|
|
294
|
-
- **Only the China region (`agent.minimax.cn`) is supported**. Whether the international gateway works is **unverified** — please do not infer that other regions are supported from this.
|
|
295
|
-
- **Reasoning must stay enabled**. All MiniMax models declare that they need reasoning and it cannot be turned off. The time to first token is therefore higher than for non-reasoning models.
|
|
296
|
-
- **The model list is a built-in fallback when you are not signed in**. The picker still shows the two built-in entries `M3` and `M3.1-Flash` in that state, but they cannot really be called; once you sign in, the list switches to the live upstream catalog. Judge availability by the result of an actual call.
|
|
297
|
-
- **Credits only cover the granted/purchased MiniMax Code credits**; they do not include the Open Platform wallet balance or the purchased credit pool, and the plugin never converts money into credits.
|
|
298
|
-
- **The balance is an account-level number**, so the difference between two reads includes concurrent spend from the other MiniMax Code clients; the consumption of this machine alone is the token count and the equivalent cost in the pill.
|
|
299
|
-
- **The equivalent cost is an estimate converted at the public price list**, not an actual charge; MiniMax publishes no exchange rate between tokens and credits.
|
|
300
|
-
- **When the OAuth token expires the credit pill is temporarily unavailable**. With the desktop app's sign-in, open MiniMax Code once and let it refresh itself; with the plugin's own sign-in, the plugin renews it with its refresh token (the server rotates the refresh token, and the plugin writes the new pair back to its own file). This does not affect model traffic.
|
|
301
|
-
- **The check-in needs an account that is entitled to it.** The check-in is an upstream entitlement; the plugin only presses the button for you. When the server says "not claimable now", the plugin says so — it does not retry or work around it.
|
|
302
|
-
- **The automatic claim is off by default**, and it only asks when the server explicitly says the day can be claimed, at most once a day. Whether to turn it on, and whether that is within MiniMax's terms of service, is your call.
|
|
303
|
-
- **No settings card is provided**. The plugin does not register a card on the DSH settings page; the interface it adds is a single pill in the composer tool row (**drawn only while this plugin's model is selected**) and a small card on the plugin page.
|
|
304
|
-
- **The upstream APIs are non-public** and may stop working when MiniMax updates its client. If the balance API breaks, the pill shows "credits unavailable", which does not affect model traffic.
|
|
305
|
-
- **Image input depends on the host's attachment service.** The catalog declares the `image` modality (`M3`, `M3.1-Flash`), but turning a durable attachment into request bytes is the host's job: without that service the plugin still loads and serves text, and only image requests are refused.
|
|
306
|
-
- **The context window is declared as the gateway's real ceiling (1,000,000), not the 512,000 in the catalog.** Upstream's `limit.context` is the *default* window and the billing tier boundary (input above 512k costs more); the largest entry of `context_window_options` is the ceiling the gateway enforces. Measured on 2026-10-25: 946,206 tokens were served normally, 1,050,000 were refused (`invalid params, context window exceeds limit (2013)`). Declaring 512,000 meant every turn of a long conversation was reported as `pi-ai detected context overflow` — while the model had answered perfectly well and the reply was thrown away.
|
|
307
|
-
- **The device code flow needs `account.minimax.cn` to be reachable**, and the browser that authorizes it has to be able to sign in to a MiniMax account. On some restricted networks the authorization page cannot be opened.
|
|
308
|
-
|
|
309
|
-
## Changelog
|
|
310
|
-
|
|
311
|
-
### 0.5.
|
|
312
|
-
|
|
313
|
-
- **
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
- **
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
|
|
335
|
-
### 0.5.
|
|
336
|
-
|
|
337
|
-
- **
|
|
338
|
-
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
- **
|
|
345
|
-
-
|
|
346
|
-
|
|
347
|
-
### 0.
|
|
348
|
-
|
|
349
|
-
- **
|
|
350
|
-
- Added
|
|
351
|
-
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
- **
|
|
357
|
-
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
- **
|
|
363
|
-
- **
|
|
364
|
-
- **
|
|
365
|
-
|
|
366
|
-
### 0.
|
|
367
|
-
|
|
368
|
-
- **Added
|
|
369
|
-
- **
|
|
370
|
-
- **
|
|
371
|
-
- **
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
- **
|
|
377
|
-
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
|
395
|
-
|
|
396
|
-
| [dsh-plugin
|
|
397
|
-
| [
|
|
398
|
-
| [
|
|
399
|
-
| [
|
|
400
|
-
| [awesome-
|
|
401
|
-
| [awesome-
|
|
402
|
-
| [awesome-deepseek-harness
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
##
|
|
481
|
-
|
|
482
|
-
- **[
|
|
483
|
-
- **[
|
|
484
|
-
- [
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
[
|
|
1
|
+
# DSH Connect MiniMax Code (`dsh-connect-minimaxcode`)
|
|
2
|
+
|
|
3
|
+
Bring the models of the **MiniMax Code desktop app (China region)**, already signed in on this machine, into **DeepSeek Harness (DSH)**. Install it and it works — there is no API key to configure.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
[](https://www.npmjs.com/package/dsh-connect-minimaxcode)
|
|
7
|
+
[](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
[简体中文](README.md) | English
|
|
10
|
+
|
|
11
|
+
## Inspiration
|
|
12
|
+
|
|
13
|
+
The idea for this plugin came from **[corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)** (by Corrine Hu).
|
|
14
|
+
|
|
15
|
+
That project was the first to show that **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:
|
|
16
|
+
|
|
17
|
+
| | dsh-workbuddy-connect | This plugin |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Data source | Local login state of the WorkBuddy desktop app | Local login state of the MiniMax Code desktop app |
|
|
20
|
+
| Integration | Registers a DSH provider and exposes the already-signed-in models | Same |
|
|
21
|
+
| Catalog source | Live model list from upstream | Same |
|
|
22
|
+
|
|
23
|
+
Another implementation in the same direction is **[dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)**; for **how the plugin is structured and how the provider is registered**, this plugin also follows the same author's [dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae) (whose connection core itself references corrinehu's implementation — see the acknowledgements in its README).
|
|
24
|
+
|
|
25
|
+
> The two differ in this way: **corrinehu was the original pioneer** — the "reuse the local login state" route was established by that project; **dingminhua's implementation follows the existing conventions of the DSH plugin structure more closely**, and this plugin's directory layout and `cordis.patch.yml` follow the latter. Both are credited at the end of this document.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## ⚠️ Important notice (read this first)
|
|
30
|
+
|
|
31
|
+
- **This project is for learning and research only.** It is not aimed at production use and comes with no commercial support.
|
|
32
|
+
- **This is an unofficial plugin.** It is not affiliated with MiniMax or DeepSeek, and is not endorsed or authorized by either of them.
|
|
33
|
+
- The plugin only **reads the credentials of the MiniMax Code account already signed in on your own machine**, and only drives **your own account on your own computer**.
|
|
34
|
+
- **Do not** use this plugin for commercial purposes, beyond reasonable personal use, or for any bulk / multi-account / limit-evasion purpose.
|
|
35
|
+
- The plugin depends on **undocumented (non-public) MiniMax client APIs**. When MiniMax updates its client, those APIs may change and the plugin may stop working.
|
|
36
|
+
- You are responsible for complying with the [MiniMax Terms of Service](https://agent.minimax.cn/doc/zh/terms-of-service.html). You bear all consequences of using this plugin (account restrictions, quota resets, service interruption, and so on).
|
|
37
|
+
- The authors of this project are not liable for any direct or indirect loss caused by the use or misuse of this plugin.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Highlights
|
|
42
|
+
|
|
43
|
+
- **Zero configuration**: the login state of the MiniMax Code desktop app is read automatically, and no plaintext token is ever stored in DSH
|
|
44
|
+
- **Dynamic model catalog**: pulled live from the upstream API, so upstream adding or removing models needs no plugin update
|
|
45
|
+
- **Credit balance at a glance**: the pill in the composer tool row (beside the model picker) shows the remaining MiniMax Code credits and the consumption accumulated on this machine
|
|
46
|
+
- **Live usage accounting**: the token usage of every request is taken from the upstream response itself, and can be converted to an equivalent cost using the official price list
|
|
47
|
+
- **Reuses the official adapter**: upstream speaks the standard Anthropic Messages protocol, so DSH's built-in provider is reused as is; tool calls, reasoning blocks and context compaction are all handled by DSH
|
|
48
|
+
- **Works without the desktop app**: the plugin can sign in on its own with the OAuth device code flow; you confirm in your own browser and the plugin never sees a password
|
|
49
|
+
- **Daily check-in**: MiniMax Code grants credits every day and the desktop app does **not** claim them for you; the plugin shows the seven-day cycle and can claim the day's grant
|
|
50
|
+
- **Read-mostly**: apart from "refreshing an expired desktop app login state" and the sign-in you start yourself, the plugin does not write to or modify any MiniMax file
|
|
51
|
+
- **Follows the selected model**: the balance sits in the **composer tool row beside the model picker**, and is drawn **only while a MiniMax model is selected** — picking another connector's model hides it entirely instead of misleading you with MiniMax's balance
|
|
52
|
+
- **An icon and two languages of its own**: a 48×48 SVG ships with the package, so the plugin page no longer shows the default placeholder; the title and the introduction come in Chinese and English, and the interface follows the host's language
|
|
53
|
+
- **Image input**: models whose catalog entry declares the `image` input modality (`M3`, `M3.1-Flash`) take pictures directly; the host's attachment service reads the image into request bytes, and the plugin only has to connect the durable reference to it
|
|
54
|
+
|
|
55
|
+
## Prerequisites
|
|
56
|
+
|
|
57
|
+
Either:
|
|
58
|
+
|
|
59
|
+
1. **The MiniMax Code desktop app (China region) installed and signed in** — the plugin reuses its sign-in (the easy way)
|
|
60
|
+
2. **Nothing installed** — press "Sign in to MiniMax Code" on the plugin page; the plugin starts an OAuth device code flow and you confirm with your own MiniMax account in a browser (this needs `account.minimax.cn` to be reachable)
|
|
61
|
+
|
|
62
|
+
Plus DSH desktop (built-in core `>=0.2.1-rc.2 <0.3.0`, see `engines.dsh` in `package.json`; tested on `0.2.1-rc.2`) or the Web profile.
|
|
63
|
+
|
|
64
|
+
> **Sending a picture needs the host's attachment service**: the plugin does not read images itself — the host's durable attachment service turns a stored reference into request bytes, and the plugin only connects the two. On a host without that service the plugin still loads and text still works; only image requests are refused (`pi-ai image input requires the durable attachment service`).
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
**Option 1: install from the Git repository** (recommended, gets the latest code)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
dsh plugin --profile desktop add github:JeffTsuiCUG/dsh-connect-minimaxcode
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Option 2: install from npm** (available once published)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
dsh plugin --profile desktop add dsh-connect-minimaxcode
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For the Web profile, replace `--profile desktop` with `--profile web`.
|
|
81
|
+
|
|
82
|
+
> **Installing a locally modified clone**: do not pass a directory path to `dsh plugin add` — pnpm installs it as a link and nests dependency symlinks inside the plugin's own `node_modules`; the host's plugin sandbox gets `EPERM` stat-ing those links and the plugin fails to activate (`failed to import`). Pack a tarball first, then install the tarball so dependencies resolve in the profile root `node_modules` like a registry install:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pnpm pack
|
|
86
|
+
dsh plugin --profile desktop add ./dsh-connect-minimaxcode-0.5.6.tgz
|
|
87
|
+
```
|
|
88
|
+
> The desktop profile is owned exclusively by the Electron app. If the `dsh` command is not on your PATH, use the carrier CLI bundled with the DSH app (the path depends on the install location, for example `<DSH install dir>\resources\runtime\cli\bin\dsh.cmd`).
|
|
89
|
+
|
|
90
|
+
After installing, the plugin hot-reloads, and the models then show up under the **MiniMax Code** group in the model picker.
|
|
91
|
+
|
|
92
|
+
> The credit pill and the plugin page card are browser-side plugins, so **the first install, or an upgrade to 0.3.0 or later, requires one DSH restart** before they appear (the model route itself does not need a restart).
|
|
93
|
+
|
|
94
|
+
### If the install is blocked by `allowBuilds`
|
|
95
|
+
|
|
96
|
+
This repository commits the build output `lib/`, so a normal install does not trigger a build. If you see:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
[ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED] Failed to prepare git-hosted package
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
it means you are on **a commit older than `0.2.1`** (those versions did not commit `lib/` yet). Two ways out:
|
|
103
|
+
|
|
104
|
+
- Pin a version with the syntax above: `github:JeffTsuiCUG/dsh-connect-minimaxcode#v0.2.1`
|
|
105
|
+
- Or add the `allowBuilds` entry pnpm suggests to that profile's `pnpm-workspace.yaml` and retry
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
1. **Confirm the desktop app is signed in**
|
|
110
|
+
Open the MiniMax Code desktop app and check that you are signed in. The plugin reads the local login state that the app writes.
|
|
111
|
+
|
|
112
|
+
2. **Install the plugin** (with the command above) and wait for the hot reload to finish.
|
|
113
|
+
|
|
114
|
+
3. **Pick a model**
|
|
115
|
+
Open the DSH model picker and choose a model under the **MiniMax Code** group. The available models come from the live upstream catalog, for example `M3`, `M3.1-Flash`.
|
|
116
|
+
|
|
117
|
+
4. **Chat as usual**
|
|
118
|
+
Just use it. Tool calls are executed by DSH's local tools; the plugin only provides the model channel.
|
|
119
|
+
|
|
120
|
+
5. **Sign in again when the token expires**
|
|
121
|
+
MiniMax Code has **two login states that are not interchangeable**, and this plugin uses one of each:
|
|
122
|
+
|
|
123
|
+
| Credential | Location | Lifetime | Used for |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| JWT | `~/.minimax/local-runtime.auth.json` | about 15 days | Model gateway (`/mavis/api/v1/llm/v1/messages`) |
|
|
126
|
+
| OAuth (`mmoat_`) | `~/.minimax/auth/prod/<region>/mcode-public/auth.json` | about 1 hour | Account and credit reads (`/matrix/api/v1/commerce/get_membership_info`) |
|
|
127
|
+
|
|
128
|
+
The two credentials are not interchangeable: the JWT gets `401` on every account API call, and the OAuth token does not work against the model gateway. **Model traffic** uses the JWT, **the credit display** uses OAuth.
|
|
129
|
+
|
|
130
|
+
| Symptom | Meaning | What to do |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| Everything works | Both credentials are valid | — |
|
|
133
|
+
| The model reports an auth error | The JWT has expired | **Sign in again** in MiniMax Code, then restart DSH |
|
|
134
|
+
| The credit pill says "login state expired" | The OAuth token expired | The plugin refreshes it with the app's own refresh token and writes it back; if the refresh fails, open the MiniMax Code desktop app once and let it refresh itself |
|
|
135
|
+
|
|
136
|
+
> An expired OAuth token does **not** affect model traffic: the JWT is still valid and the model works as usual — only the credit pill cannot fetch a balance for a while.
|
|
137
|
+
>
|
|
138
|
+
> The refresh **rotates** the refresh token. The plugin therefore writes the new token pair back to the same file atomically, in the client's **original format**, and leaves `generation` unchanged — the client relies on it to decide "does this credential still belong to the current login", and raising it on its own would make the desktop app think the login came from another machine.
|
|
139
|
+
|
|
140
|
+
### About response latency
|
|
141
|
+
|
|
142
|
+
These MiniMax models **require reasoning before any output and cannot turn it off**. Therefore:
|
|
143
|
+
|
|
144
|
+
- There is a wait before the first character appears (measured: about 3 seconds for M3.1-Flash)
|
|
145
|
+
- The reasoning is streamed back live as `thinking` blocks, so you can see the model "thinking" in the interface
|
|
146
|
+
- This is the model's own behaviour, **not latency caused by the plugin**
|
|
147
|
+
|
|
148
|
+
If you care more about the immediacy of streaming feedback, keep one of DSH's official models or another provider as a fallback.
|
|
149
|
+
|
|
150
|
+
## How it works
|
|
151
|
+
|
|
152
|
+
1. Read `~/.minimax/local-runtime.auth.json` (the plaintext login information the MiniMax Code app writes) and **parse its JWT in memory only**
|
|
153
|
+
2. Call `GET /mavis/api/v1/models` to fetch the live model catalog (including context length, multimodal support, tool calling and other capability declarations)
|
|
154
|
+
3. Register the `minimaxcode` provider, pointing at `POST /mavis/api/v1/llm/v1/messages` (the standard Anthropic Messages protocol)
|
|
155
|
+
4. Read the OAuth token from `~/.minimax/auth/prod/<region>/mcode-public/auth.json` and refresh it when needed
|
|
156
|
+
5. Call `POST /matrix/api/v1/commerce/get_membership_info` for the credit balance, and hand it to the UI pill through the plugin's own status endpoint `/plugins/dsh-connect-minimaxcode/status`
|
|
157
|
+
|
|
158
|
+
### Three compatibility fixes for the gateway
|
|
159
|
+
|
|
160
|
+
These are the differences that had to be handled in practice, and they are the main reason this plugin is not "just a different URL and it works":
|
|
161
|
+
|
|
162
|
+
| # | Symptom | Fix |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| 1 | The gateway only accepts `Authorization: Bearer`, while pi-ai sends `x-api-key` by default; the latter on its own returns `401 token is required` | Attach an `Authorization` header to the model descriptor (the gateway accepts both headers) |
|
|
165
|
+
| 2 | The Anthropic SDK appends `/v1/messages` to `baseUrl` itself; if `baseUrl` already ends with `/v1`, the request path becomes `/v1/v1/messages` and the gateway returns `503 direct_route_not_configured` | Cut `baseUrl` before the version segment and let the SDK complete it |
|
|
166
|
+
| 3 | Every model requires reasoning, and "disable reasoning" makes pi-ai send `thinking: {type: "disabled"}`, which the gateway rejects with `400 ... requires adaptive thinking` | Mark the "off" level as unsupported so the field is omitted (which the gateway accepts) |
|
|
167
|
+
|
|
168
|
+
The plugin contains **no** hardcoded token, account or API key.
|
|
169
|
+
|
|
170
|
+
## Signing in without the desktop app
|
|
171
|
+
|
|
172
|
+
The plugin can sign in on its own, using the same **OAuth 2.0 device authorization grant** (RFC 8628) the desktop app uses:
|
|
173
|
+
|
|
174
|
+
1. The plugin asks `account.minimax.cn/oauth2/device/code` for a device code and gets an eight-character user code and the page to authorize it.
|
|
175
|
+
2. You open that page, sign in to **your own** MiniMax account, and approve the request.
|
|
176
|
+
3. The plugin polls `oauth2/token` and receives the `mmoat_` token and a refresh token.
|
|
177
|
+
|
|
178
|
+
What this means in practice:
|
|
179
|
+
|
|
180
|
+
- **The plugin never sees a password or a verification code** — the only human step happens in your own browser, on the account site's own page.
|
|
181
|
+
- **PKCE is used**, so a leaked device code cannot be redeemed without the verifier that never leaves the process.
|
|
182
|
+
- **One token is enough**: the `mmoat_` token works against the model gateway (`agent.minimax.cn/mavis/api/v1/llm`) *and* the account endpoints (credits, check-in), so one sign-in covers all of it.
|
|
183
|
+
- **The desktop app's files are left alone.** The plugin keeps its own credential in `~/.dsh/.minimaxcode-auth.json` (mode 0600, atomic write). The app's two files carry `generation` counters that must stay in step, and writing there would look like a sign-in somewhere else and force a new one.
|
|
184
|
+
- **The refresh token rotates**, so the plugin writes the new pair back to its own file and the sign-in keeps working.
|
|
185
|
+
|
|
186
|
+
## The daily check-in
|
|
187
|
+
|
|
188
|
+
The free credits are granted by a **daily check-in**, not by signing in. The desktop app has a "每日签到" card, but it does **not** claim on its own — the card opens and you press the button.
|
|
189
|
+
|
|
190
|
+
The upstream endpoints are:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
GET /minimax-cloud/api/v1/signin/status?timezone_id=Asia/Shanghai # the seven-day cycle
|
|
194
|
+
POST /minimax-cloud/api/v1/signin/claim?timezone_id=Asia/Shanghai # claim today's grant
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Measured behaviour:
|
|
198
|
+
|
|
199
|
+
- **The time zone must be an IANA name** (`Asia/Shanghai`). An offset, or none at all, is answered with `invalid timezone_id`.
|
|
200
|
+
- **A cycle runs seven days**: 400 / 400 / 400 / 1000 / 400 / 400 / 1000 = **4000 credits**.
|
|
201
|
+
- **Claimed credits are valid for thirty days** (the response's `expire_at_ms` is exactly thirty days later, at midnight in the account's time zone).
|
|
202
|
+
- **The claim is idempotent per day**: claiming again answers `claim_result: 2` (already claimed) and grants nothing.
|
|
203
|
+
|
|
204
|
+
The plugin page shows which day of the cycle it is, what that day is worth, and which days are left, and offers two actions:
|
|
205
|
+
|
|
206
|
+
- **A "Claim" button** — you press it and the plugin claims the day's grant.
|
|
207
|
+
- **A "Claim daily on its own" switch** (off by default) — with it on, the plugin claims once a day, and only when the server itself says the day can be claimed.
|
|
208
|
+
|
|
209
|
+
The automatic claim is deliberately restrained: it only asks when the upstream says the day is claimable, at most once a day, never concurrently, never around any verification, and it shows the reason when something goes wrong rather than retrying quietly. Please check that this is within MiniMax's terms of service yourself.
|
|
210
|
+
|
|
211
|
+
## Credit balance and usage
|
|
212
|
+
|
|
213
|
+
The pill in the composer tool row (beside the model picker) shows the **remaining MiniMax Code credits**; expanding it also shows the granted/purchased split, the consumption between the two reads, and the tokens and equivalent cost accumulated on this machine. The pill appears **only while a `minimaxcode` model is selected**.
|
|
214
|
+
|
|
215
|
+
### Where the balance comes from
|
|
216
|
+
|
|
217
|
+
The MiniMax Code desktop app calls a "membership info" API every day itself, and the response carries the three numbers shown on the app's settings page:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
POST https://agent.minimax.cn/matrix/api/v1/commerce/get_membership_info
|
|
221
|
+
Authorization: Bearer <the desktop app's mmoat_ OAuth token>
|
|
222
|
+
Content-Type: application/json
|
|
223
|
+
|
|
224
|
+
{}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
"op_credit_summary": {
|
|
229
|
+
"total_remaining_amount": "11459.454", // total remaining credits
|
|
230
|
+
"purchased_remaining_amount": "0", // of which purchased
|
|
231
|
+
"free_remaining_amount": "11459.454" // of which granted
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
What the plugin reads is `op_credit_summary.total_remaining_amount`. **Mind the field**: `total_remains_credit` and `opcredit_balance` in the same response are legacy balances; on accounts that have already migrated to the credit pool they are always `0`, so reading them shows "0 credits".
|
|
236
|
+
|
|
237
|
+
Incidentally, there are three **different units of "money"** here — do not mix them up:
|
|
238
|
+
|
|
239
|
+
| Value | Meaning | Shown by this plugin |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `op_credit_summary.total_remaining_amount` | Code credit balance | ✅ This is the one |
|
|
242
|
+
| `token_plan_credit.remaining_credits` | Purchased credit pool | ❌ |
|
|
243
|
+
| `query_balance.available_amount` | Open Platform wallet (CNY) | ❌ |
|
|
244
|
+
|
|
245
|
+
### Do credits expire? Yes
|
|
246
|
+
|
|
247
|
+
Credits are not a single pool but **lots granted one at a time**, each with its own expiry date. The client grants one lot per day, and each lot expires on **the grant date + 30 calendar days** (it lapses at 00:00 on that day, in the account's time zone), which forms a 30-day ladder staggered by one day:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
GET https://agent.minimax.cn/minimax-cloud/api/v1/credit/details?page_number=1&page_size=100
|
|
251
|
+
→ details[] { granted_amount, remaining_amount, consumed_amount, granted_at_ms, expire_at_ms, credit_type }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Measured on this machine, 23 lots: the one granted on 09-20 expires on 10-20, the one granted on 09-21 expires on 10-21, and so on; one granted on 08-25 expires on 2027-08-25 (a one-year lot; `credit_type` 1 = purchased / 2 = check-in). **Always read the expiry date from `expire_at_ms`** instead of hardcoding a number of days — there are at least a 30-day and a 365-day scheme.
|
|
255
|
+
|
|
256
|
+
Based on that, the plugin shows, in the **plugin page** (Settings → Plugins → DSH Connect MiniMax Code) and in the pill in the composer tool row:
|
|
257
|
+
|
|
258
|
+
- The remaining credits **expiring within 7 days**, and the expiry date of the most recent lot
|
|
259
|
+
- The remaining credits **expiring within 30 days**
|
|
260
|
+
- A list of the **upcoming expiring lots** (date + amount)
|
|
261
|
+
- The balance bar and the consumption on this machine (token count, equivalent cost)
|
|
262
|
+
|
|
263
|
+
Only lots that **still have a balance** are counted: a lot that was granted but already spent has nothing left to lose when it expires, and counting it would make every account look "about to expire". Consumption is roughly first-in, first-out, so **the "expiring within 7 days" amount is often 0** (the lots that are about to expire are basically used up already), which is why the interface also shows the **earliest expiry time** — that is the signal that actually means something.
|
|
264
|
+
|
|
265
|
+
> Expiry does not zero out a lot's `remaining_amount` (in practice a negative residue such as -0.31 is left behind); it excludes the expired lots **from the balance** instead, so the total comes from the balance API.
|
|
266
|
+
>
|
|
267
|
+
> The sum of `remaining_amount` in the ledger is digit-for-digit equal to `total_remaining_amount` from the balance API **while nothing is being spent**; the difference seen between two samples is what the account really spent during that window.
|
|
268
|
+
|
|
269
|
+
### How usage is computed
|
|
270
|
+
|
|
271
|
+
Two sets of data each cover one thing:
|
|
272
|
+
|
|
273
|
+
- **Real credit consumption**: the balance is a live value, so the difference between two reads is the credits actually spent in that window (the client spends concurrently as well, so the difference includes spend from the other clients).
|
|
274
|
+
- **Consumption on this machine**: every response carries its own `usage` (`input_tokens` / `output_tokens` / `cache_creation_input_tokens` / `cache_read_input_tokens`), which the plugin accumulates as is. When converting to money, **compute from these four components alone** — never use any `total_token` aggregate, because upstream's two aggregation levels use different bases (they differ by 1.94×, and the difference is exactly the cache-read part). The prices come from the [official MiniMax price list](https://platform.minimax.cn/docs/pricing/overview):
|
|
275
|
+
|
|
276
|
+
| Model | Input | Output | Cache read |
|
|
277
|
+
|---|---|---|---|
|
|
278
|
+
| `MiniMax-M3.1-Flash` | ¥1.00 / 1M tokens | 4.00 | 0.10 |
|
|
279
|
+
| `MiniMax-M3` (≤512k input) | 2.10 | 8.40 | 0.42 |
|
|
280
|
+
| `MiniMax-M3` (>512k input) | 4.20 | 16.80 | 0.84 |
|
|
281
|
+
|
|
282
|
+
The converted amount is an **equivalent cost, not a charge**: MiniMax publishes no exchange rate between credits and money (the plans only publish relative multipliers of 1×/3×/12×), so the plugin does not present it as "how many credits were spent".
|
|
283
|
+
|
|
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
|
+
|
|
286
|
+
### About token refresh
|
|
287
|
+
|
|
288
|
+
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.
|
|
289
|
+
|
|
290
|
+
> The conclusion of this section is the opposite of what 0.2.x reported ("the quota cannot be displayed"). The investigation at the time guessed endpoint paths from English semantics like `credits` / `quota` / `summary`, but the real API uses the `commerce` / `membership` vocabulary of the billing domain, so every guess missed; the path was later pinned down by reading the URL the desktop app really calls every day out of its own runtime logs (`%APPDATA%\MiniMax\logs\api-*.log`).
|
|
291
|
+
|
|
292
|
+
## Known limitations
|
|
293
|
+
|
|
294
|
+
- **Only the China region (`agent.minimax.cn`) is supported**. Whether the international gateway works is **unverified** — please do not infer that other regions are supported from this.
|
|
295
|
+
- **Reasoning must stay enabled**. All MiniMax models declare that they need reasoning and it cannot be turned off. The time to first token is therefore higher than for non-reasoning models.
|
|
296
|
+
- **The model list is a built-in fallback when you are not signed in**. The picker still shows the two built-in entries `M3` and `M3.1-Flash` in that state, but they cannot really be called; once you sign in, the list switches to the live upstream catalog. Judge availability by the result of an actual call.
|
|
297
|
+
- **Credits only cover the granted/purchased MiniMax Code credits**; they do not include the Open Platform wallet balance or the purchased credit pool, and the plugin never converts money into credits.
|
|
298
|
+
- **The balance is an account-level number**, so the difference between two reads includes concurrent spend from the other MiniMax Code clients; the consumption of this machine alone is the token count and the equivalent cost in the pill.
|
|
299
|
+
- **The equivalent cost is an estimate converted at the public price list**, not an actual charge; MiniMax publishes no exchange rate between tokens and credits.
|
|
300
|
+
- **When the OAuth token expires the credit pill is temporarily unavailable**. With the desktop app's sign-in, open MiniMax Code once and let it refresh itself; with the plugin's own sign-in, the plugin renews it with its refresh token (the server rotates the refresh token, and the plugin writes the new pair back to its own file). This does not affect model traffic.
|
|
301
|
+
- **The check-in needs an account that is entitled to it.** The check-in is an upstream entitlement; the plugin only presses the button for you. When the server says "not claimable now", the plugin says so — it does not retry or work around it.
|
|
302
|
+
- **The automatic claim is off by default**, and it only asks when the server explicitly says the day can be claimed, at most once a day. Whether to turn it on, and whether that is within MiniMax's terms of service, is your call.
|
|
303
|
+
- **No settings card is provided**. The plugin does not register a card on the DSH settings page; the interface it adds is a single pill in the composer tool row (**drawn only while this plugin's model is selected**) and a small card on the plugin page.
|
|
304
|
+
- **The upstream APIs are non-public** and may stop working when MiniMax updates its client. If the balance API breaks, the pill shows "credits unavailable", which does not affect model traffic.
|
|
305
|
+
- **Image input depends on the host's attachment service.** The catalog declares the `image` modality (`M3`, `M3.1-Flash`), but turning a durable attachment into request bytes is the host's job: without that service the plugin still loads and serves text, and only image requests are refused.
|
|
306
|
+
- **The context window is declared as the gateway's real ceiling (1,000,000), not the 512,000 in the catalog.** Upstream's `limit.context` is the *default* window and the billing tier boundary (input above 512k costs more); the largest entry of `context_window_options` is the ceiling the gateway enforces. Measured on 2026-10-25: 946,206 tokens were served normally, 1,050,000 were refused (`invalid params, context window exceeds limit (2013)`). Declaring 512,000 meant every turn of a long conversation was reported as `pi-ai detected context overflow` — while the model had answered perfectly well and the reply was thrown away.
|
|
307
|
+
- **The device code flow needs `account.minimax.cn` to be reachable**, and the browser that authorizes it has to be able to sign in to a MiniMax account. On some restricted networks the authorization page cannot be opened.
|
|
308
|
+
|
|
309
|
+
## Changelog
|
|
310
|
+
|
|
311
|
+
### 0.5.6 (2026-11-09)
|
|
312
|
+
|
|
313
|
+
- **Fixed "every model request fails while credits and check-in still work"**: the two features read two different credential files. The model gateway only knew the plugin's own sign-in and the **legacy JWT** an older desktop app left behind (`local-runtime.auth.json`), while current installs store an OAuth grant instead (`auth/prod/<region>/mcode-public/auth.json`, an `mmoat_` token). Once the legacy JWT expired, the gateway answered every request with `401 auth failed` — and because credits read the OAuth file, the card showed a healthy balance beside a dead model. The gateway now tries, in order: the plugin's own sign-in → **the desktop app's OAuth grant** → the legacy JWT, taking the first that works.
|
|
314
|
+
- **Fixed a revoked token never prompting a new sign-in**: revocation does not change the locally recorded expiry, and the refresh check **looked only at that expiry**, so a dead token was used forever, every request answered `401`, and the card kept claiming a sign-in with no way back. An account endpoint answering `401` now forces one refresh; when the refresh itself is refused (`400`/`401`/`invalid_grant`, or a credential that carries no refresh token) the credential is deleted, the cached views of the account are dropped, and the card asks for a new sign-in. **A network error or a `5xx` is not a revocation** — a bad minute upstream must not sign anyone out.
|
|
315
|
+
- **Fixed the status card reporting a working machine as expired**: the card's sign-in state read only the legacy JWT, so it said `expired` even when the gateway worked. It now describes **the credential the gateway will actually use**, and adds `credential-revoked` / `credential-expired` hints that point at the card's own sign-in button instead of "sign in to the desktop app and restart DSH".
|
|
316
|
+
- Added `tests/revoked-credential.spec.ts` (6 cases) pinning: the credential is dropped and the card goes signed-out after a `401`, a refusal yields a "sign in again" remedy, a `503` leaves the credential alone, a plugin-only sign-in reports as signed-in, the OAuth grant wins over the legacy JWT, and the status document never leaks a token. All 193 cases pass.
|
|
317
|
+
|
|
318
|
+
### 0.5.5 (2026-11-06)
|
|
319
|
+
|
|
320
|
+
- **New "rate-limit windows" readout**: reads the open platform's `GET /backend/account/token_plan/remains_percent` (found in the desktop app's own traffic, likewise undocumented) and shows the plan's **5-hour window** and **weekly window** with used percentages and a reset countdown. Window lengths are derived from the response's own edges, never hard-coded.
|
|
321
|
+
- The endpoint belongs to the account domain and **accepts only the OAuth token** (desktop app or plugin sign-in); a JWT answers `1016 invalid api key`. The read rides the credits poller with independent failure handling.
|
|
322
|
+
- Shown in the pill detail and the plugin page card; `tests/rate-limit.spec.ts` pins parsing, URL, failure handling and the polling wiring.
|
|
323
|
+
### 0.5.4 (2026-10-29)
|
|
324
|
+
|
|
325
|
+
- **Fixed long conversations being reported as context overflow**: upstream's `limit.context` (512,000) is the *default* window and the billing tier boundary, while the ceiling the gateway enforces is the 1,000,000 in `context_window_options`. Declaring 512,000 meant that once a conversation crossed that line, *every* turn was reported as `pi-ai detected context overflow` — even though the model answered normally and the reply was discarded. The window is now declared as the ceiling the gateway actually enforces.
|
|
326
|
+
- The built-in fallback roster (used when the catalog cannot be fetched) now declares 1,000,000 as well.
|
|
327
|
+
- Four new cases pin the window parsing: the largest of `context_window_options` wins, upstream's `limit.context` is the fallback, a malformed option list is ignored, and the fallback roster does not claim 512k.
|
|
328
|
+
|
|
329
|
+
### 0.5.3 (2026-10-25)
|
|
330
|
+
|
|
331
|
+
- **Fixed image input**: an upload used to fail every time — `pi-ai image input requires the durable attachment service` (`UNSUPPORTED_CONTENT`). The catalog always declared `image` among a model's input modalities, but the plugin built its `PiAiAdapter` without `resolveAttachments` or `resolveImageAccess`, so pi-ai had no way to turn a durable attachment reference into request bytes and refused the call before it reached the gateway. The plugin now reads the host's `attachments` service on demand and maps an attachment's host path into the current execution world with `fs.processPathFromHostPath`; a host that offers no attachment service gets `undefined` for both hooks, and text-only calls are unchanged.
|
|
332
|
+
- New `tests/image-input.spec.ts` pins how both hooks resolve, the path mapping, and the "no attachment service still loads" case; `peerDependencies` gains `@deepseek-ai/dsh-attachment`.
|
|
333
|
+
- Measured against the live gateway: a 2×2 PNG sent to `M3.1-Flash` came back answered correctly (`inputTokens: 316`).
|
|
334
|
+
|
|
335
|
+
### 0.5.2 (2026-10-21)
|
|
336
|
+
|
|
337
|
+
- **An icon for the plugin page**: a new `icon.svg` (48×48, under 800 bytes) ships with the package, declared as `"icon": "./icon.svg"` in `package.json`. The host reads it as a data URL and draws it on the plugin page instead of the default placeholder.
|
|
338
|
+
- **Chinese and English title and introduction**: new `locale/en.json` and `locale/zh.json` (the host reads the language files in the package root's `locale/` directory, with the English file as the anchor). The plugin page used to show the package name `dsh-connect-minimaxcode` and the mixed-language description from package.json; the title is now `DSH Connect MiniMax Code` and the introduction comes in both languages.
|
|
339
|
+
- **The interface follows the host's language**: neither the composer nor the plugin-page slot carries a translator in its standard props, so the copy always came from the built-in Chinese fallback — Chinese labels even in an English host. The bound locale is now injected into both slots, so an English host shows English.
|
|
340
|
+
- `exports` gains `./locale/*` (the host resolves the language files through it) and `files` gains `locale` and `icon.svg`.
|
|
341
|
+
|
|
342
|
+
### 0.5.1 (2026-10-20)
|
|
343
|
+
|
|
344
|
+
- **The balance follows the selected model**: moved from the dock below the input box to the **composer tool row beside the model picker**, gated by the host's `modelSelection` projection — the readout is drawn **only while a `minimaxcode` model is selected**, and selecting another connector's model (workbuddy, for instance) hides it entirely, polling included (following `dsh-connect-trae`).
|
|
345
|
+
- **`engines.dsh` narrowed to `>=0.2.1-rc.2 <0.3.0-0`**: only the tier actually tested is claimed; the previous `>=0.1.7-rc.1` reached too far.
|
|
346
|
+
|
|
347
|
+
### 0.5.0 (2026-10-20)
|
|
348
|
+
|
|
349
|
+
- **Added "sign in on your own"**: the plugin can sign in to MiniMax Code with the OAuth device code flow, so it no longer depends on the desktop app being installed and signed in. You confirm in your own browser, the plugin never sees a password, and the credential lives in the plugin's own file rather than the client's directory.
|
|
350
|
+
- **Added "daily check-in"**: reads the upstream seven-day cycle (400/400/400/1000/400/400/1000 = 4000 credits per round), shows the day's state and the expiring grants, and offers a manual "Claim" and a "Claim daily on its own" switch that is **off by default**.
|
|
351
|
+
- **Corrected by measurement**: the `mmoat_` token works against **both** the model gateway and the account endpoints (the previous documentation said the OAuth token was refused by the gateway — that was wrong), so one sign-in covers models, credits and the check-in.
|
|
352
|
+
- The plugin now mounts two routes: the status route the card reads, and a POST route for the actions (claim, sign-in, the setting), which is **loopback-only**.
|
|
353
|
+
|
|
354
|
+
### 0.4.2 (2026-10-19)
|
|
355
|
+
|
|
356
|
+
- **Discoverability**: the npm description is now bilingual, the keywords were completed (`dsh`, `credits`, `usage`, …), and the repository description and topics were updated to match.
|
|
357
|
+
- Added [`awesome-dsh-plugin-submission/`](awesome-dsh-plugin-submission/) to the repository: the market entry draft and the rules it has to follow.
|
|
358
|
+
- Added an English README ([README.en.md](README.en.md)).
|
|
359
|
+
|
|
360
|
+
### 0.4.1 (2026-10-19)
|
|
361
|
+
|
|
362
|
+
- **Fixed the ledger paging parameter**: the endpoint **accepts** `page` and then **ignores** it; the parameter it actually reads is `page_number`. An account with more than a hundred lots used to collect the same page over and over.
|
|
363
|
+
- **Corrected the expiry rule**: "29 days after the grant" was an artifact of reading a UTC grant time against a UTC expiry time. The real rule is **thirty calendar days after the day it was granted** (at midnight in the account's own timezone). Some lots run a full year, so the expiry is always read from the response's `expire_at_ms`.
|
|
364
|
+
- Two statements were corrected as well: expiry does **not** zero a lot's `remaining_amount` (it can leave a small negative residue) — it excludes the expired lot from the balance instead; and the ledger total equals the balance digit for digit while the account is idle, so a difference between two samples is real consumption in between.
|
|
365
|
+
|
|
366
|
+
### 0.4.0 (2026-10-19)
|
|
367
|
+
|
|
368
|
+
- **Added "credits about to expire"**: credits are granted one lot at a time and each lot expires on its own (thirty calendar days after the day it was granted). The plugin reads the credit ledger `GET /minimax-cloud/api/v1/credit/details` and shows, in the **plugin page** and in the pill, the credits expiring within 7 days and within 30 days, the nearest expiry date, and a list of expiring lots.
|
|
369
|
+
- **Only lots that still have a balance are counted**, so that lots which are "used up but about to expire" are not counted as a loss.
|
|
370
|
+
- **Plugin page card**: a new card under "Settings → Plugins → DSH Connect MiniMax Code" (balance, granted/purchased split, expiry distribution, expiring lots, consumption on this machine and equivalent cost); the list itself shows a one-line summary.
|
|
371
|
+
- **The pill and the card share one state store and one poller**, so multiple interfaces do not request the account API repeatedly.
|
|
372
|
+
|
|
373
|
+
### 0.3.0 (2026-10-19)
|
|
374
|
+
|
|
375
|
+
- **Added the credit balance display**: the pill under the composer input box shows the remaining MiniMax Code credits (granted/purchased split); expanding it shows the consumption between two reads, the tokens accumulated on this machine, and the equivalent cost converted at the official price list.
|
|
376
|
+
- **Added usage metering**: the `usage` of every request is taken from the upstream response itself, accumulated locally and converted into money.
|
|
377
|
+
- **Located the account API**: found the `POST /matrix/api/v1/commerce/get_membership_info` the desktop app really calls every day, and **overturned the 0.2.x conclusion that the quota could not be displayed** — the reason is at the end of "Credit balance and usage".
|
|
378
|
+
- **Adjusted the token strategy**: the model gateway uses the JWT, the account API uses the desktop app's OAuth token; when the latter expires, the plugin refreshes it with the refresh token the app already stored and writes it back in the client's original format, which leaves the desktop app's login state intact.
|
|
379
|
+
- **First release with a browser-side plugin**: adds `lib/client.js` (a classic script) that registers a single composer dock pill. **A DSH restart is required after the upgrade**.
|
|
380
|
+
|
|
381
|
+
### 0.2.3 (2026-10-19)
|
|
382
|
+
|
|
383
|
+
- **Synced with upstream model changes**: MiniMax retired the two older models `M2.7` and `M2.7-highspeed`; the test model that had been rolling out anonymously (codename "space bunny" / 太空兔) was released as **`M3.1-Flash`**. The current live upstream catalog is `M3` and `M3.1-Flash`.
|
|
384
|
+
- The built-in fallback list and the documentation were updated accordingly. **Signed-in users see the model change without updating the plugin** (the list comes from the live upstream catalog); this release only brings the signed-out fallback, the tests and the docs in line with upstream.
|
|
385
|
+
|
|
386
|
+
For earlier versions, see [GitHub Releases](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/releases) and the [npm version list](https://www.npmjs.com/package/dsh-connect-minimaxcode?activeTab=versions).
|
|
387
|
+
|
|
388
|
+
## Marketplace listing and presentation
|
|
389
|
+
|
|
390
|
+
The plugin ships an installable `dsh.bundle` manifest and is published to npm. 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 listing has been submitted as PR [#8110](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8110). A draft and the operating notes are kept in the repository: [`awesome-dsh-plugin-submission/`](awesome-dsh-plugin-submission/).
|
|
391
|
+
|
|
392
|
+
**Other directories and marketplaces** (entries submitted or already listed, text refreshed to 0.5.1):
|
|
393
|
+
|
|
394
|
+
| Channel | State |
|
|
395
|
+
|---|---|
|
|
396
|
+
| [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) (the registry) | Listed; entry refresh PR [#8380](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8380) |
|
|
397
|
+
| [awesome-deepseek-harness](https://github.com/0xsline/awesome-deepseek-harness) | PR [#727](https://github.com/0xsline/awesome-deepseek-harness/pull/727) |
|
|
398
|
+
| [Awesome-DeepSeek-Harness-Plugins](https://github.com/Zhiyuan-Fan/Awesome-DeepSeek-Harness-Plugins) | PR [#92](https://github.com/Zhiyuan-Fan/Awesome-DeepSeek-Harness-Plugins/pull/92) |
|
|
399
|
+
| [dsh-suite](https://github.com/whyihaveyou/dsh-suite) | PR [#66](https://github.com/whyihaveyou/dsh-suite/pull/66) |
|
|
400
|
+
| [awesome-dsh-plugins](https://github.com/kejixiaoliang/awesome-dsh-plugins) | PR [#143](https://github.com/kejixiaoliang/awesome-dsh-plugins/pull/143) |
|
|
401
|
+
| [awesome-dsh-plugins](https://github.com/dshworks/awesome-dsh-plugins) | PR [#176](https://github.com/dshworks/awesome-dsh-plugins/pull/176) |
|
|
402
|
+
| [awesome-deepseek-harness](https://github.com/awesome-deepseekharness/awesome-deepseek-harness) | PR [#177](https://github.com/awesome-deepseekharness/awesome-deepseek-harness/pull/177) |
|
|
403
|
+
| [dsh-plugin-radar](https://github.com/AdamPlatin123/dsh-plugin-radar) | PR [#964](https://github.com/AdamPlatin123/dsh-plugin-radar/pull/964) |
|
|
404
|
+
| [dsh-community-plugins](https://github.com/zhu1090093659/dsh-community-plugins) | PR [#46](https://github.com/zhu1090093659/dsh-community-plugins/pull/46) |
|
|
405
|
+
| [dsh-plugin.org](https://github.com/dshplugin/dsh-plugin-hub) | Listed (automatic scan; the npm package shows up in the DSH plugin centre) |
|
|
406
|
+
| [dshbase](https://github.com/ylwl1997/dshbase) | Listed |
|
|
407
|
+
| [awesome-deepseek-harness-plugins](https://github.com/walkinglabs/awesome-deepseek-harness-plugins) | PR [#117](https://github.com/walkinglabs/awesome-deepseek-harness-plugins/pull/117) |
|
|
408
|
+
| [awesome-deepseek-harness](https://github.com/Dominic789654/awesome-deepseek-harness) | PR [#663](https://github.com/Dominic789654/awesome-deepseek-harness/pull/663) |
|
|
409
|
+
| [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) |
|
|
410
|
+
|
|
411
|
+
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.
|
|
412
|
+
|
|
413
|
+
> **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.
|
|
414
|
+
>
|
|
415
|
+
> 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.
|
|
416
|
+
|
|
417
|
+
## Uninstall
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
dsh plugin --profile desktop remove dsh-connect-minimaxcode
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Development
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
pnpm install
|
|
427
|
+
pnpm test # unit tests + host integration tests
|
|
428
|
+
pnpm typecheck # type check
|
|
429
|
+
pnpm build # build
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The build depends on `@earendil-works/pi-ai`, which must resolve to the same version as the host's `dsh-llm-pi-ai` (currently `0.85.x`), otherwise the types are incompatible.
|
|
433
|
+
|
|
434
|
+
The UI half (`src/client.ts`) is compiled separately by `tsconfig.client.json` into a **classic script** (`lib/client.js`): the host loads the client bundle with a `<script>` tag, and an ESM output would break Web startup outright (`Cannot use import statement outside a module`), so there is deliberately no bundler here and no bundled React — React comes from the host's browser module table.
|
|
435
|
+
|
|
436
|
+
**`lib/` is build output that is tracked in version control**, not a generated artifact. This is deliberate: a git-source install gets the repository content as it is, and without `lib/` pnpm decides the package "needs to be built" and triggers `prepare`, which is then blocked by the build-script allowlist. After changing the source, rebuild and commit:
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
pnpm build && git add lib
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Similar plugins
|
|
443
|
+
|
|
444
|
+
These plugins follow the same idea — **reuse the login state your local desktop app already has and bring the model into DSH** — with no separate API key to configure. They were written independently by different authors and are not affiliated with this project.
|
|
445
|
+
|
|
446
|
+
| Plugin | Data source | Daily check-in for free quota | Quota / usage display |
|
|
447
|
+
|---|---|---|---|
|
|
448
|
+
| **dsh-connect-trae** | Trae (China + international) | ✅ China region | ✅ Work / general credit overview |
|
|
449
|
+
| **dsh-qoder-connect** | Qoder (China + international, PAT) | ✅ 100 Credits per day | ✅ Sidebar quota card + breakdown |
|
|
450
|
+
| **dsh-workbuddy-connect** | WorkBuddy (China + international) | — | ✅ Remaining credits + breakdown |
|
|
451
|
+
|
|
452
|
+
> 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.
|
|
453
|
+
|
|
454
|
+
### [dsh-connect-trae](https://github.com/dingminhua/dsh-connect-trae)
|
|
455
|
+
|
|
456
|
+
Connects a locally signed-in Trae to DSH; the China and international versions can be used at the same time. The China version supports a daily check-in to claim credits, and provides a read-only overview of Work / general credits. Note that Trae's check-in is limited to "once per device per day".
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
dsh plugin --profile desktop add dsh-connect-trae
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### [dsh-qoder-connect](https://github.com/masknull/dsh-qoder-connect)
|
|
463
|
+
|
|
464
|
+
Connects Qoder through a personal access token (PAT), with independent configuration for the two regions. It signs in automatically every day to claim 100 Credits of compute quota, the check-in time can be customized (10:00 by default, UTC+8), and it makes up for a missed check-in on startup; it also shows the quota in the sidebar with a per-package breakdown.
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
dsh plugin --profile desktop add dsh-qoder-connect
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### [dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)
|
|
471
|
+
|
|
472
|
+
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:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
dsh plugin --profile web exec dsh-workbuddy-connect status
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
> 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.
|
|
479
|
+
|
|
480
|
+
## Contributors
|
|
481
|
+
|
|
482
|
+
- **[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.
|
|
483
|
+
- **[azsora](https://github.com/azsora)** — found the plan's rate-limit endpoint in the desktop app's own traffic and wired the 5-hour and weekly usage windows into the credit pill and the plugin page ([#4](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/pull/4)): the window length is derived from the response's own edges rather than hard-coded. It became 0.5.5.
|
|
484
|
+
- **[qzz19970504](https://github.com/qzz19970504)** — found and fixed the wiring gap that made every image upload fail ([#3](https://github.com/JeffTsuiCUG/dsh-connect-minimaxcode/pull/3)): the catalog declared the `image` modality, but the adapter never connected the host's durable attachment service, so pi-ai refused image requests before they were sent. The fix became 0.5.3.
|
|
485
|
+
- Plugin author: **[JeffTsuiCUG](https://github.com/JeffTsuiCUG)**
|
|
486
|
+
|
|
487
|
+
## Acknowledgements
|
|
488
|
+
|
|
489
|
+
- **[corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)** — **the inspiration for this plugin**. The overall idea of "reuse the desktop app's local login state and bring the already-signed-in model into DSH" was established first by that project, and this plugin follows the same approach; the read-only reuse of credentials was designed with it in mind.
|
|
490
|
+
- **[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).
|
|
491
|
+
- [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.
|
|
492
|
+
|
|
493
|
+
> 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.
|
|
494
|
+
|
|
495
|
+
## License
|
|
496
|
+
|
|
497
|
+
[MIT](./LICENSE)
|