pi-patch-keeper 1.3.4 → 1.3.6

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 +36 -32
  2. package/index.ts +26 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -69,28 +69,30 @@ pi install npm:pi-patch-keeper
69
69
 
70
70
  ## 原理(专业)
71
71
 
72
- ```mermaid
73
- flowchart TD
74
- A[factory:读 settings.json 建指纹] --> B[session_start]
75
- A --> C[before_agent_start]
76
- B -->|指纹变化| D[通知:手册 / 结论可能过期]
77
- B -->|force 顺序错| E[备份 + 重排 packages]
78
- C -->|注入版本 vs 磁盘不一致| F[通知 + nextTurn 尾部消息<br/>以磁盘为准]
72
+ ```text
73
+ factory ─ 读 settings.json 建指纹
74
+ ├─▶ session_start
75
+ └─▶ before_agent_start
76
+
77
+ session_start
78
+ ├─ 指纹变化 ──▶ 通知:手册 / 结论可能过期
79
+ └─ force 顺序错 ──▶ 备份 + 重排 packages
80
+
81
+ before_agent_start
82
+ └─ 注入版本 vs 磁盘不一致 ──▶ 通知 + nextTurn 尾部消息(以磁盘为准)
79
83
  ```
80
84
 
81
85
  ## 原理(小白)
82
86
 
83
87
  **像出门前的门卫**,检查三件事:开关是否一致、排队顺序对不对、手里的说明书是不是最新版;能顺手修的直接修,修不了就提醒你。
84
88
 
85
- ```mermaid
86
- flowchart TD
87
- A[启动] --> B[门卫三项检查]
88
- B --> C{开关一致?}
89
- B --> D{顺序对不对?}
90
- B --> E{说明书最新?}
91
- C -->|否| F[提醒]
92
- D -->|否| G[直接调好]
93
- E -->|否| H[提醒:以手上为准]
89
+ ```text
90
+ 启动
91
+ ↓
92
+ 门卫三项检查
93
+ ├─ 开关一致?否 ──▶ 提醒
94
+ ├─ 顺序对不对?否 ──▶ 直接调好
95
+ └─ 说明书最新?否 ──▶ 提醒:以手上为准
94
96
  ```
95
97
 
96
98
  ## License
@@ -164,28 +166,30 @@ Injection mismatch: injected v22.172 != disk v22.173 (disk wins) -> the injected
164
166
 
165
167
  ## How it works (technical)
166
168
 
167
- ```mermaid
168
- flowchart TD
169
- A[factory: build fingerprint from settings.json] --> B[session_start]
170
- A --> C[before_agent_start]
171
- B -->|fingerprint changed| D[notify: manuals may be stale]
172
- B -->|force order wrong| E[backup + reorder packages]
173
- C -->|injected vs disk mismatch| F[notify + nextTurn message<br/>trust disk]
169
+ ```text
170
+ factory: build fingerprint from settings.json
171
+ ├─▶ session_start
172
+ └─▶ before_agent_start
173
+
174
+ session_start
175
+ ├─ fingerprint changed ──▶ notify: manuals may be stale
176
+ └─ force order wrong ──▶ backup + reorder packages
177
+
178
+ before_agent_start
179
+ └─ injected vs disk mismatch ──▶ notify + nextTurn message (trust disk)
174
180
  ```
175
181
 
176
182
  ## How it works (plain words)
177
183
 
178
184
  **Like a doorman** before you leave: checks three things — switches agree, queue order is right, the manual in hand is the latest version; fixes what it can and tells you the rest.
179
185
 
180
- ```mermaid
181
- flowchart TD
182
- A[startup] --> B[doorman: three checks]
183
- B --> C{switches agree?}
184
- B --> D{order right?}
185
- B --> E{manual latest?}
186
- C -->|no| F[notify]
187
- D -->|no| G[fix directly]
188
- E -->|no| H[notify: trust the copy in hand]
186
+ ```text
187
+ startup
188
+ ↓
189
+ doorman: three checks
190
+ ├─ switches agree? no ──▶ notify
191
+ ├─ order right? no ──▶ fix directly
192
+ └─ manual latest? no ──▶ notify: trust the copy in hand
189
193
  ```
190
194
  ## License
191
195
 
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.4",
3
+ "version": "1.3.6",
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",