magicrew-auth 0.3.30 → 0.3.31

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.3.31 — 2026-10-29
4
+
5
+ **自动更新加全局锁(0.3.30 补丁)**
6
+
7
+ 0.3.30 的 `magicrew` 与 `magicrew-intl` 两个 provider 会在同一时刻各自跑
8
+ `config`,各调一次 `kick`,可能同时 spawn 两个 `magpie plugin update`。
9
+ 0.3.31 加全局锁 `~/.cache/magpie/magicrew-auto-update.lock`(10 分钟过期):
10
+ 一个更新进程在跑时,另一个 provider 直接跳过。
11
+
12
+ 验证:`tests/auto-maintenance.mjs` 21 条断言(新增「已有未过期全局锁 → locked」
13
+ 与「进程退出后释放锁」)。
14
+
3
15
  ## 0.3.30 — 2026-10-29
4
16
 
5
17
  **默认开启自动签到 · 插件自身自动更新**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magicrew-auth",
3
- "version": "0.3.30",
3
+ "version": "0.3.31",
4
4
  "description": "超级麦吉 / MagiCrew for magpie and OpenCode — one package, two providers: letsmagic.cn (国内) and magicrew.ai (international). Password or web-JWT sign-in, the account's own live model list, points balance, newcomer rewards, invite codes.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
@@ -28,7 +28,7 @@
28
28
  //
29
29
  // 两个动作都是**尽力而为**:失败不抛、不拖慢签到/额度/登录。
30
30
 
31
- import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, copyFileSync } from "node:fs"
31
+ import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, copyFileSync, unlinkSync } from "node:fs"
32
32
  import { homedir } from "node:os"
33
33
  import { join, dirname, basename } from "node:path"
34
34
  import { execFileSync, spawn } from "node:child_process"
@@ -37,6 +37,7 @@ const PKG_NAME = "Magicrew"
37
37
  const AUTO_DIR = () => join(homedir(), ".config", "magpie")
38
38
  const CACHE_DIR = () => join(homedir(), ".cache", "magpie")
39
39
  const THROTTLE_MS = 6 * 3600_000 // 同一次更新失败后,6 小时内不再试
40
+ const LOCK_MS = 10 * 60_000 // 一个更新进程最多占用 10 分钟
40
41
 
41
42
  function readJson(path, fallback) {
42
43
  try {
@@ -114,6 +115,33 @@ function writeAutoState(site, state, { cacheDir = CACHE_DIR() } = {}) {
114
115
  } catch {}
115
116
  }
116
117
 
118
+ // 两个 provider(magicrew / magicrew-intl)会在同一时刻各自跑 config,
119
+ // 各调一次 kick。全局锁保证「magpie plugin update」只有一个进程在跑。
120
+ function updateLockPath(cacheDir) {
121
+ return join(cacheDir, "magicrew-auto-update.lock")
122
+ }
123
+
124
+ function lockHeld(cacheDir, now) {
125
+ try {
126
+ const v = JSON.parse(readFileSync(updateLockPath(cacheDir), "utf8"))
127
+ return now - Number(v?.at || 0) < LOCK_MS
128
+ } catch {
129
+ return false
130
+ }
131
+ }
132
+
133
+ function takeLock(cacheDir, now) {
134
+ try {
135
+ writeJsonAtomic(updateLockPath(cacheDir), { pid: process.pid, at: now })
136
+ } catch {}
137
+ }
138
+
139
+ function releaseLock(cacheDir) {
140
+ try {
141
+ unlinkSync(updateLockPath(cacheDir))
142
+ } catch {}
143
+ }
144
+
117
145
  /**
118
146
  * 找本机 magpie 可执行文件。顺序:
119
147
  * 1. 环境变量 MAGPIE_EXE(最可靠,给测试和自定义安装用)
@@ -206,6 +234,8 @@ export async function kick(c, opts = {}) {
206
234
  return { skipped: "no-magpie-exe", waiting }
207
235
  }
208
236
  if (opts.dryRun) return { wouldRun: exe, waiting }
237
+ if (lockHeld(cacheDir, now)) return { skipped: "locked", waiting }
238
+ takeLock(cacheDir, now)
209
239
 
210
240
  let child
211
241
  try {
@@ -216,10 +246,12 @@ export async function kick(c, opts = {}) {
216
246
  env: process.env,
217
247
  })
218
248
  } catch (e) {
249
+ releaseLock(cacheDir)
219
250
  writeAutoState(c.id, { ...state, lastAttempt: now, latest: waiting.latest, reason: String(e?.message ?? e) }, { cacheDir })
220
251
  return { skipped: "spawn-failed", error: String(e?.message ?? e) }
221
252
  }
222
253
  child.on?.("exit", (code) => {
254
+ releaseLock(cacheDir)
223
255
  if (code === 0) {
224
256
  noteUpdated(pkg, waiting.version, waiting.latest, { dir })
225
257
  writeAutoState(c.id, {