@cerbur/clutch-dsh-worktree 0.1.7 → 0.1.8

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 (102) hide show
  1. package/README.md +28 -8
  2. package/README.zh.md +21 -3
  3. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  4. package/lib/client/WorktreeSurface.js +24 -8
  5. package/lib/client/WorktreeSurface.js.map +1 -1
  6. package/lib/client/locales.d.ts +12 -0
  7. package/lib/client/locales.d.ts.map +1 -1
  8. package/lib/client/locales.js +12 -0
  9. package/lib/client/locales.js.map +1 -1
  10. package/lib/client/worktree-context.js +1 -1
  11. package/lib/client/worktree-context.js.map +1 -1
  12. package/lib/client/worktree-error-copy.d.ts.map +1 -1
  13. package/lib/client/worktree-error-copy.js +8 -0
  14. package/lib/client/worktree-error-copy.js.map +1 -1
  15. package/lib/client/worktree-session.d.ts.map +1 -1
  16. package/lib/client/worktree-session.js +3 -1
  17. package/lib/client/worktree-session.js.map +1 -1
  18. package/lib/client/worktree-surface-rows.d.ts.map +1 -1
  19. package/lib/client/worktree-surface-rows.js +7 -0
  20. package/lib/client/worktree-surface-rows.js.map +1 -1
  21. package/lib/client.js +47 -11
  22. package/lib/client.js.map +1 -1
  23. package/lib/contract/index.d.ts +6 -2
  24. package/lib/contract/index.d.ts.map +1 -1
  25. package/lib/contract/index.js +4 -0
  26. package/lib/contract/index.js.map +1 -1
  27. package/lib/host/dsh-read-adapter.d.ts +1 -0
  28. package/lib/host/dsh-read-adapter.d.ts.map +1 -1
  29. package/lib/host/dsh-read-adapter.js +6 -0
  30. package/lib/host/dsh-read-adapter.js.map +1 -1
  31. package/lib/host/service.d.ts +2 -0
  32. package/lib/host/service.d.ts.map +1 -1
  33. package/lib/host/service.js +6 -1
  34. package/lib/host/service.js.map +1 -1
  35. package/lib/host/worktree-permission-manager.d.ts.map +1 -1
  36. package/lib/host/worktree-permission-manager.js +3 -1
  37. package/lib/host/worktree-permission-manager.js.map +1 -1
  38. package/lib/index.d.ts +2 -1
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +1 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/manage/manager-context.d.ts +2 -0
  43. package/lib/manage/manager-context.d.ts.map +1 -1
  44. package/lib/manage/manager-worktrees.d.ts +5 -0
  45. package/lib/manage/manager-worktrees.d.ts.map +1 -1
  46. package/lib/manage/manager-worktrees.js +125 -37
  47. package/lib/manage/manager-worktrees.js.map +1 -1
  48. package/lib/manage/manager.d.ts +20 -0
  49. package/lib/manage/manager.d.ts.map +1 -1
  50. package/lib/manage/manager.js +91 -13
  51. package/lib/manage/manager.js.map +1 -1
  52. package/lib/manage/types.d.ts +9 -1
  53. package/lib/manage/types.d.ts.map +1 -1
  54. package/lib/provider/git.d.ts +37 -9
  55. package/lib/provider/git.d.ts.map +1 -1
  56. package/lib/provider/git.js +162 -61
  57. package/lib/provider/git.js.map +1 -1
  58. package/lib/provider/index.d.ts +5 -1
  59. package/lib/provider/index.d.ts.map +1 -1
  60. package/lib/provider/index.js +4 -0
  61. package/lib/provider/index.js.map +1 -1
  62. package/lib/provider/mutation-lock.d.ts +37 -0
  63. package/lib/provider/mutation-lock.d.ts.map +1 -0
  64. package/lib/provider/mutation-lock.js +270 -0
  65. package/lib/provider/mutation-lock.js.map +1 -0
  66. package/lib/provider/mutation-token.d.ts +8 -0
  67. package/lib/provider/mutation-token.d.ts.map +1 -0
  68. package/lib/provider/mutation-token.js +20 -0
  69. package/lib/provider/mutation-token.js.map +1 -0
  70. package/lib/provider/repository-fingerprint.d.ts +8 -0
  71. package/lib/provider/repository-fingerprint.d.ts.map +1 -0
  72. package/lib/provider/repository-fingerprint.js +13 -0
  73. package/lib/provider/repository-fingerprint.js.map +1 -0
  74. package/lib/provider/sidecar-persistence.d.ts +7 -3
  75. package/lib/provider/sidecar-persistence.d.ts.map +1 -1
  76. package/lib/provider/sidecar-persistence.js +118 -44
  77. package/lib/provider/sidecar-persistence.js.map +1 -1
  78. package/lib/provider/sidecar-schema.d.ts +1 -1
  79. package/lib/provider/sidecar-schema.d.ts.map +1 -1
  80. package/lib/provider/sidecar-schema.js +140 -8
  81. package/lib/provider/sidecar-schema.js.map +1 -1
  82. package/lib/provider/sidecar.d.ts +3 -1
  83. package/lib/provider/sidecar.d.ts.map +1 -1
  84. package/lib/provider/sidecar.js +6 -1
  85. package/lib/provider/sidecar.js.map +1 -1
  86. package/lib/provider/subprocess.d.ts +37 -0
  87. package/lib/provider/subprocess.d.ts.map +1 -0
  88. package/lib/provider/subprocess.js +319 -0
  89. package/lib/provider/subprocess.js.map +1 -0
  90. package/lib/provider/transaction.d.ts +65 -0
  91. package/lib/provider/transaction.d.ts.map +1 -0
  92. package/lib/provider/transaction.js +1011 -0
  93. package/lib/provider/transaction.js.map +1 -0
  94. package/lib/provider/types.d.ts +98 -7
  95. package/lib/provider/types.d.ts.map +1 -1
  96. package/lib/provider/types.js +1 -1
  97. package/lib/provider/types.js.map +1 -1
  98. package/lib/typert.host.js +29 -25
  99. package/lib/typert.remote-client.d.ts +2 -2
  100. package/lib/typert.remote-client.d.ts.map +1 -1
  101. package/lib/typert.remote-client.js +29 -25
  102. package/package.json +3 -1
@@ -0,0 +1,270 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { lstat, mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { setTimeout as delay } from 'node:timers/promises';
6
+ import { providerError } from './types.js';
7
+ const DEFAULT_ACQUISITION_TIMEOUT_MS = 10_000;
8
+ const DEFAULT_LEASE_MS = 30_000;
9
+ const DEFAULT_HEARTBEAT_MS = 5_000;
10
+ function isAlreadyExists(error) {
11
+ return error.code === 'EEXIST';
12
+ }
13
+ function isMissing(error) {
14
+ return error.code === 'ENOENT';
15
+ }
16
+ function processIsAlive(pid) {
17
+ try {
18
+ process.kill(pid, 0);
19
+ return true;
20
+ }
21
+ catch (error) {
22
+ return error.code === 'EPERM';
23
+ }
24
+ }
25
+ function parseOwner(value) {
26
+ try {
27
+ const parsed = JSON.parse(value);
28
+ if (parsed.version !== 1 ||
29
+ typeof parsed.token !== 'string' ||
30
+ typeof parsed.pid !== 'number' ||
31
+ typeof parsed.hostname !== 'string' ||
32
+ typeof parsed.startedAt !== 'string' ||
33
+ typeof parsed.heartbeatAt !== 'string') {
34
+ return undefined;
35
+ }
36
+ return parsed;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ /**
43
+ * A small cross-process lease lock backed by an atomically-created directory.
44
+ * An owned directory is never reclaimed based on age alone: its stale owner
45
+ * must also be provably dead on the same host. An ownerless directory is only
46
+ * possible during interrupted lock acquisition, so it can be reclaimed after
47
+ * one lease interval without stealing an alive Git operation.
48
+ */
49
+ export class CrossProcessMutationLock {
50
+ lockRoot;
51
+ acquisitionTimeoutMs;
52
+ leaseMs;
53
+ heartbeatMs;
54
+ constructor({ lockRoot, acquisitionTimeoutMs = DEFAULT_ACQUISITION_TIMEOUT_MS, leaseMs = DEFAULT_LEASE_MS, heartbeatMs = DEFAULT_HEARTBEAT_MS, }) {
55
+ if (!path.isAbsolute(lockRoot)) {
56
+ throw providerError('SIDECAR_UNAVAILABLE', 'Mutation lock root must be an absolute path', { lockRoot });
57
+ }
58
+ if (acquisitionTimeoutMs < 0 || leaseMs <= 0 || heartbeatMs <= 0 || heartbeatMs >= leaseMs) {
59
+ throw providerError('SIDECAR_UNAVAILABLE', 'Invalid mutation lock timing configuration', {
60
+ acquisitionTimeoutMs,
61
+ leaseMs,
62
+ heartbeatMs,
63
+ });
64
+ }
65
+ this.lockRoot = path.resolve(lockRoot);
66
+ this.acquisitionTimeoutMs = acquisitionTimeoutMs;
67
+ this.leaseMs = leaseMs;
68
+ this.heartbeatMs = heartbeatMs;
69
+ }
70
+ getLockPath(key) {
71
+ const keyHash = createHash('sha256').update(key).digest('hex');
72
+ return path.join(this.lockRoot, `${keyHash}.lock`);
73
+ }
74
+ async run(key, operation) {
75
+ const handle = await this.acquire(key);
76
+ try {
77
+ return await operation(handle);
78
+ }
79
+ finally {
80
+ await handle.release();
81
+ }
82
+ }
83
+ async acquire(key) {
84
+ try {
85
+ if ((await lstat(this.lockRoot)).isSymbolicLink()) {
86
+ throw providerError('SIDECAR_UNAVAILABLE', `Mutation lock root must not be a symlink: ${this.lockRoot}`, {
87
+ path: this.lockRoot,
88
+ });
89
+ }
90
+ }
91
+ catch (error) {
92
+ if (!isMissing(error))
93
+ throw error;
94
+ }
95
+ await mkdir(this.lockRoot, { recursive: true, mode: 0o700 });
96
+ const keyHash = createHash('sha256').update(key).digest('hex');
97
+ const lockPath = this.getLockPath(key);
98
+ let owner = {
99
+ version: 1,
100
+ token: randomUUID(),
101
+ pid: process.pid,
102
+ hostname: os.hostname(),
103
+ startedAt: new Date().toISOString(),
104
+ heartbeatAt: new Date().toISOString(),
105
+ };
106
+ const startedAt = Date.now();
107
+ let lost = false;
108
+ let released = false;
109
+ let heartbeatTimer;
110
+ const writeOwner = async () => {
111
+ const ownerPath = path.join(lockPath, 'owner.json');
112
+ const temporaryPath = `${ownerPath}.${owner.token}.${randomUUID()}.tmp`;
113
+ try {
114
+ await writeFile(temporaryPath, `${JSON.stringify(owner)}\n`, {
115
+ encoding: 'utf8',
116
+ mode: 0o600,
117
+ flag: 'wx',
118
+ });
119
+ await rename(temporaryPath, ownerPath);
120
+ }
121
+ finally {
122
+ try {
123
+ await rm(temporaryPath, { force: true });
124
+ }
125
+ catch {
126
+ // Best-effort cleanup must not hide the owner publication result.
127
+ }
128
+ }
129
+ };
130
+ const heartbeat = async () => {
131
+ if (released)
132
+ return;
133
+ try {
134
+ owner = { ...owner, heartbeatAt: new Date().toISOString() };
135
+ await writeOwner();
136
+ }
137
+ catch {
138
+ lost = true;
139
+ }
140
+ };
141
+ const release = async () => {
142
+ if (released)
143
+ return;
144
+ released = true;
145
+ if (heartbeatTimer !== undefined)
146
+ clearInterval(heartbeatTimer);
147
+ try {
148
+ const current = parseOwner(await readFile(path.join(lockPath, 'owner.json'), 'utf8'));
149
+ if (current?.token === owner.token)
150
+ await rm(lockPath, { recursive: true, force: true });
151
+ }
152
+ catch (error) {
153
+ if (!isMissing(error))
154
+ void error;
155
+ }
156
+ };
157
+ while (true) {
158
+ try {
159
+ await mkdir(lockPath, { recursive: false, mode: 0o700 });
160
+ try {
161
+ await writeOwner();
162
+ }
163
+ catch (error) {
164
+ await rm(lockPath, { recursive: true, force: true });
165
+ throw error;
166
+ }
167
+ heartbeatTimer = setInterval(() => {
168
+ void heartbeat();
169
+ }, this.heartbeatMs);
170
+ heartbeatTimer.unref?.();
171
+ return {
172
+ keyHash,
173
+ assertHeld: () => {
174
+ if (lost) {
175
+ throw providerError('WORKTREE_RECOVERY_REQUIRED', 'Mutation lock lease was lost', { keyHash });
176
+ }
177
+ },
178
+ release,
179
+ };
180
+ }
181
+ catch (error) {
182
+ if (!isAlreadyExists(error))
183
+ throw error;
184
+ try {
185
+ if ((await lstat(lockPath)).isSymbolicLink()) {
186
+ throw providerError('SIDECAR_UNAVAILABLE', `Mutation lock path must not be a symlink: ${lockPath}`, {
187
+ path: lockPath,
188
+ });
189
+ }
190
+ }
191
+ catch (symlinkError) {
192
+ if (!isMissing(symlinkError))
193
+ throw symlinkError;
194
+ }
195
+ await this.reclaimIfDead(lockPath);
196
+ if (Date.now() - startedAt >= this.acquisitionTimeoutMs) {
197
+ throw providerError('WORKTREE_MUTATION_BUSY', 'Another process is mutating this Worktree state', {
198
+ keyHash,
199
+ timeoutMs: this.acquisitionTimeoutMs,
200
+ });
201
+ }
202
+ await delay(Math.min(50, Math.max(5, this.heartbeatMs / 10)));
203
+ }
204
+ }
205
+ }
206
+ async reclaimIfDead(lockPath) {
207
+ let owner;
208
+ let ownerFileMissing = false;
209
+ try {
210
+ owner = parseOwner(await readFile(path.join(lockPath, 'owner.json'), 'utf8'));
211
+ }
212
+ catch (error) {
213
+ if (isMissing(error))
214
+ ownerFileMissing = true;
215
+ else
216
+ return;
217
+ }
218
+ if (ownerFileMissing) {
219
+ let lockStats;
220
+ try {
221
+ lockStats = await stat(lockPath);
222
+ }
223
+ catch (error) {
224
+ if (isMissing(error))
225
+ return;
226
+ return;
227
+ }
228
+ if (Date.now() - lockStats.mtimeMs < this.leaseMs)
229
+ return;
230
+ await this.abandon(lockPath);
231
+ return;
232
+ }
233
+ if (!owner || owner.hostname !== os.hostname())
234
+ return;
235
+ const heartbeatAt = Date.parse(owner.heartbeatAt);
236
+ if (!Number.isFinite(heartbeatAt) || Date.now() - heartbeatAt < this.leaseMs)
237
+ return;
238
+ if (processIsAlive(owner.pid))
239
+ return;
240
+ // Rename is the ownership handoff: a contender cannot remove a newly
241
+ // acquired lock after another contender has already moved this directory.
242
+ await this.abandon(lockPath);
243
+ }
244
+ async abandon(lockPath) {
245
+ try {
246
+ if ((await lstat(lockPath)).isSymbolicLink())
247
+ return;
248
+ }
249
+ catch (error) {
250
+ if (isMissing(error))
251
+ return;
252
+ return;
253
+ }
254
+ const abandonedPath = `${lockPath}.abandoned-${randomUUID()}`;
255
+ try {
256
+ await rename(lockPath, abandonedPath);
257
+ await rm(abandonedPath, { recursive: true, force: true });
258
+ }
259
+ catch (error) {
260
+ if (!isMissing(error))
261
+ void error;
262
+ }
263
+ }
264
+ }
265
+ export const MUTATION_LOCK_DEFAULTS = Object.freeze({
266
+ acquisitionTimeoutMs: DEFAULT_ACQUISITION_TIMEOUT_MS,
267
+ leaseMs: DEFAULT_LEASE_MS,
268
+ heartbeatMs: DEFAULT_HEARTBEAT_MS,
269
+ });
270
+ //# sourceMappingURL=mutation-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-lock.js","sourceRoot":"","sources":["../../src/provider/mutation-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { WorktreeRecord } from '../contract/index.js';
2
+ import type { SidecarSnapshot } from './types.js';
3
+ /**
4
+ * Create an opaque, deterministic token for one sidecar revision and Worktree
5
+ * record. It is a concurrency witness, not an authorization credential.
6
+ */
7
+ export declare function createWorktreeMutationToken(snapshot: Pick<SidecarSnapshot, 'schemaVersion' | 'workspaceId' | 'revision'>, record: Pick<WorktreeRecord, 'worktreeId' | 'workspaceId' | 'absolutePath' | 'branch' | 'source' | 'status'>): string;
8
+ //# sourceMappingURL=mutation-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-token.d.ts","sourceRoot":"","sources":["../../src/provider/mutation-token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,aAAa,GAAG,UAAU,CAAC,EAC7E,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC,GAC3G,MAAM,CAaR"}
@@ -0,0 +1,20 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Create an opaque, deterministic token for one sidecar revision and Worktree
4
+ * record. It is a concurrency witness, not an authorization credential.
5
+ */
6
+ export function createWorktreeMutationToken(snapshot, record) {
7
+ const payload = JSON.stringify([
8
+ snapshot.schemaVersion,
9
+ snapshot.workspaceId,
10
+ snapshot.revision,
11
+ record.worktreeId,
12
+ record.workspaceId,
13
+ record.absolutePath,
14
+ record.branch,
15
+ record.source,
16
+ record.status,
17
+ ]);
18
+ return `v1-${createHash('sha256').update(payload).digest('base64url')}`;
19
+ }
20
+ //# sourceMappingURL=mutation-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-token.js","sourceRoot":"","sources":["../../src/provider/mutation-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKzC;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAA6E,EAC7E,MAA4G;IAE5G,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7B,QAAQ,CAAC,aAAa;QACtB,QAAQ,CAAC,WAAW;QACpB,QAAQ,CAAC,QAAQ;QACjB,MAAM,CAAC,UAAU;QACjB,MAAM,CAAC,WAAW;QAClB,MAAM,CAAC,YAAY;QACnB,MAAM,CAAC,MAAM;QACb,MAAM,CAAC,MAAM;QACb,MAAM,CAAC,MAAM;KACd,CAAC,CAAC;IACH,OAAO,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { RepositoryIdentity } from './types.js';
2
+ /**
3
+ * Persist only a stable, non-reversible repository witness. The absolute
4
+ * repository paths remain in memory and in a pending operation only while a
5
+ * transaction is recoverable; stable sidecar state never copies projectRoot.
6
+ */
7
+ export declare function createRepositoryFingerprint(identity: Pick<RepositoryIdentity, 'commonDirectory'>): string;
8
+ //# sourceMappingURL=repository-fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-fingerprint.d.ts","sourceRoot":"","sources":["../../src/provider/repository-fingerprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,GACpD,MAAM,CAIR"}
@@ -0,0 +1,13 @@
1
+ import { createHash } from 'node:crypto';
2
+ import path from 'node:path';
3
+ /**
4
+ * Persist only a stable, non-reversible repository witness. The absolute
5
+ * repository paths remain in memory and in a pending operation only while a
6
+ * transaction is recoverable; stable sidecar state never copies projectRoot.
7
+ */
8
+ export function createRepositoryFingerprint(identity) {
9
+ const normalized = path.normalize(path.resolve(identity.commonDirectory));
10
+ const key = process.platform === 'win32' ? normalized.toLowerCase() : normalized;
11
+ return `v1-${createHash('sha256').update(key).digest('hex')}`;
12
+ }
13
+ //# sourceMappingURL=repository-fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-fingerprint.js","sourceRoot":"","sources":["../../src/provider/repository-fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAAqD;IAErD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACjF,OAAO,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAChE,CAAC"}
@@ -1,17 +1,21 @@
1
- import { type SidecarMutation, type SidecarSnapshot } from './types.js';
2
- /** Owns sidecar file paths, per-Workspace serialization, and atomic disk I/O. */
1
+ import { type LockedSidecarStore, type SidecarMutation, type SidecarSnapshot } from './types.js';
2
+ /** Owns sidecar file paths, cross-process serialization, schema migration, and atomic disk I/O. */
3
3
  export declare class SidecarPersistence {
4
4
  readonly pluginRoot: string;
5
5
  readonly worktreeRoot: string;
6
6
  readonly workspaceRoot: string;
7
+ readonly lockRoot: string;
7
8
  private readonly dshHome;
8
- private readonly mutex;
9
+ private readonly mutationLock;
9
10
  constructor({ dshHome }: {
10
11
  readonly dshHome: string;
11
12
  });
12
13
  getShardPath(workspaceId: string): string;
13
14
  read(workspaceId: string): Promise<SidecarSnapshot>;
15
+ runExclusive<T>(workspaceId: string, operation: (locked: LockedSidecarStore) => Promise<T>): Promise<T>;
16
+ private readSnapshot;
14
17
  mutate<T>(workspaceId: string, mutation: (snapshot: SidecarSnapshot) => SidecarMutation<T> | Promise<SidecarMutation<T>>): Promise<T>;
18
+ private mutateLocked;
15
19
  private write;
16
20
  private assertStorageBoundary;
17
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-persistence.d.ts","sourceRoot":"","sources":["../../src/provider/sidecar-persistence.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AAgCpB,iFAAiF;AACjF,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAE9B,EAAE,OAAO,EAAE,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAUrD,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAInC,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA+BnD,MAAM,CAAC,CAAC,EACZ,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC;YAaC,KAAK;YAkCL,qBAAqB;CAuBpC"}
1
+ {"version":3,"file":"sidecar-persistence.d.ts","sourceRoot":"","sources":["../../src/provider/sidecar-persistence.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AAQpB,mGAAmG;AACnG,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2B;gBAE5C,EAAE,OAAO,EAAE,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAYrD,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAInC,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAInD,YAAY,CAAC,CAAC,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,GACpD,OAAO,CAAC,CAAC,CAAC;YAoBC,YAAY;IA8CpB,MAAM,CAAC,CAAC,EACZ,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC;YAqBC,YAAY;YAqBZ,KAAK;YAoCL,qBAAqB;CAwBpC"}
@@ -1,44 +1,17 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { lstat, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
2
+ import { lstat, mkdir, open, readFile, rename, unlink, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import { CrossProcessMutationLock } from './mutation-lock.js';
4
5
  import { WorktreeProviderError, providerError, } from './types.js';
5
6
  import { corrupt, emptySnapshot, validateSidecarSnapshot } from './sidecar-schema.js';
6
- /*
7
- * 互斥范围是“同一 repository 实例 + 同一 Workspace key”。它让该实例内的 mutation
8
- * 依次读取最新快照,但不会假装协调其他进程或其他 repository 实例;异常也会在 finally
9
- * 中释放队列,避免后续操作永久阻塞。
10
- *
11
- * Mutual exclusion is scoped to one repository instance and one Workspace key.
12
- * It makes mutations in that instance read the latest snapshot serially, but
13
- * does not pretend to coordinate another process or another repository instance.
14
- */
15
- class KeyedMutex {
16
- tails = new Map();
17
- async run(key, operation) {
18
- const previous = this.tails.get(key) ?? Promise.resolve();
19
- let release;
20
- const current = new Promise((resolve) => {
21
- release = resolve;
22
- });
23
- this.tails.set(key, current);
24
- await previous;
25
- try {
26
- return await operation();
27
- }
28
- finally {
29
- release();
30
- if (this.tails.get(key) === current)
31
- this.tails.delete(key);
32
- }
33
- }
34
- }
35
- /** Owns sidecar file paths, per-Workspace serialization, and atomic disk I/O. */
7
+ /** Owns sidecar file paths, cross-process serialization, schema migration, and atomic disk I/O. */
36
8
  export class SidecarPersistence {
37
9
  pluginRoot;
38
10
  worktreeRoot;
39
11
  workspaceRoot;
12
+ lockRoot;
40
13
  dshHome;
41
- mutex = new KeyedMutex();
14
+ mutationLock;
42
15
  constructor({ dshHome }) {
43
16
  if (!path.isAbsolute(dshHome)) {
44
17
  throw providerError('SIDECAR_UNAVAILABLE', 'DSH Home must be an absolute path', { dshHome });
@@ -47,11 +20,35 @@ export class SidecarPersistence {
47
20
  this.pluginRoot = path.join(this.dshHome, 'clutch-dsh-worktree');
48
21
  this.worktreeRoot = path.join(this.pluginRoot, 'worktree');
49
22
  this.workspaceRoot = path.join(this.pluginRoot, 'workspaces');
23
+ this.lockRoot = path.join(this.pluginRoot, 'locks');
24
+ this.mutationLock = new CrossProcessMutationLock({ lockRoot: this.lockRoot });
50
25
  }
51
26
  getShardPath(workspaceId) {
52
27
  return path.join(this.workspaceRoot, `${encodeURIComponent(workspaceId)}.json`);
53
28
  }
54
29
  async read(workspaceId) {
30
+ return (await this.readSnapshot(workspaceId)).snapshot;
31
+ }
32
+ async runExclusive(workspaceId, operation) {
33
+ // Validate the lock/storage boundary before creating a lock directory. A
34
+ // symlinked lock root must not become an alternate write location.
35
+ await this.assertStorageBoundary();
36
+ return this.mutationLock.run(`workspace:${workspaceId}`, async (lock) => {
37
+ lock.assertHeld();
38
+ const locked = {
39
+ read: async () => {
40
+ lock.assertHeld();
41
+ return (await this.readSnapshot(workspaceId)).snapshot;
42
+ },
43
+ mutate: async (mutation) => {
44
+ lock.assertHeld();
45
+ return this.mutateLocked(workspaceId, mutation, lock);
46
+ },
47
+ };
48
+ return operation(locked);
49
+ });
50
+ }
51
+ async readSnapshot(workspaceId) {
55
52
  await this.assertStorageBoundary();
56
53
  const shardPath = this.getShardPath(workspaceId);
57
54
  let text;
@@ -60,8 +57,9 @@ export class SidecarPersistence {
60
57
  }
61
58
  catch (error) {
62
59
  // ENOENT is an uninitialized state; other filesystem failures remain unavailable.
63
- if (error.code === 'ENOENT')
64
- return emptySnapshot(workspaceId);
60
+ if (error.code === 'ENOENT') {
61
+ return { snapshot: emptySnapshot(workspaceId), needsMigration: false };
62
+ }
65
63
  throw providerError('SIDECAR_UNAVAILABLE', `Unable to read sidecar shard: ${shardPath}`, {
66
64
  path: shardPath,
67
65
  cause: String(error),
@@ -81,21 +79,51 @@ export class SidecarPersistence {
81
79
  if (snapshot.workspaceId !== workspaceId) {
82
80
  throw corrupt(shardPath, 'sidecar shard Workspace identity does not match its filename');
83
81
  }
84
- return snapshot;
82
+ const parsedObject = parsed !== null && typeof parsed === 'object'
83
+ ? parsed
84
+ : undefined;
85
+ const legacyRepositoryField = parsedObject?.repository !== undefined ||
86
+ (parsedObject?.pendingOperation !== null &&
87
+ typeof parsedObject?.pendingOperation === 'object' &&
88
+ parsedObject.pendingOperation !== undefined &&
89
+ 'repository' in parsedObject.pendingOperation);
90
+ return {
91
+ snapshot,
92
+ needsMigration: (parsed !== null && typeof parsed === 'object' &&
93
+ parsed.schemaVersion !== snapshot.schemaVersion) ||
94
+ legacyRepositoryField,
95
+ };
85
96
  }
86
97
  async mutate(workspaceId, mutation) {
87
- return this.mutex.run(workspaceId, async () => {
88
- const current = await this.read(workspaceId);
89
- const result = await mutation(current);
90
- const next = validateSidecarSnapshot(result.snapshot, this.getShardPath(workspaceId), this.worktreeRoot);
91
- if (next.workspaceId !== workspaceId) {
92
- throw corrupt(this.getShardPath(workspaceId), 'sidecar mutation changed the Workspace identity');
98
+ return this.runExclusive(workspaceId, async (locked) => {
99
+ const current = await locked.read();
100
+ if (current.pendingOperation !== undefined || (current.recoveryIssues?.length ?? 0) > 0) {
101
+ const operationId = current.pendingOperation?.id;
102
+ throw providerError('WORKTREE_RECOVERY_REQUIRED', operationId
103
+ ? `Workspace has a pending Worktree operation: ${operationId}`
104
+ : `Workspace has unresolved Worktree recovery issues: ${workspaceId}`, {
105
+ workspaceId,
106
+ ...(operationId ? { operationId } : {}),
107
+ ...(current.recoveryIssues ? { recoveryCodes: current.recoveryIssues.map((issue) => issue.code) } : {}),
108
+ });
93
109
  }
94
- if (result.changed !== false)
95
- await this.write(next);
96
- return result.result;
110
+ return locked.mutate(mutation);
97
111
  });
98
112
  }
113
+ async mutateLocked(workspaceId, mutation, lock) {
114
+ const current = await this.readSnapshot(workspaceId);
115
+ const result = await mutation(current.snapshot);
116
+ lock.assertHeld();
117
+ const next = stripLegacyRepositoryFields(validateSidecarSnapshot(result.snapshot, this.getShardPath(workspaceId), this.worktreeRoot));
118
+ if (next.workspaceId !== workspaceId) {
119
+ throw corrupt(this.getShardPath(workspaceId), 'sidecar mutation changed the Workspace identity');
120
+ }
121
+ const shouldWrite = result.changed !== false || current.needsMigration;
122
+ if (shouldWrite) {
123
+ await this.write({ ...next, revision: nextRevision(current.snapshot.revision, result.changed !== false) });
124
+ }
125
+ return result.result;
126
+ }
99
127
  async write(snapshot) {
100
128
  await this.assertStorageBoundary();
101
129
  const shardPath = this.getShardPath(snapshot.workspaceId);
@@ -109,8 +137,10 @@ export class SidecarPersistence {
109
137
  encoding: 'utf8',
110
138
  mode: 0o600,
111
139
  });
140
+ await syncFile(temporaryPath);
112
141
  // Readers observe either the complete old file or complete new file.
113
142
  await rename(temporaryPath, shardPath);
143
+ await syncDirectory(this.workspaceRoot);
114
144
  }
115
145
  catch (error) {
116
146
  throw providerError('SIDECAR_UNAVAILABLE', `Unable to atomically write sidecar shard: ${shardPath}`, {
@@ -138,6 +168,7 @@ export class SidecarPersistence {
138
168
  [this.pluginRoot, 'plugin sidecar root'],
139
169
  [this.worktreeRoot, 'Worktree root'],
140
170
  [this.workspaceRoot, 'Workspace shard root'],
171
+ [this.lockRoot, 'Mutation lock root'],
141
172
  ]) {
142
173
  try {
143
174
  if ((await lstat(pathname)).isSymbolicLink()) {
@@ -159,4 +190,47 @@ export class SidecarPersistence {
159
190
  }
160
191
  }
161
192
  }
193
+ function stripLegacyRepositoryFields(snapshot) {
194
+ const { repository: _repository, pendingOperation, ...withoutRepository } = snapshot;
195
+ void _repository;
196
+ if (pendingOperation === undefined)
197
+ return withoutRepository;
198
+ const { repository: _pendingRepository, ...withoutPendingRepository } = pendingOperation;
199
+ void _pendingRepository;
200
+ return {
201
+ ...withoutRepository,
202
+ pendingOperation: withoutPendingRepository,
203
+ };
204
+ }
205
+ function nextRevision(current, changed) {
206
+ if (!changed)
207
+ return current;
208
+ return (BigInt(current) + 1n).toString();
209
+ }
210
+ async function syncFile(pathname) {
211
+ const handle = await open(pathname, 'r');
212
+ try {
213
+ await handle.sync();
214
+ }
215
+ finally {
216
+ await handle.close();
217
+ }
218
+ }
219
+ async function syncDirectory(pathname) {
220
+ try {
221
+ const handle = await open(pathname, 'r');
222
+ try {
223
+ await handle.sync();
224
+ }
225
+ finally {
226
+ await handle.close();
227
+ }
228
+ }
229
+ catch (error) {
230
+ const code = error.code;
231
+ // Windows and some filesystems do not support opening directories for fsync.
232
+ if (code !== 'EINVAL' && code !== 'ENOTSUP' && code !== 'EISDIR' && code !== 'EPERM')
233
+ throw error;
234
+ }
235
+ }
162
236
  //# sourceMappingURL=sidecar-persistence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-persistence.js","sourceRoot":"","sources":["../../src/provider/sidecar-persistence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sidecar-persistence.js","sourceRoot":"","sources":["../../src/provider/sidecar-persistence.ts"],"names":[],"mappings":"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"}