magpie-plugin-msmoli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +550 -372
- package/bin/sync.mjs +1569 -148
- package/index.mjs +2456 -272
- package/package.json +3 -4
- package/README.zh-CN.md +0 -358
package/README.md
CHANGED
|
@@ -1,12 +1,74 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# magpie-plugin-msmoli
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
**ModelScope daily credits, claimed for you every day.**
|
|
6
|
+
|
|
7
|
+
A [magpie](https://github.com/yetone/magpie) plugin that checks in to
|
|
8
|
+
ModelScope once a day and puts the balance and today's spend on the
|
|
9
|
+
account's usage card. Works with both **modelscope.cn** and **modelscope.ai**.
|
|
10
|
+
|
|
11
|
+
[](https://www.npmjs.com/package/magpie-plugin-msmoli)
|
|
12
|
+
[](https://github.com/MisonL/magpie-plugin-msmoli/actions/workflows/test.yml)
|
|
13
|
+
[](LICENSE)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
> [!WARNING]
|
|
18
|
+
> ModelScope has no check-in API. This plugin earns the daily grant by
|
|
19
|
+
> making a website sign-in happen, which ModelScope's terms may treat as
|
|
20
|
+
> abuse. **Read [Purpose, responsibility, and risk](#purpose-responsibility-and-risk)
|
|
21
|
+
> before you install it.**
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
● msmoli API key …a1b2 · 魔搭社区 魔粒 1% (今日用 2 / 领 250 · 余额 249.5) ↻ tomorrow 09:31
|
|
25
|
+
● msmoli Mison · 魔搭社区(国际版) 魔粒 0% (今日用 0 / 领 300 · 余额 300) ↻ tomorrow 10:00
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Before the day's grant lands, the line ends with when it will:
|
|
29
|
+
`今日用 0 / 领 0 · 余额 300 · 09:56 领取(北京时间)`.
|
|
6
30
|
|
|
7
|
-
##
|
|
31
|
+
## Contents
|
|
8
32
|
|
|
9
|
-
|
|
33
|
+
- [Features](#features)
|
|
34
|
+
- [Quick start](#quick-start)
|
|
35
|
+
- [What credits are](#what-credits-are)
|
|
36
|
+
- [Purpose, responsibility, and risk](#purpose-responsibility-and-risk)
|
|
37
|
+
- [Install](#install)
|
|
38
|
+
- [Accounts and sites](#accounts-and-sites)
|
|
39
|
+
- [How the check-in works](#how-the-check-in-works)
|
|
40
|
+
- [Why its own provider, with no models](#why-its-own-provider-with-no-models)
|
|
41
|
+
- [Privacy](#privacy)
|
|
42
|
+
- [Scope and limits](#scope-and-limits)
|
|
43
|
+
- [Troubleshooting](#troubleshooting)
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
| | |
|
|
48
|
+
|---|---|
|
|
49
|
+
| 🗓️ **Daily check-in** | Claims the 200 + 50 daily grant once per Beijing day, per account |
|
|
50
|
+
| 📊 **Usage card** | Balance, today's spend and earnings, and when the soonest credits expire |
|
|
51
|
+
| 🌏 **Two sites, one card** | modelscope.cn and modelscope.ai accounts side by side; each token's site is found automatically |
|
|
52
|
+
| ⏰ **Check-in window** | Claims at an account's own minute in a configurable window (default 08:30–10:00) |
|
|
53
|
+
| 🔒 **Token stays put** | Lives only in magpie's `plugin-auth.json`; masked in every message the plugin or its CLI prints |
|
|
54
|
+
| 🔁 **Sync CLI** | Copies the key you already gave magpie's ModelScope preset into the plugin |
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
magpie plugin add magpie-plugin-msmoli
|
|
60
|
+
printf 'ms-your-token\n' | magpie plugin login msmoli
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then turn the check-in on: **Settings → Usage → Plugins → `msmoli`**.
|
|
64
|
+
|
|
65
|
+
Get the token from your site's access-token page:
|
|
66
|
+
[modelscope.cn](https://modelscope.cn/my/myaccesstoken) ·
|
|
67
|
+
[modelscope.ai](https://modelscope.ai/my/myaccesstoken).
|
|
68
|
+
|
|
69
|
+
## What credits are
|
|
70
|
+
|
|
71
|
+
ModelScope's API-Inference is free, paid in credits:
|
|
10
72
|
|
|
11
73
|
| Grant | Amount | Expires |
|
|
12
74
|
|---|---|---|
|
|
@@ -14,31 +76,37 @@ ModelScope's API-Inference is free, paid in 魔粒:
|
|
|
14
76
|
| Aliyun account bound, per day | 50 | 24 hours |
|
|
15
77
|
| Email / profile / homepage completed | 50 each | 90 days, once |
|
|
16
78
|
|
|
17
|
-
Daily grants
|
|
18
|
-
spend today is gone. A flagship model costs 2
|
|
79
|
+
Daily grants last **24 hours only** and do not accumulate: what you don't
|
|
80
|
+
spend today is gone. A flagship model costs 2 credits per call (mainstream 1,
|
|
19
81
|
lightweight 0.5), so the daily 250 is roughly 125 calls.
|
|
20
82
|
|
|
83
|
+
The day's numbers come from the ledger, which a busy account can spread over
|
|
84
|
+
several pages. When a page cannot be read (a 5xx, a budget already gone), the
|
|
85
|
+
card does not present a floor as a fact: its line ends with
|
|
86
|
+
`账单未读完,数字偏低`. A check-in in that state does not read "no grant" into
|
|
87
|
+
a ledger it could not finish either — it answers `failed` and the host retries
|
|
88
|
+
in half an hour.
|
|
89
|
+
|
|
21
90
|
## Purpose, responsibility, and risk
|
|
22
91
|
|
|
23
|
-
**What it is for.** A personal utility: it claims the daily
|
|
24
|
-
ModelScope
|
|
92
|
+
**What it is for.** A personal utility: it claims the daily credit grant of the
|
|
93
|
+
ModelScope account *you* sign in, and does nothing else. It is not an
|
|
25
94
|
official ModelScope product; it is not affiliated with ModelScope, Alibaba or
|
|
26
|
-
magpie, and it collects nothing about you
|
|
95
|
+
magpie, and it collects nothing about you. The token stays in magpie's own
|
|
27
96
|
`plugin-auth.json`, and every request goes to the site the account signed in
|
|
28
97
|
to.
|
|
29
98
|
|
|
30
99
|
**The risk you are taking.** ModelScope has no check-in endpoint. The daily
|
|
31
100
|
grant exists for a *real* website sign-in, and this plugin obtains it by
|
|
32
|
-
calling `login/info` once a day with the browser's session cookie
|
|
101
|
+
calling `login/info` once a day with the browser's session cookie: a script
|
|
33
102
|
posing as the browser, not a button the official client offers. ModelScope's
|
|
34
103
|
user agreement forbids plugins that interfere with the service (4.1.2(1)) and
|
|
35
|
-
|
|
36
|
-
user's benefits or blacklist them for abuse (2.3.1 and the
|
|
104
|
+
point farming with third-party tools (4.1.2(5)), and lets the platform freeze a
|
|
105
|
+
user's benefits or blacklist them for abuse (2.3.1 and the credit rules). An
|
|
37
106
|
account is real-name verified and often tied to Aliyun, so what is at stake is
|
|
38
107
|
the account, not the 250 credits that expire the same day. No account has been
|
|
39
|
-
reported frozen for running this, and no one can promise one never will be
|
|
40
|
-
|
|
41
|
-
project can answer. Whether to run it is a decision about your own account.
|
|
108
|
+
reported frozen for running this, and no one can promise one never will be.
|
|
109
|
+
Whether to run it is a decision about your own account.
|
|
42
110
|
|
|
43
111
|
**Responsibility.** You are responsible for complying with ModelScope's terms
|
|
44
112
|
when you use this. The software is provided under the MIT licence, without
|
|
@@ -54,198 +122,300 @@ npm.
|
|
|
54
122
|
|
|
55
123
|
## Install
|
|
56
124
|
|
|
57
|
-
|
|
125
|
+
You need magpie itself (<https://usemagpie.ai>). On macOS, `magpie --version`
|
|
126
|
+
tells you whether it is on `PATH`; if not, use the app's full path,
|
|
58
127
|
`/Applications/magpie.app/Contents/MacOS/magpie`.
|
|
59
128
|
|
|
60
|
-
|
|
129
|
+
The plugin relies on the host's plugin API — the account scope a check-in and
|
|
130
|
+
a usage read run in, the provider argument `auth.checkin` is handed, the
|
|
131
|
+
`input.directory` the plugin's own folder comes from, and `auth.set` writing
|
|
132
|
+
back into the same account. It is checked against **magpie 0.1.3354**; an older
|
|
133
|
+
host may lack part of that contract.
|
|
61
134
|
|
|
62
|
-
|
|
63
|
-
|
|
135
|
+
Pick one of three ways. They all end the same: an `msmoli` row under
|
|
136
|
+
**Settings → Usage → Plugins**. The sign-in is kept per provider id, not per
|
|
137
|
+
folder, so switching between them (or moving a clone) keeps the token.
|
|
138
|
+
|
|
139
|
+
<table>
|
|
140
|
+
<tr><th>From npm (recommended)</th><th>From GitHub</th><th>From a clone</th></tr>
|
|
141
|
+
<tr valign="top">
|
|
142
|
+
<td>
|
|
64
143
|
|
|
65
144
|
```bash
|
|
66
|
-
magpie plugin add
|
|
67
|
-
|
|
145
|
+
magpie plugin add \
|
|
146
|
+
magpie-plugin-msmoli
|
|
68
147
|
```
|
|
69
148
|
|
|
70
|
-
|
|
149
|
+
`magpie plugin update` follows npm releases.
|
|
71
150
|
|
|
72
|
-
|
|
151
|
+
</td>
|
|
152
|
+
<td>
|
|
73
153
|
|
|
74
154
|
```bash
|
|
75
|
-
magpie plugin add
|
|
76
|
-
|
|
155
|
+
magpie plugin add \
|
|
156
|
+
github:MisonL/magpie-plugin-msmoli
|
|
77
157
|
```
|
|
78
158
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
### Install from a clone
|
|
159
|
+
Installed into magpie's plugin folder; updates with `magpie plugin update`.
|
|
82
160
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
while that folder is there, and an update means pulling and adding it again.
|
|
161
|
+
</td>
|
|
162
|
+
<td>
|
|
86
163
|
|
|
87
164
|
```bash
|
|
88
165
|
git clone https://github.com/MisonL/magpie-plugin-msmoli.git
|
|
89
166
|
cd magpie-plugin-msmoli
|
|
90
167
|
magpie plugin add "$PWD"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Referenced in place: it loads only while the folder exists. Update by pulling.
|
|
171
|
+
|
|
172
|
+
</td>
|
|
173
|
+
</tr>
|
|
174
|
+
</table>
|
|
175
|
+
|
|
176
|
+
Then sign in and turn the check-in on:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
91
179
|
printf 'ms-your-token\n' | magpie plugin login msmoli
|
|
92
180
|
```
|
|
93
181
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
switching between the two forms (or moving the clone) keeps the token — no
|
|
97
|
-
second login.
|
|
182
|
+
The switch is **Settings → Usage → Plugins → `msmoli`**, or `settings.json` in
|
|
183
|
+
magpie's own folder (`~/.config/magpie` for an installed magpie):
|
|
98
184
|
|
|
99
|
-
|
|
185
|
+
```json
|
|
186
|
+
{ "pluginCheckins": { "msmoli": true } }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
One more optional step. The Providers row reads **未暴露模型 / no models
|
|
190
|
+
exposed** — accurate (the plugin registers no models) but it reads like
|
|
191
|
+
something failed. Say it serves none on purpose, and the row reads **仅看用量
|
|
192
|
+
/ usage only**:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
magpie provider models msmoli none
|
|
196
|
+
```
|
|
100
197
|
|
|
101
|
-
The
|
|
102
|
-
|
|
103
|
-
daily grant rules, **but tokens are site-specific** — a token from `modelscope.cn`
|
|
104
|
-
answers 401 on `modelscope.ai`, and vice versa.
|
|
198
|
+
The same `ms-` token is both the inference `Authorization: Bearer` and the web
|
|
199
|
+
session cookie, so nothing else needs configuring.
|
|
105
200
|
|
|
106
|
-
|
|
201
|
+
### Already use ModelScope for chat? Sync that key
|
|
202
|
+
|
|
203
|
+
If you added ModelScope to magpie earlier for chat, its key is already in
|
|
204
|
+
`providers.json`. The bundled CLI logs the same token into the plugin:
|
|
107
205
|
|
|
108
206
|
```bash
|
|
109
|
-
|
|
207
|
+
npx magpie-plugin-msmoli --status # see both sides, change nothing
|
|
208
|
+
npx magpie-plugin-msmoli --to-plugin # copy the preset's key into the plugin
|
|
110
209
|
```
|
|
111
210
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
211
|
+
From a GitHub install, run magpie's own copy instead:
|
|
212
|
+
`node ~/.config/magpie/plugins/node_modules/magpie-plugin-msmoli/bin/sync.mjs`.
|
|
213
|
+
From a clone, run `./bin/sync.mjs` (or `node bin/sync.mjs` on Windows and
|
|
214
|
+
WSL). npm 12 blocks `npx github:…` by default, so don't start from that.
|
|
116
215
|
|
|
117
|
-
|
|
216
|
+
`npx`, `magpie plugin add magpie-plugin-msmoli` and `magpie plugin update` all
|
|
217
|
+
follow the npm package. The current release is **0.3.0**: one provider for
|
|
218
|
+
both sites, the row-level ledger dedupe, the chunked-echo masking, and the
|
|
219
|
+
budgets this document describes.
|
|
118
220
|
|
|
119
|
-
If you
|
|
120
|
-
|
|
121
|
-
|
|
221
|
+
If you are on **0.2.0**, that version ships an older `bin/sync.mjs` with both
|
|
222
|
+
directions, but it keeps the international site in a second provider and
|
|
223
|
+
predates this round of fixes. Upgrade (`npm i -g magpie-plugin-msmoli@latest`
|
|
224
|
+
or `magpie plugin update msmoli`) to get what this document describes.
|
|
122
225
|
|
|
123
|
-
It syncs the domestic site only
|
|
124
|
-
`modelscope.cn` one.
|
|
125
|
-
in by hand (`magpie plugin login msmoli-intl`).
|
|
226
|
+
It syncs the **domestic site only**: the built-in preset it reads is the
|
|
227
|
+
`modelscope.cn` one. Sign an international account in by hand.
|
|
126
228
|
|
|
127
|
-
|
|
229
|
+
| Flag | What it does |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `--status` | Report both sides and the plan; write nothing |
|
|
232
|
+
| `--to-plugin` | Log the preset's key into the plugin |
|
|
233
|
+
| `--to-preset` | The other way round; a preset that already holds a different key needs `--force` |
|
|
234
|
+
| `--force` | Replace a single existing plugin account whose key differs |
|
|
235
|
+
| `--from <id>` | Pick the preset when more than one provider could be it |
|
|
236
|
+
| `--dry-run` | Print the commands, masked, without running them or writing anything; no magpie binary needed |
|
|
237
|
+
| `--dir <folder>` | Where magpie's config is (a portable install's `data` folder) |
|
|
238
|
+
| `--magpie <path>` | Path to the magpie binary; a bare `magpie` is refused |
|
|
239
|
+
| `--version` | Print the version (read from the package's own `package.json`) |
|
|
240
|
+
|
|
241
|
+
<details>
|
|
242
|
+
<summary><b>How the CLI keeps your config and your token safe</b></summary>
|
|
243
|
+
|
|
244
|
+
- **It never writes magpie's files itself.** It reads them directly, and every
|
|
245
|
+
write goes through magpie's own CLI (`magpie plugin login`,
|
|
246
|
+
`magpie plugin logout`, `magpie provider key`).
|
|
247
|
+
- **A preset with no key is filled without `--force`, in either direction.**
|
|
248
|
+
There is nothing to overwrite; `--force` is for replacing a different key
|
|
249
|
+
that chat may be using.
|
|
250
|
+
- **It never adds a second account by accident.** Same key on both sides: it
|
|
251
|
+
changes nothing. Different keys: it refuses, because a second `login` would
|
|
252
|
+
add an account. `--force` replaces only when exactly one account exists.
|
|
253
|
+
- **It reads and writes the same magpie.** `--dir` says where to read; magpie's
|
|
254
|
+
CLI has no such flag, so a write always lands in the binary's own folder. If
|
|
255
|
+
`--dir` and `--magpie` name different places, it stops before writing.
|
|
256
|
+
"Different" means different folders, not different strings: a trailing
|
|
257
|
+
slash, `/private/tmp` vs `/tmp` on a Mac, and `data/.` all count as the same.
|
|
258
|
+
- **`--magpie` must be a path.** A bare `magpie` resolves through `PATH`, and
|
|
259
|
+
the first `magpie` on it would be handed the token.
|
|
260
|
+
- **A value flag given twice is refused**, not silently last-wins.
|
|
261
|
+
`--dir=<folder>` is read as well.
|
|
262
|
+
- **The token is masked in everything it prints, failures included.** That
|
|
263
|
+
covers magpie's own output, and keys of other shapes too: GitHub and Slack
|
|
264
|
+
tokens, AWS secrets, UUIDs, hex digests, base64 bodies. A key with a space,
|
|
265
|
+
newline or invisible character pasted into it is still masked whole, and so
|
|
266
|
+
is one printed another way: upper-cased, in full-width characters, or broken
|
|
267
|
+
into short groups (`ms-01234 56789abc …`), and so is a vendor key cut the
|
|
268
|
+
same way (`ghp_ 1234 AbCd 5678 …`, `sk-a nt-a pi03 …`, one character per
|
|
269
|
+
word, `AKIAIOSF ODNN7EXA MPLE`) — the words are judged as one key joined
|
|
270
|
+
back up. A value that starts like a path keeps its shape, and only the
|
|
271
|
+
key-like part inside it is masked, so `--dir /tmp/ghp_16C7…` still names the
|
|
272
|
+
folder. What the masking cannot know: a key typed into an argument the CLI
|
|
273
|
+
does not know, inside a path segment (`/keys/sk-ant-…`), and a run of words
|
|
274
|
+
that reads as prose as well as as a key.
|
|
275
|
+
|
|
276
|
+
</details>
|
|
277
|
+
|
|
278
|
+
## Accounts and sites
|
|
279
|
+
|
|
280
|
+
### The international site (modelscope.ai)
|
|
281
|
+
|
|
282
|
+
There is one provider, `msmoli`, and it serves both sites. Tokens are
|
|
283
|
+
site-specific (a `modelscope.cn` token answers 401 on `modelscope.ai`, and the
|
|
284
|
+
other way round), so the plugin works out each account's site from its token:
|
|
285
|
+
|
|
286
|
+
1. The first read goes to the domestic site.
|
|
287
|
+
2. A 401 sends it on to the international one, and so does a failure that is
|
|
288
|
+
not an answer at all (a transport error, a proxy's page, a 5xx, a JSON 429):
|
|
289
|
+
those say nothing about which site holds the token. The same is true of a
|
|
290
|
+
*remembered* site that times out or answers with nothing usable (a 4xx as
|
|
291
|
+
well as a 5xx): it is not the only site tried, the other one is asked
|
|
292
|
+
within the same request budget. When nothing else answers, the first
|
|
293
|
+
answer is the one reported.
|
|
294
|
+
3. The answer is remembered for the rest of the process (the last 64 tokens
|
|
295
|
+
seen, oldest evicted first), and an international
|
|
296
|
+
one is also kept on the sign-in (`site`), so a restarted host goes straight
|
|
297
|
+
there instead of paying the 401 again. A 401 from a remembered site drops
|
|
298
|
+
it and the *other* site is asked — the sign-in may have been re-keyed with
|
|
299
|
+
a token of the other site, and the site that just refused the token is not
|
|
300
|
+
worth a second request.
|
|
301
|
+
|
|
302
|
+
The card's plan line tells you which site an account is on (`魔搭社区` or
|
|
303
|
+
`魔搭社区(国际版)`), and a 401 names the site that refused (or 任一站点 when
|
|
304
|
+
both did).
|
|
305
|
+
|
|
306
|
+
To add an international account, sign in again with that site's token. It
|
|
307
|
+
lands under the same `msmoli` card:
|
|
128
308
|
|
|
129
309
|
```bash
|
|
130
|
-
|
|
131
|
-
|
|
310
|
+
printf 'ms-your-intl-token\n' | magpie plugin login msmoli
|
|
311
|
+
magpie accounts # both accounts, each with its own credit window
|
|
132
312
|
```
|
|
133
313
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
folder the executable sits in. `--magpie` takes a path, absolute or relative —
|
|
141
|
-
a bare `magpie` is refused, because that one is resolved through `PATH` and the
|
|
142
|
-
first `magpie` on it would be handed the token. `npx magpie-plugin-msmoli` runs
|
|
143
|
-
the CLI from npm (published as 0.1.0) — npm 12 blocks `npx github:…` by
|
|
144
|
-
default, so don't start from that.
|
|
145
|
-
|
|
146
|
-
`--dir` says where to *read*; magpie's CLI has no such flag, so a write always
|
|
147
|
-
lands in the binary's own folder. Given both and they disagree, the tool stops
|
|
148
|
-
before writing anything rather than reading one magpie and writing another.
|
|
149
|
-
"Disagree" means the folders are different places, not that the strings differ:
|
|
150
|
-
a shell completes a directory with a trailing slash, `/private/tmp` and `/tmp`
|
|
151
|
-
are one folder on a Mac, and `data/.` is `data` — each names the binary's own
|
|
152
|
-
folder and is allowed.
|
|
153
|
-
|
|
154
|
-
It reads magpie's config but never writes it: the writes are magpie's own
|
|
155
|
-
(`magpie plugin login`, `magpie plugin logout`, `magpie provider key`), and the
|
|
156
|
-
token is masked in everything it prints, failures included. When the plugin
|
|
157
|
-
already uses the same key it changes nothing; when the two keys differ it
|
|
158
|
-
refuses, because a second `login` would add a second account — pass `--force`
|
|
159
|
-
to replace a single existing account. `--to-preset` goes the other way (needs
|
|
160
|
-
`--force`: that key is what chat uses). If more than one provider could be the
|
|
161
|
-
built-in side (say, a relay of the same host), the tool refuses to guess and
|
|
162
|
-
`--from <id>` picks one; `--dry-run` prints the commands without running them.
|
|
163
|
-
A value flag given twice (`--dir a --dir b`) is refused, not silently last-wins,
|
|
164
|
-
and `--dir=<folder>` is read as well. `--from` and a stray argument are echoed
|
|
165
|
-
back masked when the value looks like a key — a token pasted into the wrong
|
|
166
|
-
position is never printed whole, whatever its shape. That covers the shapes
|
|
167
|
-
`ms-…` does not: a GitHub or Slack token, an AWS secret (which carries `/`), a
|
|
168
|
-
UUID, a hex digest, a base64 body — long, or short enough that only its trailing
|
|
169
|
-
`=` (or a `+` beside a case mix) gives it away. The judgement runs
|
|
170
|
-
word by word,
|
|
171
|
-
so a key with a space or a newline pasted onto it — the shape a copy out of a
|
|
172
|
-
terminal or a chat window leaves — or quoted inside a line is still the key it
|
|
173
|
-
is. A word that is one key is masked whole, ends and all, including one split with
|
|
174
|
-
separators — a hex digest with dashes, a key written as `AbCdEfGh.IjKlMnOp…` —
|
|
175
|
-
and an AWS secret, whose two parts of 12+ characters with mixed case mark it. An
|
|
176
|
-
invisible character dropped inside a key (a zero-width space or `\x01`, the
|
|
177
|
-
shape a copy out of a chat window leaves) does not save it either: the
|
|
178
|
-
control/format characters sitting between key-alphabet characters are removed
|
|
179
|
-
before masking, so the two halves are rejoined into the one key they are —
|
|
180
|
-
each half alone falls under the length floor and would have leaked. A
|
|
181
|
-
word that starts like a path (`/`, `~`, `.`, a drive letter, a UNC root) keeps
|
|
182
|
-
its shape, and only the key-like run inside it is masked, so `--dir
|
|
183
|
-
/tmp/ghp_16C7…` still names the folder it read; a key pasted in *behind* a path
|
|
184
|
-
prefix (`~/keys/sk-ant-…`) reads as part of that path, which is what keeping a
|
|
185
|
-
path's shape costs. The folder is reported
|
|
186
|
-
the same way on every line that names it, and magpie's own output goes through
|
|
187
|
-
the same door — a key of another provider in the child's error message is
|
|
188
|
-
masked there too, not only the `ms-` shape. A provider id and an account name
|
|
189
|
-
are read out of magpie's own files and go through the same door: a key pasted
|
|
190
|
-
into either field is masked on every line the tool names it.
|
|
191
|
-
|
|
192
|
-
Turn the check-in on: Settings → Usage → Plugins → the `msmoli` row, or in
|
|
193
|
-
`settings.json` (in magpie's own folder, `~/.config/magpie` for an installed
|
|
194
|
-
magpie):
|
|
314
|
+
If you are coming from 0.2.0, which kept the international site in a second
|
|
315
|
+
provider called `msmoli-intl`: that provider is gone, and the sign-in filed
|
|
316
|
+
under it is not read by this version. Sign in once more under `msmoli` with
|
|
317
|
+
the same token (the command above), and the card picks the account up. Aliases
|
|
318
|
+
set on the old `msmoli-intl` row belong to a provider this version no longer
|
|
319
|
+
has.
|
|
195
320
|
|
|
196
|
-
|
|
197
|
-
{
|
|
198
|
-
"pluginCheckins": {
|
|
199
|
-
"msmoli": true
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
```
|
|
321
|
+
### More than one account
|
|
203
322
|
|
|
204
|
-
The
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
(`msmoli-intl`), the token comes from <https://modelscope.ai/my/myaccesstoken>
|
|
208
|
-
instead — the two sites' tokens do not interchange.
|
|
323
|
+
The plugin works per account, not per token. magpie keeps each signed-in token
|
|
324
|
+
under its own key (the first as `msmoli`, later ones as `msmoli#xxxxxx`) and
|
|
325
|
+
asks the check-in once per account. With two tokens:
|
|
209
326
|
|
|
210
|
-
|
|
211
|
-
|
|
327
|
+
- each account gets **its own target minute** that day (hashed from the day
|
|
328
|
+
and that account's token), so they normally claim at different minutes;
|
|
329
|
+
- each account's check-in is recorded, retried and shown separately;
|
|
330
|
+
- the name read at check-in is cached per token, so one account's nickname
|
|
331
|
+
never appears on another's row.
|
|
212
332
|
|
|
333
|
+
Three things worth knowing once there are two:
|
|
334
|
+
|
|
335
|
+
- Signing the **same token in twice** can leave two account rows: magpie folds
|
|
336
|
+
a repeated sign-in into the first row only while neither row carries a name
|
|
337
|
+
(host.js's `settle`), so once the nickname is there the second sign-in is a
|
|
338
|
+
row of its own. Both are asked; the day's grant lands once, and the second
|
|
339
|
+
answers `done`. Remove one in the editor to quiet it.
|
|
340
|
+
- Dropping **one account** of several: **Remove** on its row in the provider's
|
|
341
|
+
editor, or `magpie accounts forget msmoli "<the account as its card shows
|
|
342
|
+
it>"`. `magpie plugin logout msmoli` is the blunt one — it takes no account
|
|
343
|
+
name, so it forgets the provider's whole sign-in.
|
|
344
|
+
- An **alias** is kept under the account's label of the moment (see [Account
|
|
345
|
+
names](#account-names)). Remove an account and sign in again and the label
|
|
346
|
+
resets to the key's tail, so an alias you set under the nickname re-attaches
|
|
347
|
+
only once the next check-in writes the nickname back — or just set it again.
|
|
348
|
+
|
|
349
|
+
The check-in switch, the card and the window (`msmoli.json`) are per provider,
|
|
350
|
+
so one setting covers every account, whichever site it is from.
|
|
351
|
+
|
|
352
|
+
The daily grant belongs to the ModelScope account, not to the key: a second
|
|
353
|
+
token for the same ModelScope user gets `done` rather than another 250.
|
|
354
|
+
|
|
355
|
+
### Account names
|
|
356
|
+
|
|
357
|
+
The card names an account by its ModelScope nickname. An api-type sign-in has
|
|
358
|
+
no name, so the first check-in that runs writes the nickname back as
|
|
359
|
+
`accountId` on the same sign-in (the token is still stored once). Until then a
|
|
360
|
+
fresh install shows `API key …a1b2`. A day already settled is not re-claimed,
|
|
361
|
+
but the name is still worth a `login/info` read, so the nickname appears on
|
|
362
|
+
the first ask after sign-in rather than with the next day's claim.
|
|
363
|
+
|
|
364
|
+
To choose the name yourself, magpie's own alias wins:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
magpie accounts alias msmoli "API key …a1b2" my-main
|
|
213
368
|
```
|
|
214
|
-
|
|
215
|
-
|
|
369
|
+
|
|
370
|
+
The second argument is the account as the card shows it *now*: the key's tail
|
|
371
|
+
before the first check-in, the nickname after. An account you have named is
|
|
372
|
+
left alone; the check-in never writes over your alias.
|
|
216
373
|
|
|
217
374
|
## How the check-in works
|
|
218
375
|
|
|
219
376
|
ModelScope publishes no check-in endpoint. What it does have is a side effect:
|
|
220
377
|
`GET /api/v1/users/login/info` counts as a sign-in, and the first one of the
|
|
221
|
-
day grants `daily_active` 200 and `aliyun_bindlogin` 50.
|
|
222
|
-
|
|
378
|
+
day grants `daily_active` 200 and `aliyun_bindlogin` 50. Repeating it the same
|
|
379
|
+
day grants nothing more.
|
|
223
380
|
|
|
224
381
|
A hook that just calls it can't tell "claimed now" from "claimed hours ago", so
|
|
225
|
-
this one reads the
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
seconds late was recorded as "the account may not be in the campaign". Before
|
|
236
|
-
noon magpie's own provisional re-ask (every 30 minutes) already covers it.
|
|
237
|
-
|
|
238
|
-
Four outcomes follow:
|
|
382
|
+
this one reads the ledger around it:
|
|
383
|
+
|
|
384
|
+
```mermaid
|
|
385
|
+
flowchart LR
|
|
386
|
+
A[Read today's EARN rows] -->|daily_active already there| D[done]
|
|
387
|
+
A -->|not yet| B[Touch login/info]
|
|
388
|
+
B --> C[Read the ledger again]
|
|
389
|
+
C -->|new grant| E[claimed]
|
|
390
|
+
C -->|nothing yet| F[look again in 30 min]
|
|
391
|
+
```
|
|
239
392
|
|
|
240
393
|
| Outcome | Meaning |
|
|
241
394
|
|---|---|
|
|
242
395
|
| `done` | already claimed today, with the day's total |
|
|
243
|
-
| `claimed` | this run claimed it, with the amount
|
|
244
|
-
| `inactive` | the sign-in went through but nothing was granted (from noon on,
|
|
245
|
-
| `failed` | nothing was claimed
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
396
|
+
| `claimed` | this run claimed it, with the amount; a row that was there before the touch is never counted as this run's |
|
|
397
|
+
| `inactive` | the sign-in went through but nothing was granted (from noon on, checked once more 30 minutes later before it stands for the day) |
|
|
398
|
+
| `failed` | nothing was claimed, including "not yet time" (see below) |
|
|
399
|
+
|
|
400
|
+
When the touch went through but the ledger still shows nothing, the plugin
|
|
401
|
+
asks magpie to look again half an hour later (`until`), once. Before Beijing
|
|
402
|
+
noon magpie's own 30-minute re-ask already covers it.
|
|
403
|
+
|
|
404
|
+
A `done` or `claimed` also carries the streak: how many Beijing days in a row
|
|
405
|
+
the ledger shows a daily grant, today included. magpie prints it as
|
|
406
|
+
`3-day streak` beside the outcome. The ledger's `total` tells the plugin
|
|
407
|
+
whether one page is the whole ledger; when it is not — or is missing, or the
|
|
408
|
+
rows contradict it — and the run is still going at the oldest row, the next
|
|
409
|
+
page is read, up to eight. A run it could not follow to its end — a page that
|
|
410
|
+
fails, or one longer than those eight pages — is left unsaid rather than
|
|
411
|
+
printed short, because magpie shows any number it is given.
|
|
412
|
+
|
|
413
|
+
<details>
|
|
414
|
+
<summary><b>Endpoints and the ledger</b></summary>
|
|
415
|
+
|
|
416
|
+
Every request goes to the account's own site (`modelscope.cn` or
|
|
417
|
+
`modelscope.ai`) with `Cookie: m_session_id=ms-…`. A browser UA and a
|
|
418
|
+
`Referer` are enough; no CSRF token.
|
|
249
419
|
|
|
250
420
|
| Purpose | Path |
|
|
251
421
|
|---|---|
|
|
@@ -253,258 +423,266 @@ The endpoints it uses, all on `modelscope.cn` with `Cookie: m_session_id=ms-…`
|
|
|
253
423
|
| Balance | `GET /openapi/v1/magicubes/balance` |
|
|
254
424
|
| Ledger | `GET /openapi/v1/magicubes/transactions?page=1&page_size=50` |
|
|
255
425
|
|
|
256
|
-
`type`
|
|
257
|
-
|
|
258
|
-
`rule_key` and `model_tier
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
the server answers `page_size=200` with 50.
|
|
426
|
+
The ledger's `type` has been seen as `EARN` and `SPEND_CONFIRM`; there is also
|
|
427
|
+
an `EXPIRE`, which the plugin ignores. Rows are aggregated per day, type,
|
|
428
|
+
`rule_key` and `model_tier`, with a `count` on each, and sorted newest first.
|
|
429
|
+
The ordering is what makes the first page enough in the common case: today's
|
|
430
|
+
rows always come first. A live account shows 3–7 rows a day, so a page reaches
|
|
431
|
+
back about a fortnight. A single day with more than 50 rows is followed onto
|
|
432
|
+
the next page (up to `DAY_PAGES` extra pages), and a walk that cannot finish is
|
|
433
|
+
answered with the numbers it saw and marked `未读完`. Asking for a bigger page
|
|
434
|
+
doesn't help: the server answers `page_size=200` with 50.
|
|
435
|
+
|
|
436
|
+
</details>
|
|
265
437
|
|
|
266
438
|
### When it claims: the check-in window
|
|
267
439
|
|
|
268
|
-
The claim
|
|
269
|
-
**08:30–10:00 Beijing time
|
|
270
|
-
|
|
271
|
-
the
|
|
272
|
-
|
|
440
|
+
The claim waits until a time of day, by default an account's own minute in
|
|
441
|
+
**08:30–10:00 Beijing time**, picked from a hash of the day and the account so
|
|
442
|
+
it doesn't move between retries. The traffic looks like a person opening the
|
|
443
|
+
site in the morning, not a script at midnight. The times this plugin itself
|
|
444
|
+
names say **北京时间** — the day ModelScope grants in is Beijing's wherever
|
|
445
|
+
magpie's own clock is — while times magpie adds around them (a window's reset)
|
|
446
|
+
are in the machine's zone.
|
|
273
447
|
|
|
274
|
-
Waiting costs nothing: daily grants
|
|
275
|
-
|
|
276
|
-
the credits good until 9am tomorrow.
|
|
448
|
+
Waiting costs nothing: daily grants expire **24 hours after they land**, so
|
|
449
|
+
claiming at 9am leaves the credits good until 9am tomorrow.
|
|
277
450
|
|
|
278
|
-
Set
|
|
279
|
-
installed magpie, the data folder for a portable one)
|
|
451
|
+
Set the window in `msmoli.json` in magpie's own folder (`~/.config/magpie` for
|
|
452
|
+
an installed magpie, the data folder for a portable one). The file is read on
|
|
453
|
+
every check-in ask (and by a usage read only while the day has no credit yet
|
|
454
|
+
to show), so changes apply without restarting magpie.
|
|
280
455
|
|
|
281
456
|
```json
|
|
282
457
|
{ "checkinWindow": ["08:30", "10:00"] }
|
|
283
458
|
```
|
|
284
459
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
`
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
then every 30 minutes — always after a `failed`, and also after a
|
|
301
|
-
`done`/`inactive`/`ineligible` it hasn't seen a claim for yet, up to noon
|
|
302
|
-
Beijing (a provisional answer: the vendor's day may not have turned; from noon
|
|
303
|
-
on the plugin's own `until` buys the same re-ask when the ledger lagged). So
|
|
304
|
-
before the window's moment the plugin answers `failed` with the time it will
|
|
305
|
-
run, and sends **no request at all**. `failed` is still the one outcome that
|
|
306
|
-
guarantees a re-ask; the others only get one while the day is unproven.
|
|
460
|
+
| Value | Effect |
|
|
461
|
+
|---|---|
|
|
462
|
+
| `["08:30", "10:00"]` or `"08:30-10:00"` | claim inside that window |
|
|
463
|
+
| `false`, `"off"`, `"no"` | no window; claim whenever magpie asks |
|
|
464
|
+
| an unusable value (end before start, not a clock time) | falls back to the default |
|
|
465
|
+
| an end past **23:00** | capped at 23:00; a window entirely past it falls back to the default |
|
|
466
|
+
|
|
467
|
+
<details>
|
|
468
|
+
<summary><b>Why "failed" before the window, and why the 23:00 cap</b></summary>
|
|
469
|
+
|
|
470
|
+
magpie asks once when the Beijing day turns, then every 30 minutes: always
|
|
471
|
+
after a `failed`, and after a `done`/`inactive`/`ineligible` only until Beijing
|
|
472
|
+
noon. So before the window's moment the plugin answers `failed` with the time
|
|
473
|
+
it will run, and sends **no request at all**. `failed` is the one outcome that
|
|
474
|
+
guarantees a re-ask.
|
|
307
475
|
|
|
308
476
|
What that costs, since a plugin has no other way to defer: until the claim
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
477
|
+
runs, the card shows a red dot and magpie's "check-in failed; tries again
|
|
478
|
+
later" wording, under the line
|
|
479
|
+
`09:56 后自动领取(北京时间,magpie 每半小时问一次,无需操作)`.
|
|
480
|
+
Each such ask rewrites `plugin-checkin.json`, and `magpie accounts checkin` run
|
|
481
|
+
in that period exits non-zero. Turning the window off removes all three.
|
|
482
|
+
|
|
483
|
+
**Check in now** (Settings → Usage → Plugins) does not claim early either: the
|
|
484
|
+
plugin cannot tell a manual press from the host's half-hourly ask — both reach
|
|
485
|
+
it through the same `auth.checkin` call — so before the target minute it
|
|
486
|
+
answers the same line. After the target, the button claims like any ask.
|
|
487
|
+
|
|
488
|
+
How long it lasts depends on when magpie next asks. On a measured day the
|
|
489
|
+
target was 09:01, magpie asked at 09:00:57 (three seconds early, so `failed`),
|
|
490
|
+
and the claim landed on the next ask at 09:31. That is also why the card names
|
|
491
|
+
the time and the cadence instead of a countdown: the line stays up for half an
|
|
492
|
+
hour, so a countdown ("in about 8 minutes") would be wrong for most of that time.
|
|
493
|
+
|
|
494
|
+
The cap: magpie's asks run on a 30-minute phase of their own, so the first ask
|
|
495
|
+
after a target can be up to half an hour late, and the retry after a failure
|
|
496
|
+
half an hour after that. Both have to land on the same day, or the day's
|
|
497
|
+
credits are lost. Ending the window at 23:00 leaves every target that room.
|
|
498
|
+
|
|
499
|
+
</details>
|
|
321
500
|
|
|
322
501
|
### Upstream
|
|
323
502
|
|
|
324
503
|
magpie gives a plugin no timer, no clock, and no say in when it is called:
|
|
325
|
-
`auth.checkin(getAuth, info)` returns an outcome and the host decides when
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
[this issue](https://github.com/yetone/magpie/issues/3245) proposing timers and
|
|
330
|
-
`recheck` for plugins.
|
|
331
|
-
|
|
332
|
-
Two consequences worth knowing, both from the same missing timer: a window's
|
|
333
|
-
target is only reached by the host's own asks, so one that falls while the
|
|
334
|
-
machine is asleep is claimed on the next ask after it wakes (the day is lost
|
|
335
|
-
only if that happens past midnight); and the plugin cannot retry on its own
|
|
336
|
-
between asks — the window's cap (above) is what buys the retry.
|
|
504
|
+
`auth.checkin(getAuth, info)` returns an outcome and the host decides when to
|
|
505
|
+
ask again. Everything above works within that contract.
|
|
506
|
+
[yetone/magpie#3245](https://github.com/yetone/magpie/issues/3245) proposes
|
|
507
|
+
timers and `recheck` for plugins.
|
|
337
508
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
token under its own key — the first as `msmoli`, later ones as `msmoli#xxxxxx`
|
|
342
|
-
— and asks the check-in once per account, handing each its own token through
|
|
343
|
-
`getAuth`. So with two tokens:
|
|
344
|
-
|
|
345
|
-
- each account gets **its own target minute** that day (the hash is over the
|
|
346
|
-
day and that account's token), so the two normally claim at different
|
|
347
|
-
minutes — a collision is possible, but only costs both a shared minute;
|
|
348
|
-
- each account's check-in is recorded, retried and shown separately in
|
|
349
|
-
`plugin-checkin.json` and on the card;
|
|
350
|
-
- the name read at check-in is cached per token, so one account's nickname
|
|
351
|
-
never appears on another's row.
|
|
352
|
-
|
|
353
|
-
Add a second one the same way, then pick which to use:
|
|
354
|
-
|
|
355
|
-
```bash
|
|
356
|
-
printf 'ms-the-second-token\n' | magpie plugin login msmoli
|
|
357
|
-
magpie accounts # both rows, each with its own 魔粒 window
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
The card names an account by the name ModelScope gives it. An api-type
|
|
361
|
-
sign-in has no such name, so the check-in writes the nickname back: the same
|
|
362
|
-
sign-in entry, the token still stored once, now with `accountId` beside it.
|
|
363
|
-
|
|
364
|
-
It happens on the first check-in that actually runs in the host process, so a
|
|
365
|
-
fresh install shows `API key …a1b2` until then. An account whose check-in is
|
|
366
|
-
already settled for the day is not asked again — magpie reuses the record — so
|
|
367
|
-
the name appears with the next day's check-in rather than on upgrade.
|
|
368
|
-
|
|
369
|
-
If you would rather choose the name yourself, magpie's own alias wins over it:
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
magpie accounts alias msmoli "API key …a1b2" my-main
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
The second word is the account as the card shows it *now*: before the first
|
|
376
|
-
check-in that is the key's tail, afterwards the ModelScope nickname. An account
|
|
377
|
-
you have named is left alone — the check-in won't write its nickname over your
|
|
378
|
-
alias — so either label works, and the alias outranks the nickname from then on.
|
|
379
|
-
|
|
380
|
-
And the daily grant belongs to the ModelScope account, not to the key: the
|
|
381
|
-
claim is idempotent server-side per account per day, so a second token for the
|
|
382
|
-
same ModelScope user gets `done` rather than another 250.
|
|
509
|
+
Two consequences of the missing timer: a target that falls while the machine
|
|
510
|
+
is asleep is claimed on the first ask after it wakes (the day is lost only if
|
|
511
|
+
that is past midnight), and the plugin cannot retry on its own between asks.
|
|
383
512
|
|
|
384
513
|
## Why its own provider, with no models
|
|
385
514
|
|
|
386
|
-
The plugin registers `msmoli`
|
|
387
|
-
|
|
515
|
+
The plugin registers `msmoli` instead of riding on the built-in `modelscope`
|
|
516
|
+
preset:
|
|
388
517
|
|
|
389
|
-
1. magpie
|
|
390
|
-
|
|
391
|
-
same provider anyway — it would only sit next to the preset and invite people
|
|
392
|
-
to take one for the other.
|
|
518
|
+
1. magpie renames a plugin id that collides with a preset to `<id>-plugin`, so
|
|
519
|
+
a plugin called `modelscope` would not be the same provider anyway.
|
|
393
520
|
2. **Safety.** magpie's gateway passes over an account whose usage window reads
|
|
394
|
-
as used up. If a check-in plugin served the provider
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
So chat keeps using the `modelscope` preset with your API key in
|
|
398
|
-
`providers.json`, and this plugin only carries the sign-in its check-in runs as.
|
|
399
|
-
|
|
400
|
-
### Two ModelScope rows, and why that is fine
|
|
521
|
+
as used up. If a check-in plugin served the provider chat routes through,
|
|
522
|
+
reporting 100% would take the whole chat path out of magpie.
|
|
401
523
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
happen to share one token, and they do not fight:
|
|
524
|
+
So chat keeps using the `modelscope` preset with your API key, and this plugin
|
|
525
|
+
only carries the sign-in its check-in runs as. The Providers page shows both:
|
|
405
526
|
|
|
406
|
-
| | `
|
|
527
|
+
| | `ModelScope` (preset) | `ModelScope 魔粒` (this plugin) |
|
|
407
528
|
|---|---|---|
|
|
408
|
-
| Serves chat models | yes | no
|
|
409
|
-
|
|
|
410
|
-
| Check-in | not built in
|
|
411
|
-
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
`
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
remove the plugin's sign-in, chat is unaffected, and if you remove the preset,
|
|
430
|
-
the check-in keeps working. They fail independently.
|
|
431
|
-
|
|
432
|
-
If you sign in the international site as well, that adds one more row,
|
|
433
|
-
`msmoli-intl` (ModelScope 魔粒(国际版)). It has every property in the table
|
|
434
|
-
above — no models, its own card, its own check-in — and fails independently of
|
|
435
|
-
both the preset and the domestic plugin row.
|
|
529
|
+
| Serves chat models | yes | no; `magpie models` never lists it |
|
|
530
|
+
| Usage card | none (no balance endpoint) | the credit balance and today's spend |
|
|
531
|
+
| Check-in | not built in | once a day |
|
|
532
|
+
| Used by the gateway | yes | no, so a card at 100% can't block chat |
|
|
533
|
+
|
|
534
|
+
Its row reads 未暴露模型 / no models exposed until told it serves none on
|
|
535
|
+
purpose; `magpie provider models msmoli none` (see [Install](#install)) makes
|
|
536
|
+
it read 仅看用量 / usage only. Either way nothing is served.
|
|
537
|
+
|
|
538
|
+
Entering the same `ms-` token in both is expected: a preset keeps its key in
|
|
539
|
+
`providers.json`, a plugin in `plugin-auth.json`. They fail independently;
|
|
540
|
+
remove either and the other keeps working. International accounts are extra
|
|
541
|
+
accounts under the same `msmoli` row, not new rows.
|
|
542
|
+
|
|
543
|
+
> [!TIP]
|
|
544
|
+
> The preset's card is empty because ModelScope's inference API has no balance
|
|
545
|
+
> endpoint, so the gateway only learns the credits are gone from the vendor's
|
|
546
|
+
> `429 insufficient balance`. You can give the preset a reading: in its editor,
|
|
547
|
+
> set Balance URL `https://modelscope.cn/openapi/v1/magicubes/balance` and
|
|
548
|
+
> Balance field `data.available_balance`. `magpie quota modelscope` then shows a
|
|
549
|
+
> number.
|
|
436
550
|
|
|
437
551
|
## Privacy
|
|
438
552
|
|
|
439
|
-
`login/info`
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
553
|
+
`login/info` returns `Email` and `GitlabAccessToken` among other fields. The
|
|
554
|
+
plugin reads `NickName` / `Name` and drops the rest as soon as the response is
|
|
555
|
+
parsed. The token lives only in magpie's own `plugin-auth.json`: the plugin
|
|
556
|
+
never writes it anywhere, never logs it, and never sends it anywhere but the
|
|
557
|
+
two ModelScope sites (`modelscope.cn` and `modelscope.ai`) — the domestic one
|
|
558
|
+
first, and the international one when the domestic one refuses the token, or
|
|
559
|
+
when the sign-in is already known to belong there.
|
|
560
|
+
|
|
561
|
+
The one place a token could still be copied is the account name: a reply whose
|
|
562
|
+
`NickName` is the token, or a piece of it, or a key of a vendor's shape
|
|
563
|
+
(`sk-…`, `ghp_…`, `AKIA…`) is not written back as `accountId` and is masked
|
|
564
|
+
on the card instead. A name that happens to begin like one is refused too: `sk-`
|
|
565
|
+
is a key's front before it is a nickname. Whether a name reads as a key is the
|
|
566
|
+
card's own question, asked of the name as written, so "ms-office 2016",
|
|
567
|
+
"ms-cli 2026.11.9" and all-capitals names ("CHRISTOPHER LEE 2026") are names
|
|
568
|
+
and are written back. Three situations the mask cannot cover: the
|
|
569
|
+
first 4 and last 4 characters of the token do appear, that is the mask; a key
|
|
570
|
+
of a shape this plugin does not recognise, echoed back in a spelling it does
|
|
571
|
+
not fold (the `ms-` shapes are covered in any case and full width); and a word
|
|
572
|
+
that is already masked is not judged again, so its ellipsis is printed as the
|
|
573
|
+
mask it is.
|
|
574
|
+
|
|
575
|
+
Text from the server that carries a key cut by whitespace is masked the same
|
|
576
|
+
way as the CLI's (`ghp_ 1234 AbCd …`, `sk-a nt-a pi03 …`), via the same
|
|
577
|
+
reading: a run of words that joins back into a key. The cut it reads across
|
|
578
|
+
is a space, a no-break or ideographic space, a line break, or a middot or a
|
|
579
|
+
bullet (one or two in a row, at most one space either side) — a value copied
|
|
580
|
+
out of a web page or a Chinese IME carries these as often as plain spaces. A
|
|
581
|
+
middot is read across when a piece beside it is not a word (`A·K·I·A·…`,
|
|
582
|
+
`9f8e·7d6c·…`, the pieces of a key cut in fours), and a run of single-character
|
|
583
|
+
pieces is read whatever the separator (`A· K· I· A· …`). The card's own middot
|
|
584
|
+
sits between words on both sides ("Qwen3-Max · 调用返还") and stays as written;
|
|
585
|
+
a middot inside a key stays in its mask when the mask shows only its ends,
|
|
586
|
+
because the mask keeps everything between the first four characters and the
|
|
587
|
+
last four. A full-width hyphen
|
|
588
|
+
(`-`, which Unicode does not fold) is read as the key's own. A run that also
|
|
589
|
+
reads as an ordinary sentence is left alone.
|
|
590
|
+
|
|
591
|
+
The run readings give up on a body past 32 KiB and only the per-word pass
|
|
592
|
+
runs: a body of thousands of one-word pieces ("s k - a n t" repeated) costs
|
|
593
|
+
two orders of magnitude more reading than any message this pass exists for,
|
|
594
|
+
and a key stored inside such a body as whole words is still masked. The
|
|
595
|
+
bound exists so a hostile server's error cannot eat a hook's budget.
|
|
596
|
+
|
|
597
|
+
A piece is masked only when it carries ten characters of the key's own —
|
|
598
|
+
**letters and digits, not the hyphens or spaces the token was written in**. A
|
|
599
|
+
five-character slice of a hyphenated key (`ms-…`'s middle `c-3333`) is left as
|
|
600
|
+
it is written: no one can read a full key out of a slice that short, and
|
|
601
|
+
masking every string that shares six characters would eat ordinary words. The
|
|
602
|
+
same floor is what keeps a nickname a person actually picked ("Mison 2026")
|
|
603
|
+
from being refused as an echo.
|
|
604
|
+
|
|
605
|
+
A key quoted in a frame keeps the frame: `(ghp_…Qrst)`, `[ghp_…]`, `"ghp_…"`.
|
|
606
|
+
The mask spends four characters at each end on the key's own, so the brackets
|
|
607
|
+
are printed as written rather than eaten by it.
|
|
608
|
+
|
|
609
|
+
The plugin writes two things, both onto the sign-in it already has and through
|
|
610
|
+
the host's own `client.auth.set`: the nickname, as `accountId` (so the card
|
|
611
|
+
reads "Mison" instead of "API key …a1b2"), and, for an international account,
|
|
612
|
+
the site as `site`. It re-reads the sign-in through the host first and writes
|
|
613
|
+
onto that copy, so a key you paste or a field you add while a check-in runs is
|
|
614
|
+
not replaced by an older read. It hands back what the host gave it with those
|
|
615
|
+
fields added, so the token is still stored once. An account on the domestic
|
|
616
|
+
site carries no `site`: a probe tries that one first anyway.
|
|
617
|
+
|
|
618
|
+
It reads `providers.json` for one thing only: whether you already named the
|
|
619
|
+
account yourself. A name you chose is never overwritten. The `site` of an
|
|
620
|
+
international account is written even then — it is not a name, and it only
|
|
621
|
+
saves the probe.
|
|
455
622
|
|
|
456
623
|
## Scope and limits
|
|
457
624
|
|
|
458
|
-
|
|
459
|
-
[Purpose, responsibility, and risk](#purpose-responsibility-and-risk)
|
|
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
|
-
|
|
491
|
-
|
|
625
|
+
The terms and risks are in
|
|
626
|
+
[Purpose, responsibility, and risk](#purpose-responsibility-and-risk).
|
|
627
|
+
|
|
628
|
+
- **Requests per day.** A check-in is three requests (ledger, touch, ledger);
|
|
629
|
+
a day already claimed answers `done` in one ledger read when the nickname
|
|
630
|
+
is known, two otherwise; a ledger that hasn't caught up takes
|
|
631
|
+
four. A run longer than one ledger page adds one read per page walked back,
|
|
632
|
+
up to seven — only while the streak is still going at the oldest row. A day
|
|
633
|
+
whose own rows fill a page (a busy account) reads the next page too, up to
|
|
634
|
+
four more, for both the check-in and the card. Asks before the window send
|
|
635
|
+
nothing. A usage read is two requests and never touches the sign-in
|
|
636
|
+
endpoint, so menu-bar polling cannot turn into check-ins.
|
|
637
|
+
- **International accounts cost one extra 401 per token, per process**, and
|
|
638
|
+
that 401 is on the first read that does not already know the site: the
|
|
639
|
+
sign-in's `site` field saves it after a restart, so an international usage
|
|
640
|
+
read is three requests the first time (domestic 401, balance, ledger) and two
|
|
641
|
+
after that. A domestic account costs nothing extra. A remembered site that
|
|
642
|
+
401s drops the memory and probes again; one that times out or answers
|
|
643
|
+
nothing usable is asked no more than the other site, so a dead link costs
|
|
644
|
+
the 8-second request timeout, not the whole budget.
|
|
645
|
+
- **Time limits.** Every request gives up after 8 seconds, body included, and a
|
|
646
|
+
body is read to at most 256 KiB. A whole check-in gives up after 25 seconds
|
|
647
|
+
and a usage read after 18, because magpie runs every provider's check-ins
|
|
648
|
+
under one two-minute budget. A hook cut short answers `failed`, the host
|
|
649
|
+
retries in half an hour, and the retry reads the ledger first, so nothing is
|
|
650
|
+
lost.
|
|
651
|
+
- **What a 401 or a 403 says.** A 401 signs the account out only when it
|
|
652
|
+
reads as the site refusing the token — "unauthorized", an invalid or expired
|
|
653
|
+
token, a refusal in Chinese ("请先登录", "登录状态已失效"), an empty JSON
|
|
654
|
+
object, or a code of 401 — and never when it names the path instead ("proxy
|
|
655
|
+
authentication required", a gateway's page, a bodyless 401). A 403 is
|
|
656
|
+
reported as ModelScope's risk control only when it carries ModelScope's
|
|
657
|
+
shape; a proxy's 403 page is reported as an ordinary refusal. A sign-in
|
|
658
|
+
that was refused after the day's credits landed reports the expiry but
|
|
659
|
+
keeps the day's answer.
|
|
660
|
+
- **A body it doesn't understand fails; it never reads as zero.** A missing
|
|
661
|
+
`data`, a list where an object belongs, a missing or non-numeric
|
|
662
|
+
`available_balance`: each is a failure with its reason, because
|
|
663
|
+
"0 魔粒可用" would be a definite and false answer. Server text (a message, a
|
|
664
|
+
grant's title) has any key in it masked before it reaches the card or the log.
|
|
665
|
+
- `MAGPIE_MSMOLI_CONFIG` overrides the config file's path. It exists for the
|
|
666
|
+
test suite, and works in production too.
|
|
667
|
+
- The other 170 credits a day (liking items, posting AIGC works and models) have
|
|
668
|
+
no API and would mean driving the web UI. This plugin doesn't touch them.
|
|
492
669
|
|
|
493
670
|
## Troubleshooting
|
|
494
671
|
|
|
495
672
|
| Symptom | Fix |
|
|
496
673
|
|---|---|
|
|
497
|
-
|
|
|
498
|
-
| No
|
|
499
|
-
| `failed` with HTTP 401 |
|
|
500
|
-
| `failed` with HTTP 403 | ModelScope's risk control refused the request; the token is fine.
|
|
501
|
-
| Before the window
|
|
502
|
-
|
|
|
503
|
-
|
|
|
504
|
-
|
|
|
505
|
-
|
|
|
506
|
-
|
|
|
674
|
+
| `没有登录信息,请先登录` | `printf 'ms-token\n' \| magpie plugin login msmoli` |
|
|
675
|
+
| No credit window on the card, or no check-in | Restart magpie; the plugin host caches. |
|
|
676
|
+
| `failed` with HTTP 401 | The token was revoked, or belongs to the other site; `modelscope.cn` and `modelscope.ai` don't share tokens. The message names the site that refused (国内站 / 国际站), or 任一站点 when both did. Make a new token there and sign in again. |
|
|
677
|
+
| `failed` with HTTP 403 | ModelScope's risk control refused the request; the token is fine. A check-in is asked again in 30 minutes; a usage read shows the 403 on the card until the next poll. |
|
|
678
|
+
| Before the window: failed, with `09:56 后自动领取(北京时间,magpie 每半小时问一次,无需操作)` | Expected. See [When it claims](#when-it-claims-the-check-in-window). |
|
|
679
|
+
| Check in now before the target minute answers failed with the same line | Expected. A manual press and the host's half-hourly ask reach the plugin the same way, so only the target minute starts the claim. |
|
|
680
|
+
| Always `inactive` | The sign-in works but today's ledger shows nothing: the account may not be eligible, or the grant hasn't been written yet. Before noon magpie re-asks every 30 minutes; from noon the plugin checks once more ("半小时后再核对一次"), and only the second `inactive` stands. |
|
|
681
|
+
| Chat through the `modelscope` preset answers `429 insufficient balance` | The day's 250 credits are spent (~125 flagship calls). Not this plugin: it registers no models. See the tip under [Why its own provider](#why-its-own-provider-with-no-models). |
|
|
682
|
+
| Two accounts with the same nickname, and only one checks in | magpie before 2026-10-20 kept check-ins per display name. Update magpie (fixed in magpie-community/plugins#254). |
|
|
683
|
+
| Edits to a clone do nothing | Path plugins are referenced in place; restart magpie. |
|
|
684
|
+
| `magpie plugin list` says `msmoli` is "served by" one of two copies | It was installed two ways. Remove one with `magpie plugin rm github:MisonL/magpie-plugin-msmoli` or `magpie plugin rm <path>`. The alias is filed with the package it was set against, so you may need to set it again. |
|
|
507
685
|
|
|
508
686
|
## License
|
|
509
687
|
|
|
510
|
-
MIT
|
|
688
|
+
[MIT](LICENSE). Contributions welcome; see [CONTRIBUTING.md](CONTRIBUTING.md).
|