quota-keepalive 0.1.2 → 0.1.4

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 ADDED
@@ -0,0 +1,284 @@
1
+ <div align="center">
2
+
3
+ # quota-keepalive
4
+
5
+ Multi-provider quota check-in, real-chat keepalive, run history, and quota trends for Magpie.
6
+
7
+ [![License](https://img.shields.io/badge/License-MIT-3B82F6?style=for-the-badge)](./LICENSE)
8
+ [![npm](https://img.shields.io/npm/v/quota-keepalive?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/quota-keepalive)
9
+ [![Magpie](https://img.shields.io/badge/Magpie-Plugin-0EA5A4?style=for-the-badge)](https://github.com/sljdxde/quota-keepalive)
10
+ [![Unofficial](https://img.shields.io/badge/Status-Unofficial-F59E0B?style=for-the-badge)](#unofficial-plugin-notice)
11
+
12
+ [中文](./README.md) · English
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ quota-keepalive adds one provider-neutral page to Magpie for inspecting signed-in
19
+ providers, running check-ins, sending configurable real-chat keepalive requests,
20
+ reviewing run history, and watching quota changes.
21
+
22
+ It does not take over provider sign-in. Magpie and the installed provider plugins
23
+ keep owning credentials, model lists, quotas, and the provider's own `auth.checkin`
24
+ hook. This plugin only coordinates those capabilities and presents the results.
25
+
26
+ ## Unofficial plugin notice
27
+
28
+ > This project is not an official Magpie plugin.
29
+ >
30
+ > Check-in uses the provider plugin's public `auth.checkin` hook whenever possible.
31
+ > Real-chat keepalive is not a provider-authorized or guaranteed feature. Keepalive
32
+ > requests use the signed provider adapter on your machine and are sent directly to
33
+ > the vendor, with no third-party relay and no token export. That reduces credential
34
+ > exposure, but it does not make vendor-risk zero.
35
+ >
36
+ > Automated synthetic messages may be classified as abnormal traffic and can lead to
37
+ > rate limits, credit freezes, account restrictions, or bans. Keepalive is disabled by
38
+ > default. Enable it only if you understand the risk and accept the consequences.
39
+
40
+ ## Features
41
+
42
+ - Detect every signed-in Magpie provider, account, model, and quota window.
43
+ - Run check-in through each provider plugin's public `auth.checkin` hook.
44
+ - Respect provider-level and account-level switches; unsupported or signed-out
45
+ accounts are reported explicitly instead of being silently skipped.
46
+ - Send real-chat keepalive through each provider's signed adapter.
47
+ - Use short probes by default. Long-context probes are opt-in and respect
48
+ `inputTokens`.
49
+ - Prefer models that recently completed a real keepalive successfully, then fall
50
+ back by success rate, recency, and known failures. Provider model order is used
51
+ when there is no history.
52
+ - Search and select a model per provider. If the selected model is unavailable,
53
+ the runner falls back to another model available to that account.
54
+ - Schedule check-in, keepalive, or both from every 5 minutes to every 7 days.
55
+ - Persist the next scheduled run so an overdue task can catch up after a Magpie
56
+ restart or computer sleep.
57
+ - Show today's result, a five-day account history, run details, and quota trends.
58
+ - Keep refresh-only events out of the check-in/keepalive run list.
59
+ - Refresh quotas every minute for enabled providers, and support per-provider or
60
+ refresh-all actions.
61
+ - Keep only hashed account tags, sanitized errors, model names, and status fields in
62
+ logs. Tokens, full account identifiers, enterprise identifiers, and model replies
63
+ are never stored.
64
+
65
+ ## Install
66
+
67
+ Install from npm. This is the recommended path and supports Magpie update checks:
68
+
69
+ ```sh
70
+ magpie plugin add quota-keepalive
71
+ ```
72
+
73
+ Magpie checks installed npm/git plugins shortly after startup and then every hour.
74
+ When a newer version is available, Plugins shows an update badge and an update action.
75
+
76
+ Update every npm/git plugin:
77
+
78
+ ```sh
79
+ magpie plugin update
80
+ ```
81
+
82
+ Update only this plugin:
83
+
84
+ ```sh
85
+ magpie plugin add quota-keepalive
86
+ ```
87
+
88
+ Re-adding the same npm package replaces the old version while preserving plugin
89
+ options, provider preferences, and enabled/disabled state. Updating also unpins an
90
+ older `quota-keepalive@x.y.z` spec.
91
+
92
+ Install from GitHub without npm:
93
+
94
+ ```sh
95
+ magpie plugin add github:sljdxde/quota-keepalive
96
+ ```
97
+
98
+ Install from a local folder only for development:
99
+
100
+ ```sh
101
+ magpie plugin add "$(pwd)"
102
+ ```
103
+
104
+ A folder plugin loads the working tree directly and is not included in Magpie's
105
+ version checks or `magpie plugin update`. Switch back to npm before shipping.
106
+
107
+ After installation, open **Plugins → Quota Keepalive** in Magpie. Reopen the page
108
+ after a UI-only update; restarting all of Magpie is not required.
109
+
110
+ ## Quick start
111
+
112
+ 1. Open the **Quota Keepalive** page.
113
+ 2. Click **Detect** to load signed-in providers, accounts, models, and quotas.
114
+ 3. Keep provider and account automatic switches in the desired state.
115
+ 4. Use **Check in all** or **Send keepalive** for one manual run.
116
+ 5. Configure **Schedule** if you want automatic execution.
117
+ 6. Review **Recent runs**, account history, and quota changes.
118
+
119
+ Keepalive sends a real provider request and may consume credits. The plugin does not
120
+ enable automatic keepalive by default.
121
+
122
+ ## Page controls
123
+
124
+ - **Detect**: refresh the provider/account/model/quota snapshot.
125
+ - **Check in all**: run check-in for every eligible provider.
126
+ - **Send keepalive**: confirm and send one keepalive request to every eligible account.
127
+ - Provider switch: enable or disable all automatic tasks for that provider.
128
+ - Account **Check-in** / **Keepalive** switches: control automatic participation only;
129
+ they do not send a request immediately.
130
+ - Account **Run check-in** / **Run keepalive** buttons: execute one account now.
131
+ Manual keepalive asks for confirmation and still requires the provider to be enabled.
132
+ - **Refresh quota**: refresh all enabled providers; each provider row also has a
133
+ provider-only refresh button.
134
+ - **Save schedule**: set the shared interval and action type.
135
+ - **Short probe / Long context**: choose the request shape used by keepalive.
136
+
137
+ Saving a schedule starts the next run after the configured interval. The next run
138
+ time is persisted, and a task already due when Magpie starts is run immediately.
139
+
140
+ ## Scheduling and failures
141
+
142
+ Magpie's native daily check-in scheduler still runs independently. This plugin adds
143
+ an optional shared scheduler for more frequent check-in or keepalive.
144
+
145
+ - Default: disabled.
146
+ - Interval: 5 minutes to 7 days.
147
+ - Actions: `checkin`, `keepalive`, or `all`.
148
+ - A successful check-in or keepalive for the same account and Beijing date is
149
+ idempotent and will not be sent again.
150
+ - Keepalive retries are bounded by `maxAttempts`.
151
+ - HTTP 402/429 credit exhaustion stops automatic retry for that day.
152
+ - Manual actions can deliberately retry a failed or quota-exhausted account.
153
+ - Network errors, expired sign-in, unavailable models, empty replies, and provider
154
+ business errors are recorded with sanitized details.
155
+
156
+ ## Model availability
157
+
158
+ Keepalive does not blindly choose the cheapest or a free model:
159
+
160
+ 1. Intersect the provider's model list with the account's allowed models.
161
+ 2. Prefer a model that recently completed a real keepalive successfully.
162
+ 3. If the account has no history, prefer a model another account of the same
163
+ provider used successfully.
164
+ 4. If there is still no history, follow the provider's model order.
165
+ 5. Put known failures after unverified candidates.
166
+ 6. Fall back to the next candidate on a missing model, credit rejection, upstream
167
+ outage, or empty reply.
168
+
169
+ For WorkBuddy CN, the keepalive path follows dsh probe semantics: an HTTP 2xx means
170
+ the upstream accepted and recorded a real chat, even when a one-token response only
171
+ contains `[DONE]`, usage data, or an empty visible body. JSON business errors, HTML
172
+ login pages, and timeouts are failures.
173
+
174
+ ## Configuration
175
+
176
+ The page writes plugin-level options. The same values can be changed from the CLI:
177
+
178
+ ```sh
179
+ magpie plugin options quota-keepalive '{
180
+ "scheduleEnabled": true,
181
+ "scheduleMinutes": 60,
182
+ "scheduleAction": "all",
183
+ "modelPreferences": {
184
+ "workbuddy": "deepseek-v4.1-flash",
185
+ "codearts": "deepseek-v4.1-flash"
186
+ },
187
+ "modelCandidates": 4,
188
+ "probeMode": "short",
189
+ "probeMessage": "Magpie daily connectivity check. Reply with OK.",
190
+ "inputTokens": 25000,
191
+ "maxAttempts": 3,
192
+ "timeoutMs": 90000,
193
+ "logMaxBytes": 2097152
194
+ }'
195
+ ```
196
+
197
+ - `scheduleEnabled`: enable the plugin scheduler. Default `false`.
198
+ - `scheduleMinutes`: shared interval, default `60`, range 5 minutes to 7 days.
199
+ - `scheduleAction`: `checkin`, `keepalive`, or `all`. Default `checkin`.
200
+ - `model`: optional preferred model; it is not a hard allowlist.
201
+ - `modelPreferences`: preferred model per provider.
202
+ - `modelCandidates`: maximum fallback candidates per account, default `4`.
203
+ - `probeMode`: `short` by default; `context` sends the configured long input.
204
+ - `probeMessage`: short probe text, up to 500 characters.
205
+ - `inputTokens`: long-context input size, only used with `probeMode=context`.
206
+ - `maxAttempts`: maximum keepalive attempts per account per Beijing day.
207
+ - `timeoutMs`: overall timeout for one model request, default 90 seconds.
208
+ - `logMaxBytes`: rotate one previous log after this size, default 2 MiB.
209
+ - `statePath`, `logPath`, `snapshotPath`, `commandPath`, `schedulePath`: optional
210
+ absolute paths.
211
+
212
+ ## Logs
213
+
214
+ The default log is:
215
+
216
+ ```text
217
+ ~/.config/magpie/workbuddy-daily.log.jsonl
218
+ ```
219
+
220
+ Read it with the bundled command:
221
+
222
+ ```sh
223
+ node ./bin/daily-keepalive-log.js --limit 50
224
+ node ./bin/daily-keepalive-log.js --day 2026-10-30 --status failed
225
+ node ./bin/daily-keepalive-log.js --json
226
+ ```
227
+
228
+ Agents can use the `quota-keepalive_logs` tool. Logs contain stable hashes of account
229
+ identifiers, provider IDs, status, model names, and sanitized errors. They never
230
+ contain tokens, full account identifiers, enterprise identifiers, or model replies.
231
+
232
+ ## Security
233
+
234
+ - State and log files are created with mode `0600`.
235
+ - Writes use temporary files and atomic rename.
236
+ - `plugin-auth.json` updates use a short lock, file fingerprint checks, and conflict
237
+ retries. The plugin re-reads and merges when Magpie or another provider plugin
238
+ changed the file after it was read.
239
+ - Model replies are not written to state or logs.
240
+ - Tokens, Bearer values, API keys, and account identifiers are redacted from page
241
+ output and logs.
242
+ - Missing provider plugins, signed-out accounts, and model-less providers produce
243
+ explicit skip or failure results.
244
+
245
+ ## Development
246
+
247
+ ```sh
248
+ bun test
249
+ bun test --coverage
250
+ npm pack --dry-run
251
+ ```
252
+
253
+ Tests use mocked requests. They do not contact vendors or consume credits. The core
254
+ JavaScript modules currently have 100% line and function coverage, including
255
+ performance-boundary cases.
256
+
257
+ ## Publishing
258
+
259
+ The repository uses npm Trusted Publishing through
260
+ `.github/workflows/publish.yml`; no npm token is stored in the repository or in
261
+ GitHub Secrets.
262
+
263
+ 1. Update the version in `package.json` and commit the changes.
264
+ 2. Create a matching tag, for example `v0.1.2`.
265
+ 3. Push `main` and the tag.
266
+ 4. Publish a GitHub Release for that tag.
267
+ 5. The workflow runs tests, syntax checks, `npm pack`, and publishes through OIDC.
268
+ 6. Installed users see the update through Magpie's hourly plugin update check.
269
+
270
+ Manual npm publishing with two-factor authentication enabled requires the current
271
+ one-time code:
272
+
273
+ ```sh
274
+ npm publish --access public --otp=<current-one-time-code>
275
+ ```
276
+
277
+ Never put an OTP, npm token, or recovery code in a repository, README, or chat.
278
+
279
+ ## Links
280
+
281
+ - [GitHub](https://github.com/sljdxde/quota-keepalive)
282
+ - [npm](https://www.npmjs.com/package/quota-keepalive)
283
+ - [Releases](https://github.com/sljdxde/quota-keepalive/releases)
284
+ - [License](./LICENSE)
package/README.md CHANGED
@@ -1,8 +1,23 @@
1
+ <div align="center">
2
+
1
3
  # quota-keepalive
2
4
 
3
5
  面向 Magpie 全部已登录供应商的额度签到、真实对话保活、执行留痕和额度趋势页面。
4
- 插件不接管任何供应商的登录,也不会把页面伪装成单一供应商专属功能;登录、模型、
5
- 额度和签到 Hook 仍由各供应商插件维护,本插件负责统一检测、调度、执行和展示。
6
+
7
+ [![License](https://img.shields.io/badge/License-MIT-3B82F6?style=for-the-badge)](./LICENSE)
8
+ [![npm](https://img.shields.io/npm/v/quota-keepalive?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/quota-keepalive)
9
+ [![Magpie](https://img.shields.io/badge/Magpie-Plugin-0EA5A4?style=for-the-badge)](https://github.com/sljdxde/quota-keepalive)
10
+ [![Unofficial](https://img.shields.io/badge/Status-Unofficial-F59E0B?style=for-the-badge)](#非官方插件声明)
11
+
12
+ 中文 · [English](./README.en.md)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ quota-keepalive 不接管任何供应商的登录,也不会把页面伪装成单一供应商专属功能。
19
+ 登录、模型、额度和签到 Hook 仍由各供应商插件维护;本插件负责统一检测、调度、
20
+ 执行和展示。
6
21
 
7
22
  > **非官方插件声明**
8
23
  >
@@ -132,7 +147,7 @@ npm publish --access public --otp=<6 位动态码>
132
147
  - 账号后的“执行签到/执行保活”:只对当前账号立即执行一次;执行保活会先弹出确认框。
133
148
  - 连续切换多个开关时会合并成最后一次状态写入,并按 Magpie 的页面配置限频串行保存,
134
149
  不会因为快速连续点击触发“每秒只能写入一次”的报错。
135
- - 圆形刷新按钮:立即刷新全部启用供应商额度;供应商卡片内的刷新按钮只刷新该供应商。
150
+ - `刷新额度`按钮:立即刷新全部启用供应商额度;供应商卡片内的刷新按钮只刷新该供应商。
136
151
  - `保存计划`:设置统一执行间隔和任务类型。计划默认关闭,防止无意间消耗积分。
137
152
  - `短探活/长上下文`:短探活风险、耗时和额度消耗更低;长上下文会按 `inputTokens`
138
153
  填充请求,适合明确需要维持长上下文窗口的场景。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quota-keepalive",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Unofficial multi-provider quota inventory, check-in, keepalive scheduling, history, and quota trends for Magpie",
5
5
  "keywords": [
6
6
  "magpie",
package/ui/index.html CHANGED
@@ -53,16 +53,19 @@
53
53
  </button>
54
54
  </div>
55
55
  <button
56
- class="refresh-button"
56
+ class="button refresh-button"
57
57
  id="refresh"
58
58
  type="button"
59
- title="刷新全部启用供应商额度"
60
- aria-label="刷新全部启用供应商额度"
59
+ title="刷新全部启用供应商的实时额度"
60
+ aria-label="刷新全部启用供应商的实时额度"
61
61
  >
62
- <svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor"
63
- stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
64
- <path d="M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3"></path>
65
- </svg>
62
+ <span class="refresh-icon" aria-hidden="true">
63
+ <svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor"
64
+ stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
65
+ <path d="M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3"></path>
66
+ </svg>
67
+ </span>
68
+ <span>刷新额度</span>
66
69
  </button>
67
70
  </div>
68
71
  </header>
package/ui/style.css CHANGED
@@ -180,19 +180,29 @@ h2 { margin-bottom: 2px; font-size: 16px; letter-spacing: -.015em; }
180
180
  background: color-mix(in srgb, var(--green) 8%, var(--card));
181
181
  }
182
182
  .refresh-button {
183
- display: inline-grid;
184
- place-items: center;
185
- width: 38px;
183
+ display: inline-flex;
184
+ align-items: center;
185
+ justify-content: center;
186
+ gap: 6px;
187
+ width: auto;
186
188
  height: 38px;
187
189
  flex: none;
188
- padding: 0;
190
+ padding: 0 11px;
189
191
  border: 1px solid var(--line);
190
- border-radius: 50%;
191
- color: var(--muted);
192
+ border-radius: 9px;
193
+ color: var(--ink-2);
192
194
  background: color-mix(in srgb, var(--bg) 62%, var(--card));
195
+ font-size: 11px;
196
+ font-weight: 600;
197
+ white-space: nowrap;
193
198
  cursor: pointer;
194
199
  transition: color .15s ease, border-color .15s ease, background .15s ease, box-shadow .15s ease;
195
200
  }
201
+ .refresh-icon {
202
+ display: grid;
203
+ place-items: center;
204
+ color: var(--accent);
205
+ }
196
206
  .refresh-button:hover:not(:disabled) {
197
207
  color: var(--accent);
198
208
  border-color: color-mix(in srgb, var(--accent) 28%, var(--line));
@@ -200,8 +210,8 @@ h2 { margin-bottom: 2px; font-size: 16px; letter-spacing: -.015em; }
200
210
  box-shadow: 0 2px 8px color-mix(in srgb, var(--ink) 8%, transparent);
201
211
  }
202
212
  .refresh-button:disabled { cursor: default; opacity: .62; }
203
- .refresh-button svg > * { transform-box: view-box; transform-origin: 50% 50%; }
204
- .refresh-button.spin svg > * { animation: spin .8s linear infinite; }
213
+ .refresh-button .refresh-icon { transform-origin: 50% 50%; }
214
+ .refresh-button.spin .refresh-icon { animation: spin .8s linear infinite; }
205
215
  .provider-refresh {
206
216
  display: inline-grid;
207
217
  place-items: center;
@@ -1138,8 +1148,9 @@ h2 { margin-bottom: 2px; font-size: 16px; letter-spacing: -.015em; }
1138
1148
  @media (max-width: 760px) {
1139
1149
  .page-shell { width: min(100% - 20px, 680px); padding-top: 12px; }
1140
1150
  .hero { flex-direction: column; padding: 18px; }
1141
- .hero-tools { width: 100%; align-items: stretch; }
1142
- .actions { width: 100%; }
1151
+ .hero-tools { width: 100%; align-items: stretch; flex-wrap: wrap; }
1152
+ .actions { width: 100%; flex-basis: 100%; }
1153
+ .refresh-button { margin-left: auto; }
1143
1154
  .summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1144
1155
  .command-copy { display: grid; gap: 2px; }
1145
1156
  .command-copy span { white-space: normal; }