@cortexkit/opencode-openai-auth 0.11.1 → 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
@@ -1,384 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises'
3
- import { dirname, join } from 'node:path'
4
-
5
- const setRefreshLockRenewalTimeout = globalThis.setTimeout.bind(globalThis)
6
- const clearRefreshLockRenewalTimeout = globalThis.clearTimeout.bind(globalThis)
7
-
8
- // A concurrent contender renaming the freshly-created eviction-marker directory
9
- // away surfaces the vanished parent differently per platform: ENOENT on Linux,
10
- // EINVAL or ENOTDIR on macOS/APFS. All three mean the marker is no longer ours
11
- // to hold — a lost race the caller should retry, not a fatal lock error.
12
- export function isLostMarkerRaceError(error: unknown): boolean {
13
- const code = (error as NodeJS.ErrnoException | null)?.code
14
- return code === 'ENOENT' || code === 'EINVAL' || code === 'ENOTDIR'
15
- }
16
-
17
- export async function acquireRefreshFileLock(options: {
18
- name: string
19
- ttlMs: number
20
- /**
21
- * File the lock is named after. Required: the lock and the write it guards
22
- * must target the same file, and a default resolved in here could only ever
23
- * be one host's path.
24
- */
25
- path: string
26
- now?: () => number
27
- renew?: boolean
28
- renewIntervalMs?: number
29
- onStep?: (
30
- step:
31
- | 'stale-marker-stat'
32
- | 'stale-marker-claimed'
33
- | 'stale-lock-confirmed'
34
- | 'eviction-marker-acquired'
35
- | 'renewal-owner-confirmed'
36
- | 'renewal-marker-unavailable'
37
- | 'renewal-write-fenced'
38
- | 'renewal-write-ready'
39
- | 'relinquish-read'
40
- | 'renewal-finished'
41
- | 'release-owner-confirmed',
42
- ) => void | Promise<void>
43
- }): Promise<{ release: () => Promise<void> } | null> {
44
- const lockPath = `${options.path}.${options.name}.lock`
45
- const legacyOwnerPath = join(lockPath, 'owner.json')
46
- const ownerId = randomUUID()
47
- const now = options.now ?? Date.now
48
- let renewTimer: ReturnType<typeof setTimeout> | null = null
49
- let released = false
50
- let renewalInFlight: Promise<void> | null = null
51
- // Fencing-token eviction: a directory-based marker (mkdir O_EXCL) serializes
52
- // destructive removal and generation-sensitive owner mutations. The marker
53
- // holds an owner file so ownership survives a stale-marker recovery rename:
54
- // the recovering contender renames the stale directory, then must re-check
55
- // ownership before acting — preventing the 3rd interleaving where a stale
56
- // observer renames the FRESH marker the mkdir-winner created.
57
- const evictPath = `${lockPath}.evicting`
58
- const evictOwnerPath = join(evictPath, 'owner.json')
59
- const evictOwnerId = randomUUID()
60
- const EVICT_TTL = 5_000
61
- const MAX_STEAL_ATTEMPTS = 8
62
-
63
- async function readOwner() {
64
- try {
65
- return JSON.parse(await readFile(lockPath, 'utf8'))
66
- } catch (error) {
67
- const code = (error as NodeJS.ErrnoException).code
68
- if (code !== 'EISDIR') throw error
69
- return JSON.parse(await readFile(legacyOwnerPath, 'utf8'))
70
- }
71
- }
72
-
73
- async function writeOwner() {
74
- await writeFile(
75
- lockPath,
76
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
77
- { encoding: 'utf8', mode: 0o600 },
78
- )
79
- }
80
-
81
- async function tryAcquire() {
82
- try {
83
- await writeFile(
84
- lockPath,
85
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
86
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
87
- )
88
- return true
89
- } catch (error) {
90
- const code = (error as NodeJS.ErrnoException).code
91
- if (code === 'EEXIST' || code === 'EISDIR') return false
92
- if (code === 'ENOENT') {
93
- // Reboots can clear runtime temp directories before the next lock acquisition.
94
- await mkdir(dirname(lockPath), { recursive: true })
95
- try {
96
- await writeFile(
97
- lockPath,
98
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
99
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
100
- )
101
- return true
102
- } catch (retryError) {
103
- const retryCode = (retryError as NodeJS.ErrnoException).code
104
- if (retryCode === 'EEXIST' || retryCode === 'EISDIR') return false
105
- throw retryError
106
- }
107
- }
108
- throw error
109
- }
110
- }
111
-
112
- async function backoff() {
113
- await new Promise((resolve) =>
114
- setTimeout(resolve, Math.floor(Math.random() * 4)),
115
- )
116
- }
117
-
118
- async function lockIsLive() {
119
- try {
120
- const currentOwner = await readOwner()
121
- return Number(currentOwner?.expiresAt) > now()
122
- } catch {
123
- try {
124
- const current = await stat(lockPath)
125
- return current.mtimeMs + options.ttlMs > now()
126
- } catch {
127
- // Lock doesn't exist — safe to acquire.
128
- return false
129
- }
130
- }
131
- }
132
-
133
- // Fail-closed: any read error means we do NOT own the marker.
134
- async function ownsEvictionMarker() {
135
- try {
136
- const owner = JSON.parse(await readFile(evictOwnerPath, 'utf8'))
137
- return owner?.ownerId === evictOwnerId
138
- } catch {
139
- return false
140
- }
141
- }
142
-
143
- async function releaseEvictionMarker() {
144
- if (await ownsEvictionMarker()) {
145
- await rm(evictPath, { recursive: true, force: true }).catch(() => {})
146
- }
147
- }
148
-
149
- async function tryAcquireEvictionMarker() {
150
- await mkdir(evictPath)
151
- try {
152
- await writeFile(
153
- evictOwnerPath,
154
- `${JSON.stringify({ ownerId: evictOwnerId, createdAt: now() })}\n`,
155
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
156
- )
157
- } catch (error) {
158
- // A competing contender can rename our just-created marker directory
159
- // away between the mkdir above and this write (the stale-marker steal
160
- // path below does exactly that). That is a lost race, not a failure, so
161
- // report it as such and let the caller back off and retry rather than
162
- // failing the whole lock acquisition.
163
- if (isLostMarkerRaceError(error)) return false
164
- await releaseEvictionMarker()
165
- throw error
166
- }
167
- if (options.onStep) await options.onStep('eviction-marker-acquired')
168
- return true
169
- }
170
-
171
- async function recoverStaleEvictionMarker(): Promise<
172
- 'fresh' | 'missing' | 'recovered'
173
- > {
174
- let evictStat: Awaited<ReturnType<typeof stat>>
175
- try {
176
- evictStat = await stat(evictPath)
177
- } catch (error) {
178
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'
179
- throw error
180
- }
181
- if (evictStat.mtimeMs + EVICT_TTL > now()) return 'fresh'
182
-
183
- if (options.onStep) await options.onStep('stale-marker-stat')
184
- const claimedPath = `${evictPath}.${randomUUID()}`
185
- try {
186
- await rename(evictPath, claimedPath)
187
- } catch (error) {
188
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'
189
- throw error
190
- }
191
- if (options.onStep) await options.onStep('stale-marker-claimed')
192
- await rm(claimedPath, { recursive: true, force: true }).catch(() => {})
193
- return 'recovered'
194
- }
195
-
196
- async function withEvictionMarker(action: () => Promise<void>) {
197
- try {
198
- if (!(await tryAcquireEvictionMarker())) return false
199
- } catch (error) {
200
- if ((error as NodeJS.ErrnoException).code === 'EEXIST') return false
201
- throw error
202
- }
203
-
204
- try {
205
- await action()
206
- } finally {
207
- await releaseEvictionMarker()
208
- }
209
- return true
210
- }
211
-
212
- // Marker loss after a write may mean our record replaced a successor's.
213
- // Delete only a record still owned by us; a concurrent successor write can
214
- // then yield zero winners, never two.
215
- async function relinquishLockAfterMarkerLoss() {
216
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
217
- if (options.onStep) await options.onStep('relinquish-read')
218
- let owner: { ownerId?: string } | undefined
219
- try {
220
- owner = await readOwner()
221
- } catch {
222
- return
223
- }
224
- if (owner?.ownerId !== ownerId) return
225
- try {
226
- await rm(lockPath, { recursive: true, force: true })
227
- return
228
- } catch {
229
- await backoff()
230
- }
231
- }
232
- }
233
-
234
- function scheduleRenewal() {
235
- if (!options.renew || released) return
236
- const intervalMs =
237
- options.renewIntervalMs ?? Math.max(1_000, Math.floor(options.ttlMs / 3))
238
- renewTimer = setRefreshLockRenewalTimeout(() => {
239
- const renewal = (async () => {
240
- let shouldReschedule = !released
241
- try {
242
- const markerAcquired = await withEvictionMarker(async () => {
243
- const owner = await readOwner()
244
- const currentNow = now()
245
- if (released || owner?.ownerId !== ownerId) {
246
- shouldReschedule = false
247
- return
248
- }
249
- // An expired lease is no longer ours to extend; a contender may
250
- // already be eligible to acquire it.
251
- if (Number(owner?.expiresAt) <= currentNow) {
252
- shouldReschedule = false
253
- return
254
- }
255
- if (options.onStep) await options.onStep('renewal-owner-confirmed')
256
- if (released) {
257
- shouldReschedule = false
258
- return
259
- }
260
- if (!(await ownsEvictionMarker())) return
261
- if (options.onStep) await options.onStep('renewal-write-fenced')
262
- if (released) {
263
- shouldReschedule = false
264
- return
265
- }
266
- if (!(await ownsEvictionMarker())) return
267
- if (options.onStep) await options.onStep('renewal-write-ready')
268
- await writeOwner()
269
- if (!(await ownsEvictionMarker())) {
270
- // Marker read errors fail closed: prompt relinquish avoids ambiguity
271
- // instead of waiting for TTL; both outcomes keep zero or one winner.
272
- shouldReschedule = false
273
- await relinquishLockAfterMarkerLoss()
274
- return
275
- }
276
- })
277
- if (!markerAcquired && options.onStep) {
278
- await options.onStep('renewal-marker-unavailable')
279
- }
280
- } catch {
281
- // Transient marker and filesystem failures retry on the next interval.
282
- } finally {
283
- if (options.onStep) {
284
- try {
285
- await options.onStep('renewal-finished')
286
- } catch {
287
- // Test seams must not turn an otherwise-safe renewal into a rejection.
288
- }
289
- }
290
- if (shouldReschedule && !released) scheduleRenewal()
291
- }
292
- })()
293
- renewalInFlight = renewal
294
- void renewal.finally(() => {
295
- if (renewalInFlight === renewal) renewalInFlight = null
296
- })
297
- }, intervalMs)
298
- if ('unref' in renewTimer) renewTimer.unref()
299
- }
300
-
301
- let acquired = await tryAcquire()
302
- if (!acquired) {
303
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
304
- acquired = await tryAcquire()
305
- if (acquired) break
306
- if (await lockIsLive()) return null
307
-
308
- try {
309
- if (!(await tryAcquireEvictionMarker())) {
310
- await backoff()
311
- continue
312
- }
313
- } catch (evictError) {
314
- const code = (evictError as NodeJS.ErrnoException).code
315
- if (code !== 'EEXIST') throw evictError
316
-
317
- const recovered = await recoverStaleEvictionMarker()
318
- if (recovered === 'fresh') return null
319
- await backoff()
320
- continue
321
- }
322
-
323
- try {
324
- if (await lockIsLive()) return null
325
- // Fence check 1: verify we still own the marker before acting on the
326
- // stale-lock-confirmed decision.
327
- if (!(await ownsEvictionMarker())) return null
328
- if (options.onStep) await options.onStep('stale-lock-confirmed')
329
- // Fence check 2: re-verify ownership after the seam (another contender
330
- // may have renamed our fresh marker while we were paused here).
331
- if (!(await ownsEvictionMarker())) return null
332
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
333
- // Fence check 3: re-verify ownership after removing the stale lock.
334
- if (!(await ownsEvictionMarker())) return null
335
- acquired = await tryAcquire()
336
- if (!acquired) return null
337
- // Fence check 4: re-verify ownership after acquiring the lock. If the
338
- // marker was stolen between tryAcquire and this check, release the
339
- // just-acquired lock and return null (fail-closed).
340
- if (!(await ownsEvictionMarker())) {
341
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
342
- acquired = false
343
- return null
344
- }
345
- break
346
- } finally {
347
- await releaseEvictionMarker()
348
- }
349
- }
350
- }
351
-
352
- if (!acquired) return null
353
-
354
- scheduleRenewal()
355
-
356
- return {
357
- release: async () => {
358
- released = true
359
- if (renewTimer) {
360
- clearRefreshLockRenewalTimeout(renewTimer)
361
- renewTimer = null
362
- }
363
- await renewalInFlight
364
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
365
- try {
366
- const markerAcquired = await withEvictionMarker(async () => {
367
- const owner = await readOwner()
368
- if (owner?.ownerId !== ownerId) return
369
- if (options.onStep) await options.onStep('release-owner-confirmed')
370
- if (!(await ownsEvictionMarker())) return
371
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
372
- })
373
- if (markerAcquired) return
374
- await recoverStaleEvictionMarker()
375
- } catch {
376
- return
377
- }
378
- await backoff()
379
- }
380
- // Do not delete by pathname without the marker: bounded retries leave the
381
- // lease to expire rather than risking removal of a successor's lock.
382
- },
383
- }
384
- }
@@ -1,9 +0,0 @@
1
- export function errorMessage(error: unknown) {
2
- if (error instanceof Error) return error.message
3
- if (typeof error === 'string') return error
4
- try {
5
- return JSON.stringify(error)
6
- } catch {
7
- return String(error)
8
- }
9
- }
@@ -1,30 +0,0 @@
1
- import { execFileSync as defaultExecFileSync } from 'node:child_process'
2
-
3
- type BrowserExec = (
4
- file: string,
5
- args: string[],
6
- options: { stdio: 'ignore'; timeout: number },
7
- ) => unknown
8
-
9
- export function openUrl(
10
- url: string,
11
- platform: NodeJS.Platform = process.platform,
12
- execFileSync: BrowserExec = defaultExecFileSync,
13
- ) {
14
- try {
15
- if (platform === 'win32') {
16
- execFileSync('cmd', ['/c', 'start', '', url], {
17
- stdio: 'ignore',
18
- timeout: 3000,
19
- })
20
- return
21
- }
22
-
23
- execFileSync(platform === 'darwin' ? 'open' : 'xdg-open', [url], {
24
- stdio: 'ignore',
25
- timeout: 3000,
26
- })
27
- } catch {
28
- // Browser launch is best effort; the printed URL remains the fallback.
29
- }
30
- }