pi-patch-keeper 1.3.5 → 1.3.7

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.ts +26 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  # pi-patch-keeper
9
9
 
10
- > 适用 pi ≥ 0.74 · Node ≥ 20 · MIT · 无运行时依赖(仅 peer: @earendil-works/pi-coding-agent)
10
+ > 适用 pi · Node ≥ 22.19 · MIT · 无运行时依赖(仅 peer: @earendil-works/pi-coding-agent)
11
11
 
12
12
  pi 配置守卫:codemode 一致性、force 顺序、环境指纹漂移、注入手册对齐。**不含 PiDeck 与第三方源码补丁。**
13
13
 
package/index.ts CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
9
 
10
- import { existsSync, readFileSync, writeFileSync, copyFileSync, renameSync } from "node:fs";
10
+ import { existsSync, readFileSync, writeFileSync, copyFileSync, renameSync, rmSync } from "node:fs";
11
11
 
12
12
  import { createHash } from "node:crypto";
13
13
  import * as os from "node:os";
@@ -47,11 +47,33 @@ function safeRead(p: string): string | null {
47
47
  try { return readFileSync(p, "utf8"); } catch { return null; }
48
48
  }
49
49
 
50
- /** 原子写:临时文件 + rename(崩溃/被杀不留半写文件)。 */
50
+ /** 原子写:临时文件 + rename(崩溃/被杀不留半写文件)。
51
+ * Windows 上 rename 覆盖**正被打开**的文件会抛 EPERM(实测:pi 落盘 / 杀软扫描期间);
52
+ * 故对占用类错误退避重试(80ms 起,共 6 次),其它错误立即失败;仍失败清掉 tmp。 */
51
53
  function writeAtomic(file: string, data: string | Buffer): void {
52
54
  const tmp = file + ".tmp-" + process.pid;
53
- writeFileSync(tmp, data);
54
- renameSync(tmp, file);
55
+ try {
56
+ writeFileSync(tmp, data);
57
+ let lastErr: unknown = null;
58
+ for (let attempt = 0; attempt < 6; attempt++) {
59
+ try { renameSync(tmp, file); lastErr = null; break; }
60
+ catch (err) {
61
+ lastErr = err;
62
+ const code = (err as NodeJS.ErrnoException).code;
63
+ if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") break;
64
+ sleepMs(80 * (attempt + 1));
65
+ }
66
+ }
67
+ if (lastErr) throw lastErr;
68
+ } catch (err) {
69
+ try { rmSync(tmp, { force: true }); } catch { /* ignore */ }
70
+ throw err;
71
+ }
72
+ }
73
+
74
+ /** 同步等待(writeAtomic 重试用):同步上下文没有 await。 */
75
+ function sleepMs(ms: number): void {
76
+ try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); } catch { /* ignore */ }
55
77
  }
56
78
 
57
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-patch-keeper",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "pi config guards: codemode single-source check, force-order guard (auto-fix), settings fingerprint drift, injected-manual alignment. (PiDeck / third-party source patches are a local-only module, not bundled.)",
5
5
  "type": "module",
6
6
  "license": "MIT",