@cortexkit/opencode-openai-auth 0.11.0 → 1.0.0

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 (204) hide show
  1. package/README.md +15 -26
  2. package/dist/index-766sfrh5.js +1 -0
  3. package/dist/index-8kj7sa1p.js +12 -0
  4. package/dist/index-8rqfbhfw.js +221 -0
  5. package/dist/index-b3q3j39x.js +2 -0
  6. package/dist/index-bmd7x5aq.js +2 -0
  7. package/dist/index-eqh53k8v.js +2 -0
  8. package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
  9. package/dist/index-jyr8y8k4.js +7 -0
  10. package/dist/index-rv08jmd4.js +2 -0
  11. package/dist/index.d.ts +578 -145
  12. package/dist/index.js +1 -110
  13. package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
  14. package/dist/port-file-Do3lQIEk.d.ts +29 -0
  15. package/dist/protocol-BOBdkqqm.d.ts +198 -0
  16. package/dist/rpc/port-file.d.ts +4 -17
  17. package/dist/rpc/port-file.js +1 -1
  18. package/dist/rpc/protocol.d.ts +2 -2
  19. package/dist/rpc/protocol.js +1 -0
  20. package/dist/rpc/rpc-client.d.ts +5 -4
  21. package/dist/rpc/rpc-client.js +1 -1
  22. package/dist/rpc/rpc-dir.d.ts +2 -0
  23. package/dist/rpc/rpc-dir.js +1 -1
  24. package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
  25. package/dist/sidebar-state.d.ts +2 -2
  26. package/dist/sidebar-state.js +1 -1
  27. package/dist/token-util-wdza526f.js +1 -0
  28. package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
  29. package/dist/tui-preferences.js +5 -2
  30. package/dist/tui.d.ts +9 -4
  31. package/dist/v2/server.d.ts +54 -0
  32. package/dist/v2/server.js +4 -0
  33. package/package.json +21 -9
  34. package/src/tui/entry.mjs +26 -25
  35. package/src/tui-compiled/raw/logger.js +24 -0
  36. package/src/tui-compiled/raw/rpc/protocol.js +3 -0
  37. package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
  38. package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
  39. package/src/tui-compiled/raw/selector.js +21 -0
  40. package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
  41. package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
  42. package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
  43. package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
  44. package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
  45. package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
  46. package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
  47. package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
  48. package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
  49. package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
  50. package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
  51. package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
  52. package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
  53. package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
  54. package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
  55. package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
  56. package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
  57. package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
  58. package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
  59. package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
  60. package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
  61. package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
  62. package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
  63. package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
  64. package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
  65. package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
  66. package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
  67. package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
  68. package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
  69. package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
  70. package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
  71. package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
  72. package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
  73. package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
  74. package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
  75. package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
  76. package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
  77. package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
  78. package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
  79. package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
  80. package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
  81. package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
  82. package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
  83. package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
  84. package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
  85. package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
  86. package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
  87. package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
  88. package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
  89. package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
  90. package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
  91. package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
  92. package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
  93. package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
  94. package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
  95. package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
  96. package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
  97. package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
  98. package/src/tui-compiled/raw/sidebar-state.js +960 -0
  99. package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
  100. package/src/tui-compiled/raw/tui-preferences.js +135 -0
  101. package/src/tui-compiled/raw/tui.tsx +135 -143
  102. package/src/tui-compiled/runtime/logger.js +24 -0
  103. package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
  104. package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
  105. package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
  106. package/src/tui-compiled/runtime/selector.js +21 -0
  107. package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
  108. package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
  109. package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
  110. package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
  111. package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
  112. package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
  113. package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
  114. package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
  115. package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
  116. package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
  117. package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
  118. package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
  119. package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
  120. package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
  121. package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
  122. package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
  123. package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
  124. package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
  125. package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
  126. package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
  127. package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
  128. package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
  129. package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
  130. package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
  131. package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
  132. package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
  133. package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
  134. package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
  135. package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
  136. package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
  137. package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
  138. package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
  139. package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
  140. package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
  141. package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
  142. package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
  143. package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
  144. package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
  145. package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
  146. package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
  147. package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
  148. package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
  149. package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
  150. package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
  151. package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
  152. package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
  153. package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
  154. package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
  155. package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
  156. package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
  157. package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
  158. package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
  159. package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
  160. package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
  161. package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
  162. package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
  163. package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
  164. package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
  165. package/src/tui-compiled/runtime/sidebar-state.js +960 -0
  166. package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
  167. package/src/tui-compiled/runtime/tui-preferences.js +135 -0
  168. package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
  169. package/dist/index-4effjmr6.js +0 -2
  170. package/dist/index-9ehhb051.js +0 -2
  171. package/dist/index-g3hsdjh4.js +0 -2
  172. package/dist/index-hh52v8sb.js +0 -2
  173. package/dist/index-jwn4r3sw.js +0 -1
  174. package/dist/index-sq36b120.js +0 -211
  175. package/dist/internal-BTndPmW2.d.ts +0 -914
  176. package/dist/quota-normalize-08hpp51v.js +0 -1
  177. package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
  178. package/dist/token-util-ypgz7rge.js +0 -1
  179. package/src/tui-compiled/raw/core-account-paths.ts +0 -114
  180. package/src/tui-compiled/raw/core-custody-state.ts +0 -243
  181. package/src/tui-compiled/raw/logger.ts +0 -53
  182. package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
  183. package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
  184. package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
  185. package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
  186. package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
  187. package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
  188. package/src/tui-compiled/raw/tui-preferences.ts +0 -328
  189. package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
  190. package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
  191. package/src/tui-compiled/runtime/logger.ts +0 -53
  192. package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
  193. package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
  194. package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
  195. package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
  196. package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
  197. package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
  198. package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
  199. package/src/tui-compiled/shared/internal.ts +0 -6
  200. package/src/tui-compiled/shared/logger.ts +0 -264
  201. package/src/tui-compiled/shared/paths.ts +0 -33
  202. package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
  203. package/src/tui-compiled/shared/util/error.ts +0 -9
  204. package/src/tui-compiled/shared/util/open-url.ts +0 -30
@@ -0,0 +1,501 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, open, readFile, rename, rm, stat, writeFile, } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { LockOwnershipError, lockPathFor } from "./4825e03209d6ae3e-with-lock.js";
5
+ const setRefreshLockRenewalTimeout = globalThis.setTimeout.bind(globalThis);
6
+ const clearRefreshLockRenewalTimeout = globalThis.clearTimeout.bind(globalThis);
7
+ // A concurrent contender renaming the freshly-created eviction-marker directory
8
+ // away surfaces the vanished parent differently per platform: ENOENT on Linux,
9
+ // EINVAL or ENOTDIR on macOS/APFS. All three mean the marker is no longer ours
10
+ // to hold — a lost race the caller should retry, not a fatal lock error.
11
+ export function isLostMarkerRaceError(error) {
12
+ const code = error?.code;
13
+ return code === 'ENOENT' || code === 'EINVAL' || code === 'ENOTDIR';
14
+ }
15
+ export async function acquireRefreshFileLock(options) {
16
+ const lockPath = lockPathFor(options.path, options.name);
17
+ const legacyOwnerPath = join(lockPath, 'owner.json');
18
+ const ownerId = randomUUID();
19
+ const now = options.now ?? Date.now;
20
+ let renewTimer = null;
21
+ let released = false;
22
+ let loss;
23
+ let resolveLoss;
24
+ const lostPromise = new Promise((resolve) => {
25
+ resolveLoss = resolve;
26
+ });
27
+ function notifyTimerStep(event) {
28
+ try {
29
+ const result = options.onRenewalTimer?.(event);
30
+ if (result &&
31
+ (typeof result === 'object' || typeof result === 'function') &&
32
+ 'then' in result &&
33
+ typeof result.then === 'function') {
34
+ void Promise.resolve(result).catch(() => { });
35
+ }
36
+ }
37
+ catch {
38
+ // Timer observer failures must not replace the lock operation result.
39
+ }
40
+ }
41
+ function cancelRenewalTimer() {
42
+ if (!renewTimer)
43
+ return;
44
+ clearRefreshLockRenewalTimeout(renewTimer);
45
+ renewTimer = null;
46
+ notifyTimerStep('cancelled');
47
+ }
48
+ function recordLoss(reason, owner) {
49
+ if (loss || released)
50
+ return;
51
+ loss = Object.freeze({
52
+ reason,
53
+ expectedOwnerId: ownerId,
54
+ ...(typeof owner?.ownerId === 'string'
55
+ ? { observedOwnerId: owner.ownerId }
56
+ : {}),
57
+ ...(typeof owner?.expiresAt === 'number'
58
+ ? { observedExpiresAt: owner.expiresAt }
59
+ : {}),
60
+ });
61
+ cancelRenewalTimer();
62
+ resolveLoss(loss);
63
+ }
64
+ let renewalInFlight = null;
65
+ // Only the owner of this exclusively-created marker may remove or renew a
66
+ // lock. A contender recovering a stale marker can accidentally rename a newer
67
+ // marker instead; checking its owner file before each mutation prevents the
68
+ // displaced owner from continuing to act on the lock.
69
+ const evictPath = `${lockPath}.evicting`;
70
+ const evictOwnerPath = join(evictPath, 'owner.json');
71
+ const evictOwnerId = randomUUID();
72
+ const EVICT_TTL = 5_000;
73
+ const MAX_STEAL_ATTEMPTS = 8;
74
+ async function readOwner() {
75
+ try {
76
+ return JSON.parse(await readFile(lockPath, 'utf8'));
77
+ }
78
+ catch (error) {
79
+ const code = error.code;
80
+ if (code !== 'EISDIR')
81
+ throw error;
82
+ return JSON.parse(await readFile(legacyOwnerPath, 'utf8'));
83
+ }
84
+ }
85
+ async function writeOwner() {
86
+ // Readers must see a complete lease, even while renewal is writing.
87
+ const tempPath = `${lockPath}.${randomUUID()}.tmp`;
88
+ let handle;
89
+ let created = false;
90
+ try {
91
+ handle = await open(tempPath, 'wx', 0o600);
92
+ created = true;
93
+ await handle.writeFile(`${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`, 'utf8');
94
+ await handle.chmod(0o600);
95
+ await handle.close();
96
+ handle = undefined;
97
+ await rename(tempPath, lockPath);
98
+ created = false;
99
+ }
100
+ finally {
101
+ await handle?.close().catch(() => { });
102
+ if (created)
103
+ await rm(tempPath, { force: true }).catch(() => { });
104
+ }
105
+ }
106
+ async function tryAcquire() {
107
+ try {
108
+ await writeFile(lockPath, `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
109
+ return true;
110
+ }
111
+ catch (error) {
112
+ const code = error.code;
113
+ if (code === 'EEXIST' || code === 'EISDIR')
114
+ return false;
115
+ if (code === 'ENOENT') {
116
+ // Reboots can clear runtime temp directories before the next lock acquisition.
117
+ await mkdir(dirname(lockPath), { recursive: true });
118
+ try {
119
+ await writeFile(lockPath, `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
120
+ return true;
121
+ }
122
+ catch (retryError) {
123
+ const retryCode = retryError.code;
124
+ if (retryCode === 'EEXIST' || retryCode === 'EISDIR')
125
+ return false;
126
+ throw retryError;
127
+ }
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+ async function backoff() {
133
+ await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * 4)));
134
+ }
135
+ async function lockIsLive() {
136
+ try {
137
+ const currentOwner = await readOwner();
138
+ return Number(currentOwner?.expiresAt) > now();
139
+ }
140
+ catch {
141
+ try {
142
+ const current = await stat(lockPath);
143
+ return current.mtimeMs + options.ttlMs > now();
144
+ }
145
+ catch {
146
+ // Lock doesn't exist — safe to acquire.
147
+ return false;
148
+ }
149
+ }
150
+ }
151
+ // Fail-closed: any read error means we do NOT own the marker.
152
+ async function ownsEvictionMarker() {
153
+ try {
154
+ const owner = JSON.parse(await readFile(evictOwnerPath, 'utf8'));
155
+ return owner?.ownerId === evictOwnerId;
156
+ }
157
+ catch {
158
+ return false;
159
+ }
160
+ }
161
+ async function releaseEvictionMarker() {
162
+ if (await ownsEvictionMarker()) {
163
+ await rm(evictPath, { recursive: true, force: true }).catch(() => { });
164
+ }
165
+ }
166
+ async function tryAcquireEvictionMarker() {
167
+ await mkdir(evictPath);
168
+ try {
169
+ await writeFile(evictOwnerPath, `${JSON.stringify({ ownerId: evictOwnerId, createdAt: now() })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
170
+ }
171
+ catch (error) {
172
+ // A competing contender can rename our just-created marker directory
173
+ // away between the mkdir above and this write (the stale-marker steal
174
+ // path below does exactly that). That is a lost race, not a failure, so
175
+ // report it as such and let the caller back off and retry rather than
176
+ // failing the whole lock acquisition.
177
+ if (isLostMarkerRaceError(error))
178
+ return false;
179
+ await releaseEvictionMarker();
180
+ throw error;
181
+ }
182
+ if (options.onStep)
183
+ await options.onStep('eviction-marker-acquired');
184
+ return true;
185
+ }
186
+ async function recoverStaleEvictionMarker() {
187
+ let evictStat;
188
+ try {
189
+ evictStat = await stat(evictPath);
190
+ }
191
+ catch (error) {
192
+ if (error.code === 'ENOENT')
193
+ return 'missing';
194
+ throw error;
195
+ }
196
+ if (evictStat.mtimeMs + EVICT_TTL > now())
197
+ return 'fresh';
198
+ if (options.onStep)
199
+ await options.onStep('stale-marker-stat');
200
+ const claimedPath = `${evictPath}.${randomUUID()}`;
201
+ try {
202
+ await rename(evictPath, claimedPath);
203
+ }
204
+ catch (error) {
205
+ if (error.code === 'ENOENT')
206
+ return 'missing';
207
+ throw error;
208
+ }
209
+ if (options.onStep)
210
+ await options.onStep('stale-marker-claimed');
211
+ await rm(claimedPath, { recursive: true, force: true }).catch(() => { });
212
+ return 'recovered';
213
+ }
214
+ async function withEvictionMarker(action) {
215
+ try {
216
+ if (!(await tryAcquireEvictionMarker()))
217
+ return false;
218
+ }
219
+ catch (error) {
220
+ if (error.code === 'EEXIST')
221
+ return false;
222
+ throw error;
223
+ }
224
+ try {
225
+ await action();
226
+ }
227
+ finally {
228
+ await releaseEvictionMarker();
229
+ }
230
+ return true;
231
+ }
232
+ // Marker loss after a write may mean another contender has taken over.
233
+ // Delete only a record still owned by us, so an already-installed
234
+ // successor is preserved. The owner read and the removal are not atomic:
235
+ // a successor record written between them can still be deleted.
236
+ async function relinquishLockAfterMarkerLoss() {
237
+ for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
238
+ if (options.onStep)
239
+ await options.onStep('relinquish-read');
240
+ let owner;
241
+ try {
242
+ owner = await readOwner();
243
+ }
244
+ catch {
245
+ return;
246
+ }
247
+ if (owner?.ownerId !== ownerId)
248
+ return;
249
+ try {
250
+ await rm(lockPath, { recursive: true, force: true });
251
+ return;
252
+ }
253
+ catch {
254
+ await backoff();
255
+ }
256
+ }
257
+ }
258
+ function scheduleRenewal() {
259
+ if (!options.renew || released || loss)
260
+ return;
261
+ const intervalMs = options.renewIntervalMs ?? Math.max(1_000, Math.floor(options.ttlMs / 3));
262
+ renewTimer = setRefreshLockRenewalTimeout(() => {
263
+ renewTimer = null;
264
+ const renewal = (async () => {
265
+ let shouldReschedule = !released;
266
+ try {
267
+ const markerAcquired = await withEvictionMarker(async () => {
268
+ const owner = await readOwner();
269
+ const currentNow = now();
270
+ if (released || loss) {
271
+ shouldReschedule = false;
272
+ return;
273
+ }
274
+ if (owner?.ownerId !== ownerId) {
275
+ recordLoss('taken-over', owner);
276
+ shouldReschedule = false;
277
+ return;
278
+ }
279
+ // An expired lease is no longer ours to extend; a contender may
280
+ // already be eligible to acquire it.
281
+ if (!(Number(owner?.expiresAt) > currentNow)) {
282
+ recordLoss('expired', owner);
283
+ shouldReschedule = false;
284
+ return;
285
+ }
286
+ if (options.onStep)
287
+ await options.onStep('renewal-owner-confirmed');
288
+ if (released || loss) {
289
+ shouldReschedule = false;
290
+ return;
291
+ }
292
+ if (!(await ownsEvictionMarker()))
293
+ return;
294
+ if (options.onStep)
295
+ await options.onStep('renewal-write-fenced');
296
+ if (released || loss) {
297
+ shouldReschedule = false;
298
+ return;
299
+ }
300
+ if (!(await ownsEvictionMarker()))
301
+ return;
302
+ if (options.onStep)
303
+ await options.onStep('renewal-write-ready');
304
+ if (released || loss) {
305
+ shouldReschedule = false;
306
+ return;
307
+ }
308
+ await writeOwner();
309
+ if (!(await ownsEvictionMarker())) {
310
+ // If marker ownership cannot be read, stop claiming the lease
311
+ // and remove only a record that still carries our owner id.
312
+ recordLoss('marker-lost');
313
+ shouldReschedule = false;
314
+ await relinquishLockAfterMarkerLoss();
315
+ return;
316
+ }
317
+ });
318
+ if (!markerAcquired && options.onStep) {
319
+ await options.onStep('renewal-marker-unavailable');
320
+ }
321
+ }
322
+ catch {
323
+ // Retry transient failures only while the lease can still be verified.
324
+ try {
325
+ const owner = await readOwner();
326
+ if (owner?.ownerId !== ownerId)
327
+ recordLoss('taken-over', owner);
328
+ else if (!(Number(owner?.expiresAt) > now()))
329
+ recordLoss('expired', owner);
330
+ }
331
+ catch {
332
+ recordLoss('renewal-failed');
333
+ }
334
+ }
335
+ finally {
336
+ if (options.onStep) {
337
+ try {
338
+ await options.onStep('renewal-finished');
339
+ }
340
+ catch {
341
+ // Errors from the onStep observer must not reject the renewal.
342
+ }
343
+ }
344
+ if (shouldReschedule && !released && !loss)
345
+ scheduleRenewal();
346
+ }
347
+ })();
348
+ renewalInFlight = renewal;
349
+ void renewal.finally(() => {
350
+ if (renewalInFlight === renewal)
351
+ renewalInFlight = null;
352
+ });
353
+ }, intervalMs);
354
+ if ('unref' in renewTimer)
355
+ renewTimer.unref();
356
+ notifyTimerStep('scheduled');
357
+ }
358
+ function contended() {
359
+ try {
360
+ const result = options.onContended?.();
361
+ if (result &&
362
+ (typeof result === 'object' || typeof result === 'function') &&
363
+ 'then' in result &&
364
+ typeof result.then === 'function') {
365
+ void Promise.resolve(result).catch(() => { });
366
+ }
367
+ }
368
+ catch {
369
+ // Observers cannot change whether the lock is acquired.
370
+ }
371
+ return null;
372
+ }
373
+ let acquired = await tryAcquire();
374
+ if (!acquired) {
375
+ for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
376
+ acquired = await tryAcquire();
377
+ if (acquired)
378
+ break;
379
+ if (await lockIsLive())
380
+ return contended();
381
+ if (options.onStep)
382
+ await options.onStep('stale-lock-observed');
383
+ try {
384
+ if (!(await tryAcquireEvictionMarker())) {
385
+ await backoff();
386
+ continue;
387
+ }
388
+ }
389
+ catch (evictError) {
390
+ const code = evictError.code;
391
+ if (code !== 'EEXIST')
392
+ throw evictError;
393
+ const recovered = await recoverStaleEvictionMarker();
394
+ if (recovered === 'fresh')
395
+ return null;
396
+ await backoff();
397
+ continue;
398
+ }
399
+ try {
400
+ if (await lockIsLive())
401
+ return contended();
402
+ // Verify marker ownership before removing a lock found not live.
403
+ if (!(await ownsEvictionMarker()))
404
+ return null;
405
+ if (options.onStep)
406
+ await options.onStep('stale-lock-confirmed');
407
+ // The onStep callback can pause while another contender renames
408
+ // our marker, so verify ownership again after the callback.
409
+ if (!(await ownsEvictionMarker()))
410
+ return null;
411
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
412
+ // Fence check 3: re-verify ownership after removing the stale lock.
413
+ if (!(await ownsEvictionMarker()))
414
+ return null;
415
+ if (options.onStep)
416
+ await options.onStep('stale-lock-removed');
417
+ acquired = await tryAcquire();
418
+ if (!acquired)
419
+ return null;
420
+ if (options.onStep)
421
+ await options.onStep('stale-lock-recreated');
422
+ // Fence check 4: re-verify ownership after acquiring the lock. If the
423
+ // marker was stolen between tryAcquire and this check, release the
424
+ // just-acquired lock and return null (fail-closed).
425
+ if (!(await ownsEvictionMarker())) {
426
+ await relinquishLockAfterMarkerLoss();
427
+ acquired = false;
428
+ return null;
429
+ }
430
+ break;
431
+ }
432
+ finally {
433
+ await releaseEvictionMarker();
434
+ }
435
+ }
436
+ }
437
+ if (!acquired)
438
+ return null;
439
+ scheduleRenewal();
440
+ return {
441
+ ownerId,
442
+ whenLost: () => lostPromise,
443
+ hasLost: () => loss !== undefined,
444
+ assertOwned: async () => {
445
+ let observed;
446
+ try {
447
+ const owner = await readOwner();
448
+ observed = owner;
449
+ if (!released &&
450
+ !loss &&
451
+ owner?.ownerId === ownerId &&
452
+ Number(owner?.expiresAt) > now())
453
+ return;
454
+ recordLoss(owner?.ownerId !== ownerId ? 'taken-over' : 'expired', owner);
455
+ }
456
+ catch {
457
+ // Unreadable ownership is not evidence of a valid lease.
458
+ recordLoss('unreadable');
459
+ }
460
+ throw new LockOwnershipError({
461
+ target: options.path,
462
+ name: options.name,
463
+ expectedOwnerId: ownerId,
464
+ ...(typeof observed?.ownerId === 'string'
465
+ ? { observedOwnerId: observed.ownerId }
466
+ : {}),
467
+ ...(typeof observed?.expiresAt === 'number'
468
+ ? { observedExpiresAt: observed.expiresAt }
469
+ : {}),
470
+ });
471
+ },
472
+ release: async () => {
473
+ released = true;
474
+ cancelRenewalTimer();
475
+ await renewalInFlight;
476
+ for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
477
+ try {
478
+ const markerAcquired = await withEvictionMarker(async () => {
479
+ const owner = await readOwner();
480
+ if (owner?.ownerId !== ownerId)
481
+ return;
482
+ if (options.onStep)
483
+ await options.onStep('release-owner-confirmed');
484
+ if (!(await ownsEvictionMarker()))
485
+ return;
486
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
487
+ });
488
+ if (markerAcquired)
489
+ return;
490
+ await recoverStaleEvictionMarker();
491
+ }
492
+ catch {
493
+ return;
494
+ }
495
+ await backoff();
496
+ }
497
+ // Do not delete by pathname without the marker: bounded retries leave the
498
+ // lease to expire rather than risking removal of a successor's lock.
499
+ },
500
+ };
501
+ }
@@ -0,0 +1,6 @@
1
+ export { writeJsonAtomic } from "./7211cfec06a298bc-atomic-write.js";
2
+ export { WRITER_LOCK_CONSTANTS } from "./9b4da614c45f9cd0-lock-constants.js";
3
+ // The try-once lock underneath withLock, for callers that hold a lock across
4
+ // several steps or poll instead of waiting (for example a refresh lease).
5
+ export { acquireRefreshFileLock, isLostMarkerRaceError, } from "./e8cd4929507508d8-refresh-file-lock.js";
6
+ export { LockContentionError, LockOwnershipError, lockPathFor, withLock, } from "./4825e03209d6ae3e-with-lock.js";
@@ -0,0 +1,11 @@
1
+ export function errorMessage(error) {
2
+ if (error instanceof Error)
3
+ return error.message;
4
+ if (typeof error === "string")
5
+ return error;
6
+ try {
7
+ return JSON.stringify(error);
8
+ } catch {
9
+ return String(error);
10
+ }
11
+ }