magpie-plugin-msmoli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MisonL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,433 @@
1
- # Temporary Holding Version
1
+ # magpie-plugin-msmoli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A [magpie](https://github.com/yetone/magpie) plugin that claims ModelScope's
4
+ (魔搭) daily **魔粒** credits for you — one check-in a day instead of opening the
5
+ site — and shows the balance and today's spend on the account's usage card.
6
+
7
+ ## What 魔粒 is
8
+
9
+ ModelScope's API-Inference is free, paid in 魔粒:
10
+
11
+ | Grant | Amount | Expires |
12
+ |---|---|---|
13
+ | Sign-in, per day | 200 | 24 hours |
14
+ | Aliyun account bound, per day | 50 | 24 hours |
15
+ | Email / profile / homepage completed | 50 each | 90 days, once |
16
+
17
+ Daily grants are **24 hours only**. They do not accumulate: what you don't
18
+ spend today is gone. A flagship model costs 2 魔粒 per call (mainstream 1,
19
+ lightweight 0.5), so the daily 250 is roughly 125 calls.
20
+
21
+ ## Install
22
+
23
+ Needs magpie itself (<https://usemagpie.ai>). On macOS, use `magpie --version` to check it's on PATH; if not, use the app's full path:
24
+ `/Applications/magpie.app/Contents/MacOS/magpie`.
25
+
26
+ ### Install via GitHub URL (no clone, auto-updates)
27
+
28
+ This installs into magpie's own plugin folder (`~/.config/magpie/plugins` for a regular install, or `data/plugins` for the portable version), so it updates with `magpie plugin update`.
29
+
30
+ ```bash
31
+ magpie plugin add github:MisonL/magpie-plugin-msmoli
32
+ printf 'ms-your-token\n' | magpie plugin login msmoli
33
+ ```
34
+
35
+ The token is stored in `plugin-auth.json`; one login works regardless of the form you used to install.
36
+
37
+ ### Install from a clone
38
+
39
+ Keeps your own copy, which is what you want if you plan to change it. magpie
40
+ references the folder where it lies rather than copying it, so it loads only
41
+ while that folder is there, and an update means pulling and adding it again.
42
+
43
+ ```bash
44
+ git clone https://github.com/MisonL/magpie-plugin-msmoli.git
45
+ cd magpie-plugin-msmoli
46
+ magpie plugin add "$PWD"
47
+ printf 'ms-your-token\n' | magpie plugin login msmoli
48
+ ```
49
+
50
+ Either form ends the same way: a `msmoli` row under Settings → Usage → Plugins.
51
+ The sign-in is kept per provider id (`msmoli`) rather than per folder, so
52
+ switching between the two forms (or moving the clone) keeps the token — no
53
+ second login.
54
+
55
+ ### Already have ModelScope set up in magpie? Sync that key to the plugin
56
+
57
+ If you added ModelScope earlier just for chat, its key is already in
58
+ `providers.json`. This package ships a small CLI that logs the same token into
59
+ the plugin, so you don't have to find it again.
60
+
61
+ Installed from GitHub, magpie keeps its own copy in its plugin folder:
62
+
63
+ ```bash
64
+ node ~/.config/magpie/plugins/node_modules/magpie-plugin-msmoli/bin/sync.mjs --status # see both sides
65
+ node ~/.config/magpie/plugins/node_modules/magpie-plugin-msmoli/bin/sync.mjs --to-plugin # sync
66
+ ```
67
+
68
+ Installed from a clone, run the file in the clone — magpie references that
69
+ folder and copies nothing (`./bin/sync.mjs`; on Windows or WSL, where the
70
+ file may not carry an executable bit, run it as `node bin/sync.mjs`). Portable
71
+ installs keep the same layout under their `data` folder; if detection misses,
72
+ `--dir <magpie's config folder>` and `--magpie <path to the magpie binary>` say
73
+ where things are: for a portable install that is its `data` folder, not the
74
+ folder the executable sits in. `--magpie` takes a path, absolute or relative —
75
+ a bare `magpie` is refused, because that one is resolved through `PATH` and the
76
+ first `magpie` on it would be handed the token. `npx magpie-plugin-msmoli` will
77
+ work once the package is on npm — npm 12 blocks `npx github:…` by default, so
78
+ don't start from that.
79
+
80
+ `--dir` says where to *read*; magpie's CLI has no such flag, so a write always
81
+ lands in the binary's own folder. Given both and they disagree, the tool stops
82
+ before writing anything rather than reading one magpie and writing another.
83
+ "Disagree" means the folders are different places, not that the strings differ:
84
+ a shell completes a directory with a trailing slash, `/private/tmp` and `/tmp`
85
+ are one folder on a Mac, and `data/.` is `data` — each names the binary's own
86
+ folder and is allowed.
87
+
88
+ It reads magpie's config but never writes it: the writes are magpie's own
89
+ (`magpie plugin login`, `magpie plugin logout`, `magpie provider key`), and the
90
+ token is masked in everything it prints, failures included. When the plugin
91
+ already uses the same key it changes nothing; when the two keys differ it
92
+ refuses, because a second `login` would add a second account — pass `--force`
93
+ to replace a single existing account. `--to-preset` goes the other way (needs
94
+ `--force`: that key is what chat uses). If more than one provider could be the
95
+ built-in side (say, a relay of the same host), the tool refuses to guess and
96
+ `--from <id>` picks one; `--dry-run` prints the commands without running them.
97
+ A value flag given twice (`--dir a --dir b`) is refused, not silently last-wins,
98
+ and `--dir=<folder>` is read as well. `--from` and a stray argument are echoed
99
+ back masked when the value looks like a key — a token pasted into the wrong
100
+ position is never printed whole, whatever its shape. That covers the shapes
101
+ `ms-…` does not: a GitHub or Slack token, an AWS secret (which carries `/`), a
102
+ UUID, a hex digest, a base64 body — long, or short enough that only its trailing
103
+ `=` (or a `+` beside a case mix) gives it away. The judgement runs
104
+ word by word,
105
+ so a key with a space or a newline pasted onto it — the shape a copy out of a
106
+ terminal or a chat window leaves — or quoted inside a line is still the key it
107
+ is. A word that is one key is masked whole, ends and all, including one split with
108
+ separators — a hex digest with dashes, a key written as `AbCdEfGh.IjKlMnOp…` —
109
+ and an AWS secret, whose two parts of 12+ characters with mixed case mark it. An
110
+ invisible character dropped inside a key (a zero-width space or `\x01`, the
111
+ shape a copy out of a chat window leaves) does not save it either: the
112
+ control/format characters sitting between key-alphabet characters are removed
113
+ before masking, so the two halves are rejoined into the one key they are —
114
+ each half alone falls under the length floor and would have leaked. A
115
+ word that starts like a path (`/`, `~`, `.`, a drive letter, a UNC root) keeps
116
+ its shape, and only the key-like run inside it is masked, so `--dir
117
+ /tmp/ghp_16C7…` still names the folder it read; a key pasted in *behind* a path
118
+ prefix (`~/keys/sk-ant-…`) reads as part of that path, which is what keeping a
119
+ path's shape costs. The folder is reported
120
+ the same way on every line that names it, and magpie's own output goes through
121
+ the same door — a key of another provider in the child's error message is
122
+ masked there too, not only the `ms-` shape. A provider id and an account name
123
+ are read out of magpie's own files and go through the same door: a key pasted
124
+ into either field is masked on every line the tool names it.
125
+
126
+ Turn the check-in on: Settings → Usage → Plugins → the `msmoli` row, or in
127
+ `settings.json` (in magpie's own folder, `~/.config/magpie` for an installed
128
+ magpie):
129
+
130
+ ```json
131
+ {
132
+ "pluginCheckins": {
133
+ "msmoli": true
134
+ }
135
+ }
136
+ ```
137
+
138
+ The token comes from <https://modelscope.cn/my/myaccesstoken>. The same `ms-`
139
+ token is both the inference `Authorization: Bearer` and the web session cookie,
140
+ so nothing else needs configuring.
141
+
142
+ From then on magpie claims it once a Beijing day per signed-in account, retries
143
+ later that day if it fails, and reports the outcome on the account's usage card:
144
+
145
+ ```
146
+ ● msmoli API key …a1b2 · 魔搭社区 魔粒 1% (今日用 2 / 领 250 · 余额 249.5) ↻14h24m tomorrow 09:31 249.5 魔粒可用 left
147
+ ```
148
+
149
+ ## How the check-in works
150
+
151
+ ModelScope publishes no check-in endpoint. What it does have is a side effect:
152
+ `GET /api/v1/users/login/info` counts as a sign-in, and the first one of the
153
+ day grants `daily_active` 200 and `aliyun_bindlogin` 50. Repeat it the same day
154
+ and nothing more is granted.
155
+
156
+ A hook that just calls it can't tell "claimed now" from "claimed hours ago", so
157
+ this one reads the day's ledger around it:
158
+
159
+ 1. read today's `EARN` records — is `daily_active` there?
160
+ 2. touch `login/info`
161
+ 3. read the ledger again
162
+
163
+ A `claimed` needs a grant the ledger did not already show around the touch. If
164
+ the touch went through and the ledger still shows nothing, the plugin asks
165
+ magpie to look again half an hour later (`until`), once — from Beijing noon on
166
+ magpie would otherwise settle `inactive` for the day, and a grant written a few
167
+ seconds late was recorded as "the account may not be in the campaign". Before
168
+ noon magpie's own provisional re-ask (every 30 minutes) already covers it.
169
+
170
+ Four outcomes follow:
171
+
172
+ | Outcome | Meaning |
173
+ |---|---|
174
+ | `done` | already claimed today, with the day's total |
175
+ | `claimed` | this run claimed it, with the amount — a grant the ledger did not already show, so a row that was there before the touch is never counted as this run's |
176
+ | `inactive` | the sign-in went through but nothing was granted (from noon on, asked once more in 30 minutes before it stands for the day) |
177
+ | `failed` | nothing was claimed — including "not yet time", explained below |
178
+
179
+ The endpoints it uses, all on `modelscope.cn` with `Cookie: m_session_id=ms-…`
180
+ (a browser UA and a `Referer` are enough — no CSRF token):
181
+
182
+ | Purpose | Path |
183
+ |---|---|
184
+ | Sign-in / check-in trigger | `GET /api/v1/users/login/info` |
185
+ | Balance | `GET /openapi/v1/magicubes/balance` |
186
+ | Ledger | `GET /openapi/v1/magicubes/transactions?page=1&page_size=50` |
187
+
188
+ `type` in the ledger has been seen as `EARN` and `SPEND_CONFIRM`; there is
189
+ also an `EXPIRE`, which this plugin ignores. Rows are aggregated per day, type,
190
+ `rule_key` and `model_tier` — a day's spending shows one row per model tier it
191
+ used — with a `count` on each, and the list is sorted newest first. The ordering
192
+ is what makes the single page of 50 enough: today's rows come before any older
193
+ one. Measured on a live account that is 3–7 rows a day, so a page reaches back
194
+ about a fortnight rather than 50 days; what the read would not survive is a
195
+ single day with more than 50 rows. Asking for a larger page does not help —
196
+ the server answers `page_size=200` with 50.
197
+
198
+ ### When it claims: the check-in window
199
+
200
+ The claim is held back until a time of day, by default somewhere in
201
+ **08:30–10:00 Beijing time** — an account's own minute in that window,
202
+ picked from a hash of the day and the account, so it doesn't move between
203
+ the host's retries. It makes the traffic look like a person opening the
204
+ site in the morning rather than at the stroke of midnight.
205
+
206
+ Waiting costs nothing: daily grants **expire 24 hours after they land**
207
+ (a grant at 15:35 expires at 15:35 the next day), so claiming at 9am leaves
208
+ the credits good until 9am tomorrow.
209
+
210
+ Set it in `msmoli.json` in magpie's own folder (`~/.config/magpie` for an
211
+ installed magpie, the data folder for a portable one):
212
+
213
+ ```json
214
+ { "checkinWindow": ["08:30", "10:00"] }
215
+ ```
216
+
217
+ One string works the same way: `{ "checkinWindow": "08:30-10:00" }`.
218
+
219
+ `false` turns the window off (claim whenever magpie asks); so does `"off"`.
220
+ An unusable value — `08:30`–`08:00`, a date that isn't a clock time — falls
221
+ back to the default rather than never claiming. An end past **23:00** is
222
+ capped there: the host does not ask at the target's minute — its own look runs
223
+ every 30 minutes on a phase of its own, so the first ask past a target can come
224
+ up to half an hour late, and the retry after a failure half an hour after that.
225
+ A target needs both to still land on the same day. Capping at 23:00 leaves any
226
+ target that room; later would leave some with one ask, or none. A window that sits entirely past 23:00 falls back to the
227
+ default window too, since there would be nowhere left to put the target. The
228
+ file is read on every ask, so a change takes effect without restarting
229
+ magpie.
230
+
231
+ How it fits magpie's schedule: magpie asks once when the Beijing day turns,
232
+ then every 30 minutes — always after a `failed`, and also after a
233
+ `done`/`inactive`/`ineligible` it hasn't seen a claim for yet, up to noon
234
+ Beijing (a provisional answer: the vendor's day may not have turned; from noon
235
+ on the plugin's own `until` buys the same re-ask when the ledger lagged). So
236
+ before the window's moment the plugin answers `failed` with the time it will
237
+ run, and sends **no request at all**. `failed` is still the one outcome that
238
+ guarantees a re-ask; the others only get one while the day is unproven.
239
+
240
+ What that costs, since a plugin has no other way to defer: until the claim
241
+ actually runs the account card shows a red dot and magpie's own "check-in
242
+ failed; magpie tries again later" wording, each of those asks rewrites
243
+ magpie's `plugin-checkin.json`, and `magpie accounts checkin` run in that
244
+ period exits non-zero. Turning the window off removes all three.
245
+
246
+ How long that is depends on when magpie next asks, not on the target alone.
247
+ Measured on a real day: the target was 09:01, magpie asked at 09:00:57 (three
248
+ seconds early, so `failed`), and the claim landed on the next ask at 09:31 —
249
+ about 9.5 hours from midnight, rather than the 8.5 the window's 08:30 start
250
+ alone suggests. That is also why the card names the cadence instead of a
251
+ countdown: the host keeps the line until its next ask, half an hour later, so
252
+ "约 8 分钟后" would be wrong for most of the time it is on screen.
253
+
254
+ ### Upstream
255
+
256
+ magpie gives a plugin no timer, no clock, and no say in when it is called:
257
+ `auth.checkin(getAuth, info)` returns an outcome and the host decides when
258
+ to ask again. Everything above is worked around within that contract, and
259
+ the awkward parts (proving "not yet" as a failure, whole-day granularity for
260
+ the account card) want a line in
261
+ [this issue](https://github.com/yetone/magpie/issues/3245) proposing timers and
262
+ `recheck` for plugins.
263
+
264
+ Two consequences worth knowing, both from the same missing timer: a window's
265
+ target is only reached by the host's own asks, so one that falls while the
266
+ machine is asleep is claimed on the next ask after it wakes (the day is lost
267
+ only if that happens past midnight); and the plugin cannot retry on its own
268
+ between asks — the window's cap (above) is what buys the retry.
269
+
270
+ ### More than one account
271
+
272
+ The plugin is written per account, not per token. magpie keeps each signed-in
273
+ token under its own key — the first as `msmoli`, later ones as `msmoli#xxxxxx`
274
+ — and asks the check-in once per account, handing each its own token through
275
+ `getAuth`. So with two tokens:
276
+
277
+ - each account gets **its own target minute** that day (the hash is over the
278
+ day and that account's token), so the two normally claim at different
279
+ minutes — a collision is possible, but only costs both a shared minute;
280
+ - each account's check-in is recorded, retried and shown separately in
281
+ `plugin-checkin.json` and on the card;
282
+ - the name read at check-in is cached per token, so one account's nickname
283
+ never appears on another's row.
284
+
285
+ Add a second one the same way, then pick which to use:
286
+
287
+ ```bash
288
+ printf 'ms-the-second-token\n' | magpie plugin login msmoli
289
+ magpie accounts # both rows, each with its own 魔粒 window
290
+ ```
291
+
292
+ The card names an account by the name ModelScope gives it. An api-type
293
+ sign-in has no such name, so the check-in writes the nickname back: the same
294
+ sign-in entry, the token still stored once, now with `accountId` beside it.
295
+
296
+ It happens on the first check-in that actually runs in the host process, so a
297
+ fresh install shows `API key …a1b2` until then. An account whose check-in is
298
+ already settled for the day is not asked again — magpie reuses the record — so
299
+ the name appears with the next day's check-in rather than on upgrade.
300
+
301
+ If you would rather choose the name yourself, magpie's own alias wins over it:
302
+
303
+ ```bash
304
+ magpie accounts alias msmoli "API key …a1b2" my-main
305
+ ```
306
+
307
+ The second word is the account as the card shows it *now*: before the first
308
+ check-in that is the key's tail, afterwards the ModelScope nickname. An account
309
+ you have named is left alone — the check-in won't write its nickname over your
310
+ alias — so either label works, and the alias outranks the nickname from then on.
311
+
312
+ And the daily grant belongs to the ModelScope account, not to the key: the
313
+ claim is idempotent server-side per account per day, so a second token for the
314
+ same ModelScope user gets `done` rather than another 250.
315
+
316
+ ## Why its own provider, with no models
317
+
318
+ The plugin registers `msmoli` rather than the built-in `modelscope` preset, for
319
+ two reasons:
320
+
321
+ 1. magpie namespaces a plugin id that collides with a preset into
322
+ `<id>-plugin`, so a plugin that named itself `modelscope` would not be the
323
+ same provider anyway — it would only sit next to the preset and invite people
324
+ to take one for the other.
325
+ 2. **Safety.** magpie's gateway passes over an account whose usage window reads
326
+ as used up. If a check-in plugin served the provider that inference actually
327
+ routes through, reporting 100% would take the whole chat path out of magpie.
328
+
329
+ So chat keeps using the `modelscope` preset with your API key in
330
+ `providers.json`, and this plugin only carries the sign-in its check-in runs as.
331
+
332
+ ### Two ModelScope rows, and why that is fine
333
+
334
+ The providers page then shows two: `ModelScope` (the preset, with your models)
335
+ and `ModelScope 魔粒` (this plugin, with none). They are separate providers that
336
+ happen to share one token, and they do not fight:
337
+
338
+ | | `modelscope` preset | `msmoli` (this plugin) |
339
+ |---|---|---|
340
+ | Serves chat models | yes | no — `magpie models` never lists it |
341
+ | Its usage card | none (the preset has no balance endpoint) | 魔粒 balance and today's spend |
342
+ | Check-in | not built in for ModelScope | this plugin, once a day |
343
+ | Reachable by the gateway | yes | no, so a card reading 100% cannot route around it |
344
+
345
+ The token being in both places is how magpie is built: a preset keeps its key
346
+ in `providers.json`, a plugin keeps its sign-in in `plugin-auth.json`. Entering
347
+ the same `ms-` token in both is expected. Nothing reads the plugin's usage card
348
+ to decide where chat goes — that is the point of the separate id.
349
+
350
+ The preset's empty card has one consequence worth knowing. ModelScope's
351
+ inference API has no balance endpoint, so magpie cannot see that the key's 魔粒
352
+ are gone; the gateway learns it from the vendor's own `429 insufficient balance`
353
+ and then rests the key for 30 minutes. Until then each request spends one failed
354
+ try on it first. The balance is on the account, and `modelscope.cn` does answer
355
+ it, so the preset can be told: in its editor, Balance URL
356
+ `https://modelscope.cn/openapi/v1/magicubes/balance` and Balance field
357
+ `data.available_balance` (the same token answers it — `magpie quota modelscope`
358
+ then reads a number instead of `no balance endpoint known for it`).
359
+
360
+ The one real overlap to know about: **both rows cost nothing extra**, but if you
361
+ remove the plugin's sign-in, chat is unaffected, and if you remove the preset,
362
+ the check-in keeps working. They fail independently.
363
+
364
+ ## Privacy
365
+
366
+ `login/info` responds with `Email` and `GitlabAccessToken` among other fields.
367
+ This plugin reads `NickName` / `Name` and nothing else; the rest is dropped as
368
+ soon as the response is parsed. The token lives only in magpie's own
369
+ `plugin-auth.json` — the plugin never writes it anywhere, never logs it, never
370
+ sends it anywhere but `modelscope.cn`.
371
+
372
+ The one thing it writes is that nickname, as `accountId` on the sign-in it
373
+ already has, so magpie's card can name the account. It goes through the host's
374
+ own `client.auth.set`, which replaces the sign-in entry — so the plugin hands
375
+ back what the host gave it, with the name added, and the token is still
376
+ stored once.
377
+
378
+ It also reads `providers.json`, magpie's own list of providers, for one thing:
379
+ whether you already named this account yourself. If you have, it writes
380
+ nothing — your name stays, and the nickname is not put over it.
381
+
382
+ ## Scope and limits
383
+
384
+ - One account's own daily grant, idempotent: a check-in reads the ledger, touches
385
+ the sign-in endpoint, and reads the ledger again — three requests. A day already
386
+ claimed (a retry, a later ask) answers `done` in two; a touch the ledger has not
387
+ caught up with reads it once more, four. Once a day.
388
+ Asks that arrive before the window open send nothing at all.
389
+ The usage read makes two requests and never touches the sign-in endpoint, so the
390
+ menu bar's polling cannot turn into check-ins.
391
+ - Every request gives up after 8 seconds, and a response body is read to at most
392
+ 256 KiB; neither can hold the host or grow without bound. The timeout covers the
393
+ body as well as the request: a proxy that stops mid-reply is cut at the same
394
+ 8 seconds, and says so rather than passing the runtime's own word for it
395
+ through (`terminated` on Node, a sentence about `verbose: true` on Bun). A whole
396
+ check-in gives up after 25 seconds and a whole usage read after 18, because
397
+ magpie runs every provider's check-ins under one two-minute budget and asks
398
+ accounts one at a time — a hook cut short answers `failed`, which the host
399
+ retries in half an hour, and that retry reads the ledger before claiming, so
400
+ nothing is lost.
401
+ - **A body it does not understand fails; it never reads as zero.** No `data`, a
402
+ `data` that is a list rather than an object, a balance with no
403
+ `available_balance` or one that is not a number — each is a failure with the
404
+ reason, because "0 魔粒可用" is a definite answer that would be false. The same
405
+ goes for the server's own text: a message or a grant's title carrying a key —
406
+ this account's token, or one of another vendor's, which this plugin never held
407
+ and therefore cannot compare against — has it masked before the text reaches
408
+ the card or magpie's log. What counts as a key there is the same rule `--from`
409
+ uses, so a ledger title or a server message of ordinary words is untouched.
410
+ - `MAGPIE_MSMOLI_CONFIG` overrides the config file's path. It exists so the test
411
+ suite never touches a real file; it works in production too.
412
+ - The other 170 魔粒 a day come from liking 10 items (20), posting AIGC works
413
+ (100) and AIGC models (50). None of those have an API, and automating them
414
+ means driving the web UI, so this plugin does not touch them.
415
+
416
+ ## Troubleshooting
417
+
418
+ | Symptom | Fix |
419
+ |---|---|
420
+ | `not signed in` | `printf 'ms-token\n' \| magpie plugin login msmoli` |
421
+ | No 魔粒 window on the card, or no check-in | restart magpie (the plugin host caches) |
422
+ | `failed` with HTTP 401 | the token was revoked — make a new one and sign in again |
423
+ | `failed` with HTTP 403 | ModelScope's risk control refused the request; the token is fine. It retries in 30 minutes |
424
+ | Before the window the card reads failed, with a line naming the claim time: `09:56 后自动领取(magpie 每半小时问一次,无需操作)` | expected — the card leads with when it will claim; see "When it claims" above |
425
+ | Always `inactive` | the sign-in works but nothing shows in today's ledger: the account may not be eligible, or the grant hasn't been written yet (a claim made elsewhere shows as `done`). Before Beijing noon magpie asks again every 30 minutes; from noon on the plugin asks once more itself, half an hour later ("半小时后再核对一次"), and only the second `inactive` stands for the day |
426
+ | Chat through the `modelscope` preset answers `429 insufficient balance` in the Routing log | the account's daily 250 魔粒 are spent — a flagship call costs 2, so ~125 calls run the day dry and the vendor refuses the rest. Not this plugin: it registers no models. If you want magpie to *see* the balance on the preset's card too, give the preset the same reading — Balance URL `https://modelscope.cn/openapi/v1/magicubes/balance`, Balance field `data.available_balance` in its editor (the same token answers it; read `1.50` on 2026-10-28). Whether the gateway then holds a drained key before spending a request on it is not verified — the safe path is still the 429's own 30-minute rest |
427
+ | Two accounts of one ModelScope nickname, and only one of them checks in | magpie before 2026-10-20 kept a plugin's check-in per display name rather than per sign-in, so namesakes collapsed into one. Update magpie: the fix landed upstream on 2026-10-20 (magpie-community/plugins#254) |
428
+ | Edits to this plugin do nothing | path plugins are referenced in place; restart magpie |
429
+ | Installed both ways by accident, and `magpie plugin list` says `msmoli` is "served by" one of them | that is magpie keeping two copies of one provider id. Remove one: `magpie plugin rm github:MisonL/magpie-plugin-msmoli` or `magpie plugin rm <path>`. magpie files your alias with the package it was set against, so removing a copy can take the display name with it — it comes back if you add that package again, or you set the alias once more |
430
+
431
+ ## License
432
+
433
+ MIT