@cortexkit/opencode-openai-auth 0.7.1 → 0.8.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 (114) hide show
  1. package/README.md +9 -5
  2. package/dist/{index-9jpc8yct.js → index-02h7g5f9.js} +1 -1
  3. package/dist/index-2xppkvkx.js +2 -0
  4. package/dist/index-3w4vmckx.js +2 -0
  5. package/dist/{index-zxqb5ydj.js → index-507jh8bv.js} +1 -1
  6. package/dist/{index-zg1x8sty.js → index-944gsj1s.js} +1 -1
  7. package/dist/index-nshbgkz1.js +198 -0
  8. package/dist/{index-87145jkw.js → index-xyg86jgt.js} +1 -1
  9. package/dist/index.d.ts +381 -35
  10. package/dist/index.js +66 -161
  11. package/dist/protocol-Cv9ZmU0p.d.ts +24 -0
  12. package/dist/quota-normalize-wap0rne8.js +1 -0
  13. package/dist/rpc/port-file.d.ts +11 -9
  14. package/dist/rpc/port-file.js +1 -1
  15. package/dist/rpc/protocol.d.ts +2 -21
  16. package/dist/rpc/rpc-client.d.ts +6 -4
  17. package/dist/rpc/rpc-client.js +1 -1
  18. package/dist/rpc/rpc-dir.d.ts +7 -5
  19. package/dist/rpc/rpc-dir.js +1 -1
  20. package/dist/sidebar-state.d.ts +84 -83
  21. package/dist/sidebar-state.js +1 -1
  22. package/dist/{token-bvp0y166.js → token-jdvpwk60.js} +1 -1
  23. package/dist/token-util-zzm7paz9.js +1 -0
  24. package/dist/tui-preferences.d.ts +27 -26
  25. package/dist/tui-preferences.js +1 -1
  26. package/dist/tui.d.ts +18 -17
  27. package/package.json +10 -17
  28. package/src/tui/entry.mjs +2 -2
  29. package/src/tui-compiled/{core/account-paths.ts → raw/core-account-paths.ts} +29 -18
  30. package/src/tui-compiled/raw/logger.ts +53 -0
  31. package/src/tui-compiled/{rpc/port-file.ts → raw/rpc-port-file.ts} +1 -1
  32. package/src/tui-compiled/raw/rpc-protocol.ts +12 -0
  33. package/src/{rpc/rpc-client.ts → tui-compiled/raw/rpc-rpc-client.ts} +2 -2
  34. package/src/{sidebar-state.ts → tui-compiled/raw/sidebar-state.ts} +2 -2
  35. package/src/{tui/command-dialogs.tsx → tui-compiled/raw/tui-command-dialogs.tsx} +5 -5
  36. package/src/{tui.tsx → tui-compiled/raw/tui.tsx} +11 -10
  37. package/src/{core/account-paths.ts → tui-compiled/runtime/core-account-paths.ts} +29 -18
  38. package/src/tui-compiled/runtime/logger.ts +53 -0
  39. package/src/{rpc/port-file.ts → tui-compiled/runtime/rpc-port-file.ts} +1 -1
  40. package/src/tui-compiled/runtime/rpc-protocol.ts +12 -0
  41. package/src/tui-compiled/{rpc/rpc-client.ts → runtime/rpc-rpc-client.ts} +2 -2
  42. package/src/tui-compiled/{sidebar-state.ts → runtime/sidebar-state.ts} +2 -2
  43. package/src/tui-compiled/{tui/command-dialogs.tsx → runtime/tui-command-dialogs.tsx} +3 -4
  44. package/src/tui-compiled/{tui.tsx → runtime/tui.tsx} +10 -10
  45. package/src/tui-compiled/shared/internal.ts +6 -0
  46. package/src/{logger.ts → tui-compiled/shared/logger.ts} +82 -21
  47. package/src/tui-compiled/shared/paths.ts +33 -0
  48. package/src/{core → tui-compiled/shared}/refresh-file-lock.ts +7 -4
  49. package/dist/cli.d.ts +0 -2
  50. package/dist/cli.js +0 -15
  51. package/dist/codex-http.d.ts +0 -3
  52. package/dist/commands.d.ts +0 -88
  53. package/dist/config.d.ts +0 -47
  54. package/dist/core/account-paths.d.ts +0 -25
  55. package/dist/core/accounts.d.ts +0 -330
  56. package/dist/core/atomic-write.d.ts +0 -1
  57. package/dist/core/background-quota-refresh.d.ts +0 -46
  58. package/dist/core/backoff.d.ts +0 -53
  59. package/dist/core/cachekeep.d.ts +0 -118
  60. package/dist/core/oauth.d.ts +0 -138
  61. package/dist/core/provider.d.ts +0 -70
  62. package/dist/core/quota-manager.d.ts +0 -192
  63. package/dist/core/refresh-all-quota.d.ts +0 -86
  64. package/dist/core/refresh-file-lock.d.ts +0 -12
  65. package/dist/core/reset-credits.d.ts +0 -116
  66. package/dist/core/sticky-routing.d.ts +0 -43
  67. package/dist/dump.d.ts +0 -20
  68. package/dist/hosted-web-search.d.ts +0 -5
  69. package/dist/index-8f1200qx.js +0 -6
  70. package/dist/index-nrvjr97q.js +0 -3
  71. package/dist/index-qaggaze1.js +0 -2
  72. package/dist/index-sdcpzbnk.js +0 -2
  73. package/dist/index-zf3axndm.js +0 -89
  74. package/dist/logger.d.ts +0 -22
  75. package/dist/model-costs.d.ts +0 -29
  76. package/dist/prompt-context.d.ts +0 -19
  77. package/dist/quota-normalize-dvwtmy7f.js +0 -1
  78. package/dist/quota-normalize.d.ts +0 -43
  79. package/dist/raw-ws-bun.d.ts +0 -40
  80. package/dist/raw-ws-node.d.ts +0 -37
  81. package/dist/raw-ws-upgrade.d.ts +0 -2
  82. package/dist/raw-ws.d.ts +0 -3
  83. package/dist/response-stream-error.d.ts +0 -12
  84. package/dist/rpc/notifications.d.ts +0 -5
  85. package/dist/rpc/rpc-server.d.ts +0 -17
  86. package/dist/token-util-jcj7k7pa.js +0 -1
  87. package/dist/tui/command-dialogs.d.ts +0 -21
  88. package/dist/util/error.d.ts +0 -1
  89. package/dist/util/open-url.d.ts +0 -6
  90. package/dist/util/proxy-env.d.ts +0 -4
  91. package/dist/util/record.d.ts +0 -1
  92. package/dist/util/stable-json.d.ts +0 -1
  93. package/dist/util/uuid-v7.d.ts +0 -1
  94. package/dist/version.d.ts +0 -1
  95. package/dist/ws-pool.d.ts +0 -57
  96. package/dist/ws.d.ts +0 -53
  97. package/src/rpc/notifications.ts +0 -54
  98. package/src/rpc/protocol.ts +0 -33
  99. package/src/rpc/rpc-server.ts +0 -172
  100. package/src/tui-compiled/core/refresh-file-lock.ts +0 -381
  101. package/src/tui-compiled/logger.ts +0 -196
  102. package/src/tui-compiled/rpc/protocol.ts +0 -33
  103. package/src/util/error.ts +0 -9
  104. package/src/util/open-url.ts +0 -30
  105. package/src/util/proxy-env.ts +0 -42
  106. package/src/util/record.ts +0 -3
  107. package/src/util/stable-json.ts +0 -15
  108. package/src/util/uuid-v7.ts +0 -18
  109. /package/src/{rpc/rpc-dir.ts → tui-compiled/raw/rpc-rpc-dir.ts} +0 -0
  110. /package/src/tui-compiled/{tui-preferences.ts → raw/tui-preferences.ts} +0 -0
  111. /package/src/tui-compiled/{rpc/rpc-dir.ts → runtime/rpc-rpc-dir.ts} +0 -0
  112. /package/src/{tui-preferences.ts → tui-compiled/runtime/tui-preferences.ts} +0 -0
  113. /package/src/tui-compiled/{util → shared/util}/error.ts +0 -0
  114. /package/src/tui-compiled/{util → shared/util}/open-url.ts +0 -0
@@ -1,381 +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
- import { getAccountStoragePath } from './account-paths'
5
-
6
- const setRefreshLockRenewalTimeout = globalThis.setTimeout.bind(globalThis)
7
- const clearRefreshLockRenewalTimeout = globalThis.clearTimeout.bind(globalThis)
8
-
9
- // A concurrent contender renaming the freshly-created eviction-marker directory
10
- // away surfaces the vanished parent differently per platform: ENOENT on Linux,
11
- // EINVAL or ENOTDIR on macOS/APFS. All three mean the marker is no longer ours
12
- // to hold — a lost race the caller should retry, not a fatal lock error.
13
- export function isLostMarkerRaceError(error: unknown): boolean {
14
- const code = (error as NodeJS.ErrnoException | null)?.code
15
- return code === 'ENOENT' || code === 'EINVAL' || code === 'ENOTDIR'
16
- }
17
-
18
- export async function acquireRefreshFileLock(options: {
19
- name: string
20
- ttlMs: number
21
- path?: string
22
- now?: () => number
23
- renew?: boolean
24
- renewIntervalMs?: number
25
- onStep?: (
26
- step:
27
- | 'stale-marker-stat'
28
- | 'stale-marker-claimed'
29
- | 'stale-lock-confirmed'
30
- | 'eviction-marker-acquired'
31
- | 'renewal-owner-confirmed'
32
- | 'renewal-marker-unavailable'
33
- | 'renewal-write-fenced'
34
- | 'renewal-write-ready'
35
- | 'relinquish-read'
36
- | 'renewal-finished'
37
- | 'release-owner-confirmed',
38
- ) => void | Promise<void>
39
- }): Promise<{ release: () => Promise<void> } | null> {
40
- const accountPath = options.path ?? getAccountStoragePath()
41
- const lockPath = `${accountPath}.${options.name}.lock`
42
- const legacyOwnerPath = join(lockPath, 'owner.json')
43
- const ownerId = randomUUID()
44
- const now = options.now ?? Date.now
45
- let renewTimer: ReturnType<typeof setTimeout> | null = null
46
- let released = false
47
- let renewalInFlight: Promise<void> | null = null
48
- // Fencing-token eviction: a directory-based marker (mkdir O_EXCL) serializes
49
- // destructive removal and generation-sensitive owner mutations. The marker
50
- // holds an owner file so ownership survives a stale-marker recovery rename:
51
- // the recovering contender renames the stale directory, then must re-check
52
- // ownership before acting — preventing the 3rd interleaving where a stale
53
- // observer renames the FRESH marker the mkdir-winner created.
54
- const evictPath = `${lockPath}.evicting`
55
- const evictOwnerPath = join(evictPath, 'owner.json')
56
- const evictOwnerId = randomUUID()
57
- const EVICT_TTL = 5_000
58
- const MAX_STEAL_ATTEMPTS = 8
59
-
60
- async function readOwner() {
61
- try {
62
- return JSON.parse(await readFile(lockPath, 'utf8'))
63
- } catch (error) {
64
- const code = (error as NodeJS.ErrnoException).code
65
- if (code !== 'EISDIR') throw error
66
- return JSON.parse(await readFile(legacyOwnerPath, 'utf8'))
67
- }
68
- }
69
-
70
- async function writeOwner() {
71
- await writeFile(
72
- lockPath,
73
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
74
- { encoding: 'utf8', mode: 0o600 },
75
- )
76
- }
77
-
78
- async function tryAcquire() {
79
- try {
80
- await writeFile(
81
- lockPath,
82
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
83
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
84
- )
85
- return true
86
- } catch (error) {
87
- const code = (error as NodeJS.ErrnoException).code
88
- if (code === 'EEXIST' || code === 'EISDIR') return false
89
- if (code === 'ENOENT') {
90
- // Reboots can clear runtime temp directories before the next lock acquisition.
91
- await mkdir(dirname(lockPath), { recursive: true })
92
- try {
93
- await writeFile(
94
- lockPath,
95
- `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`,
96
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
97
- )
98
- return true
99
- } catch (retryError) {
100
- const retryCode = (retryError as NodeJS.ErrnoException).code
101
- if (retryCode === 'EEXIST' || retryCode === 'EISDIR') return false
102
- throw retryError
103
- }
104
- }
105
- throw error
106
- }
107
- }
108
-
109
- async function backoff() {
110
- await new Promise((resolve) =>
111
- setTimeout(resolve, Math.floor(Math.random() * 4)),
112
- )
113
- }
114
-
115
- async function lockIsLive() {
116
- try {
117
- const currentOwner = await readOwner()
118
- return Number(currentOwner?.expiresAt) > now()
119
- } catch {
120
- try {
121
- const current = await stat(lockPath)
122
- return current.mtimeMs + options.ttlMs > now()
123
- } catch {
124
- // Lock doesn't exist — safe to acquire.
125
- return false
126
- }
127
- }
128
- }
129
-
130
- // Fail-closed: any read error means we do NOT own the marker.
131
- async function ownsEvictionMarker() {
132
- try {
133
- const owner = JSON.parse(await readFile(evictOwnerPath, 'utf8'))
134
- return owner?.ownerId === evictOwnerId
135
- } catch {
136
- return false
137
- }
138
- }
139
-
140
- async function releaseEvictionMarker() {
141
- if (await ownsEvictionMarker()) {
142
- await rm(evictPath, { recursive: true, force: true }).catch(() => {})
143
- }
144
- }
145
-
146
- async function tryAcquireEvictionMarker() {
147
- await mkdir(evictPath)
148
- try {
149
- await writeFile(
150
- evictOwnerPath,
151
- `${JSON.stringify({ ownerId: evictOwnerId, createdAt: now() })}\n`,
152
- { encoding: 'utf8', mode: 0o600, flag: 'wx' },
153
- )
154
- } catch (error) {
155
- // A competing contender can rename our just-created marker directory
156
- // away between the mkdir above and this write (the stale-marker steal
157
- // path below does exactly that). That is a lost race, not a failure, so
158
- // report it as such and let the caller back off and retry rather than
159
- // failing the whole lock acquisition.
160
- if (isLostMarkerRaceError(error)) return false
161
- await releaseEvictionMarker()
162
- throw error
163
- }
164
- if (options.onStep) await options.onStep('eviction-marker-acquired')
165
- return true
166
- }
167
-
168
- async function recoverStaleEvictionMarker(): Promise<
169
- 'fresh' | 'missing' | 'recovered'
170
- > {
171
- let evictStat: Awaited<ReturnType<typeof stat>>
172
- try {
173
- evictStat = await stat(evictPath)
174
- } catch (error) {
175
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'
176
- throw error
177
- }
178
- if (evictStat.mtimeMs + EVICT_TTL > now()) return 'fresh'
179
-
180
- if (options.onStep) await options.onStep('stale-marker-stat')
181
- const claimedPath = `${evictPath}.${randomUUID()}`
182
- try {
183
- await rename(evictPath, claimedPath)
184
- } catch (error) {
185
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'
186
- throw error
187
- }
188
- if (options.onStep) await options.onStep('stale-marker-claimed')
189
- await rm(claimedPath, { recursive: true, force: true }).catch(() => {})
190
- return 'recovered'
191
- }
192
-
193
- async function withEvictionMarker(action: () => Promise<void>) {
194
- try {
195
- if (!(await tryAcquireEvictionMarker())) return false
196
- } catch (error) {
197
- if ((error as NodeJS.ErrnoException).code === 'EEXIST') return false
198
- throw error
199
- }
200
-
201
- try {
202
- await action()
203
- } finally {
204
- await releaseEvictionMarker()
205
- }
206
- return true
207
- }
208
-
209
- // Marker loss after a write may mean our record replaced a successor's.
210
- // Delete only a record still owned by us; a concurrent successor write can
211
- // then yield zero winners, never two.
212
- async function relinquishLockAfterMarkerLoss() {
213
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
214
- if (options.onStep) await options.onStep('relinquish-read')
215
- let owner: { ownerId?: string } | undefined
216
- try {
217
- owner = await readOwner()
218
- } catch {
219
- return
220
- }
221
- if (owner?.ownerId !== ownerId) return
222
- try {
223
- await rm(lockPath, { recursive: true, force: true })
224
- return
225
- } catch {
226
- await backoff()
227
- }
228
- }
229
- }
230
-
231
- function scheduleRenewal() {
232
- if (!options.renew || released) return
233
- const intervalMs =
234
- options.renewIntervalMs ?? Math.max(1_000, Math.floor(options.ttlMs / 3))
235
- renewTimer = setRefreshLockRenewalTimeout(() => {
236
- const renewal = (async () => {
237
- let shouldReschedule = !released
238
- try {
239
- const markerAcquired = await withEvictionMarker(async () => {
240
- const owner = await readOwner()
241
- const currentNow = now()
242
- if (released || owner?.ownerId !== ownerId) {
243
- shouldReschedule = false
244
- return
245
- }
246
- // An expired lease is no longer ours to extend; a contender may
247
- // already be eligible to acquire it.
248
- if (Number(owner?.expiresAt) <= currentNow) {
249
- shouldReschedule = false
250
- return
251
- }
252
- if (options.onStep) await options.onStep('renewal-owner-confirmed')
253
- if (released) {
254
- shouldReschedule = false
255
- return
256
- }
257
- if (!(await ownsEvictionMarker())) return
258
- if (options.onStep) await options.onStep('renewal-write-fenced')
259
- if (released) {
260
- shouldReschedule = false
261
- return
262
- }
263
- if (!(await ownsEvictionMarker())) return
264
- if (options.onStep) await options.onStep('renewal-write-ready')
265
- await writeOwner()
266
- if (!(await ownsEvictionMarker())) {
267
- // Marker read errors fail closed: prompt relinquish avoids ambiguity
268
- // instead of waiting for TTL; both outcomes keep zero or one winner.
269
- shouldReschedule = false
270
- await relinquishLockAfterMarkerLoss()
271
- return
272
- }
273
- })
274
- if (!markerAcquired && options.onStep) {
275
- await options.onStep('renewal-marker-unavailable')
276
- }
277
- } catch {
278
- // Transient marker and filesystem failures retry on the next interval.
279
- } finally {
280
- if (options.onStep) {
281
- try {
282
- await options.onStep('renewal-finished')
283
- } catch {
284
- // Test seams must not turn an otherwise-safe renewal into a rejection.
285
- }
286
- }
287
- if (shouldReschedule && !released) scheduleRenewal()
288
- }
289
- })()
290
- renewalInFlight = renewal
291
- void renewal.finally(() => {
292
- if (renewalInFlight === renewal) renewalInFlight = null
293
- })
294
- }, intervalMs)
295
- if ('unref' in renewTimer) renewTimer.unref()
296
- }
297
-
298
- let acquired = await tryAcquire()
299
- if (!acquired) {
300
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
301
- acquired = await tryAcquire()
302
- if (acquired) break
303
- if (await lockIsLive()) return null
304
-
305
- try {
306
- if (!(await tryAcquireEvictionMarker())) {
307
- await backoff()
308
- continue
309
- }
310
- } catch (evictError) {
311
- const code = (evictError as NodeJS.ErrnoException).code
312
- if (code !== 'EEXIST') throw evictError
313
-
314
- const recovered = await recoverStaleEvictionMarker()
315
- if (recovered === 'fresh') return null
316
- await backoff()
317
- continue
318
- }
319
-
320
- try {
321
- if (await lockIsLive()) return null
322
- // Fence check 1: verify we still own the marker before acting on the
323
- // stale-lock-confirmed decision.
324
- if (!(await ownsEvictionMarker())) return null
325
- if (options.onStep) await options.onStep('stale-lock-confirmed')
326
- // Fence check 2: re-verify ownership after the seam (another contender
327
- // may have renamed our fresh marker while we were paused here).
328
- if (!(await ownsEvictionMarker())) return null
329
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
330
- // Fence check 3: re-verify ownership after removing the stale lock.
331
- if (!(await ownsEvictionMarker())) return null
332
- acquired = await tryAcquire()
333
- if (!acquired) return null
334
- // Fence check 4: re-verify ownership after acquiring the lock. If the
335
- // marker was stolen between tryAcquire and this check, release the
336
- // just-acquired lock and return null (fail-closed).
337
- if (!(await ownsEvictionMarker())) {
338
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
339
- acquired = false
340
- return null
341
- }
342
- break
343
- } finally {
344
- await releaseEvictionMarker()
345
- }
346
- }
347
- }
348
-
349
- if (!acquired) return null
350
-
351
- scheduleRenewal()
352
-
353
- return {
354
- release: async () => {
355
- released = true
356
- if (renewTimer) {
357
- clearRefreshLockRenewalTimeout(renewTimer)
358
- renewTimer = null
359
- }
360
- await renewalInFlight
361
- for (let attempt = 0; attempt < MAX_STEAL_ATTEMPTS; attempt++) {
362
- try {
363
- const markerAcquired = await withEvictionMarker(async () => {
364
- const owner = await readOwner()
365
- if (owner?.ownerId !== ownerId) return
366
- if (options.onStep) await options.onStep('release-owner-confirmed')
367
- if (!(await ownsEvictionMarker())) return
368
- await rm(lockPath, { recursive: true, force: true }).catch(() => {})
369
- })
370
- if (markerAcquired) return
371
- await recoverStaleEvictionMarker()
372
- } catch {
373
- return
374
- }
375
- await backoff()
376
- }
377
- // Do not delete by pathname without the marker: bounded retries leave the
378
- // lease to expire rather than risking removal of a successor's lock.
379
- },
380
- }
381
- }
@@ -1,196 +0,0 @@
1
- import {
2
- appendFileSync,
3
- chmodSync,
4
- existsSync,
5
- renameSync,
6
- statSync,
7
- } from 'node:fs'
8
- import { tmpdir } from 'node:os'
9
- import { join } from 'node:path'
10
-
11
- type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace'
12
- const ORDER: Record<Level, number> = {
13
- error: 0,
14
- warn: 1,
15
- info: 2,
16
- debug: 3,
17
- trace: 4,
18
- }
19
- const isTestEnv = process.env.NODE_ENV === 'test'
20
- const MAX_BYTES = 5 * 1024 * 1024
21
-
22
- function logFile(): string {
23
- return (
24
- process.env.OPENCODE_OPENAI_AUTH_LOG_FILE ??
25
- join(tmpdir(), 'opencode-openai-auth.log')
26
- )
27
- }
28
- let runtimeLevel: Level | undefined
29
- export function setLogLevel(l: Level | undefined) {
30
- if (l === undefined || l in ORDER) runtimeLevel = l
31
- }
32
- function configuredLevel(): Level {
33
- if (runtimeLevel) return runtimeLevel
34
- const env = process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL as Level | undefined
35
- if (env && env in ORDER) return env
36
- return 'info'
37
- }
38
-
39
- const SECRET_KEY_EXACT =
40
- /^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i
41
- function isSecretKey(key: string): boolean {
42
- if (SECRET_KEY_EXACT.test(key)) return true
43
- const k = key.toLowerCase().replace(/[-_]/g, '')
44
- // Only the unambiguous ChatGPT stable id (chatgpt-account-id / chatgptAccountId)
45
- // is sensitive. A bare `accountId` field is overloaded — most diagnostic logs put
46
- // the INTERNAL account id/key ('main' or a fallback id) there, which is safe and
47
- // needed for debugging — so it is intentionally NOT redacted by name. The genuine
48
- // ChatGPT id is kept out of logs at the source instead.
49
- if (k === 'chatgptaccountid') return true
50
- if (k.includes('apikey')) return true
51
- if (k.endsWith('secret') || k.endsWith('password')) return true
52
- if (k.endsWith('token') && !k.endsWith('tokens')) return true
53
- return false
54
- }
55
- const TOKEN_VALUE = /\b(Bearer\s+[\w.-]+|sk-[\w-]+|eyJ[\w.-]+)\b/g
56
- export function redact(value: unknown): unknown {
57
- return redactInner(value, new WeakSet<object>())
58
- }
59
-
60
- /**
61
- * Scrub credential-shaped strings without redacting by key name.
62
- *
63
- * For regions where a key names a piece of data rather than holding a secret —
64
- * a JSON Schema property called `api_key` describes an argument, it does not
65
- * carry one — replacing the node by name destroys the structure while removing
66
- * nothing sensitive. A credential pasted into a description is still caught,
67
- * because that is a string.
68
- */
69
- export function redactStrings(value: unknown): unknown {
70
- return redactStringsInner(value, new WeakSet<object>())
71
- }
72
-
73
- function redactStringsInner(value: unknown, seen: WeakSet<object>): unknown {
74
- if (typeof value === 'string')
75
- return value.replace(TOKEN_VALUE, '***REDACTED***')
76
- if (Array.isArray(value)) {
77
- if (seen.has(value)) return '[Circular]'
78
- seen.add(value)
79
- const arr = value.map((v) => redactStringsInner(v, seen))
80
- seen.delete(value)
81
- return arr
82
- }
83
- if (value && typeof value === 'object') {
84
- if (seen.has(value)) return '[Circular]'
85
- seen.add(value)
86
- const out: Record<string, unknown> = {}
87
- for (const [k, v] of Object.entries(value))
88
- out[k] = redactStringsInner(v, seen)
89
- seen.delete(value)
90
- return out
91
- }
92
- return value
93
- }
94
- function redactInner(value: unknown, seen: WeakSet<object>): unknown {
95
- if (typeof value === 'string')
96
- return value.replace(TOKEN_VALUE, '***REDACTED***')
97
- if (Array.isArray(value)) {
98
- if (seen.has(value)) return '[Circular]'
99
- seen.add(value)
100
- const arr = value.map((v) => redactInner(v, seen))
101
- seen.delete(value)
102
- return arr
103
- }
104
- if (value && typeof value === 'object') {
105
- if (seen.has(value)) return '[Circular]'
106
- seen.add(value)
107
- const out: Record<string, unknown> = {}
108
- for (const [k, v] of Object.entries(value))
109
- out[k] = isSecretKey(k) ? '***REDACTED***' : redactInner(v, seen)
110
- seen.delete(value)
111
- return out
112
- }
113
- return value
114
- }
115
-
116
- let buffer: string[] = []
117
- let timer: ReturnType<typeof setTimeout> | undefined
118
- const ROTATE_KEEP = 3
119
- function chmodPrivate(path: string) {
120
- try {
121
- chmodSync(path, 0o600)
122
- } catch {
123
- /* never throw */
124
- }
125
- }
126
- function rotateIfNeeded() {
127
- try {
128
- const f = logFile()
129
- if (!(existsSync(f) && statSync(f).size > MAX_BYTES)) return
130
- for (let i = ROTATE_KEEP - 1; i >= 1; i--) {
131
- if (existsSync(`${f}.${i}`)) {
132
- const rotated = `${f}.${i + 1}`
133
- renameSync(`${f}.${i}`, rotated)
134
- chmodPrivate(rotated)
135
- }
136
- }
137
- const rotated = `${f}.1`
138
- renameSync(f, rotated)
139
- chmodPrivate(rotated)
140
- } catch {
141
- /* never throw */
142
- }
143
- }
144
- function flush() {
145
- if (!buffer.length) return
146
- const text = buffer.join('')
147
- buffer = []
148
- try {
149
- rotateIfNeeded()
150
- const file = logFile()
151
- if (existsSync(file)) chmodPrivate(file)
152
- appendFileSync(file, text, { encoding: 'utf8', mode: 0o600 })
153
- } catch {
154
- /* never throw */
155
- }
156
- }
157
- function schedule() {
158
- if (!timer)
159
- timer = setTimeout(() => {
160
- timer = undefined
161
- flush()
162
- }, 500)
163
- }
164
- if (!isTestEnv) process.on('exit', flush)
165
-
166
- function safeSerialize(data: unknown): string {
167
- try {
168
- return ` ${JSON.stringify(redact(data))}`
169
- } catch {
170
- return ' [unserializable]'
171
- }
172
- }
173
-
174
- function emit(channel: string, level: Level, message: string, data?: unknown) {
175
- if (ORDER[level] > ORDER[configuredLevel()]) return
176
- const line =
177
- `[${new Date().toISOString()}] ${level.toUpperCase()} [${channel}] ${message}` +
178
- (data === undefined ? '' : safeSerialize(data)) +
179
- '\n'
180
- buffer.push(line)
181
- if (buffer.length >= 50) flush()
182
- else schedule()
183
- }
184
-
185
- export function createLogger(channel: string) {
186
- return {
187
- error: (m: string, d?: unknown) => emit(channel, 'error', m, d),
188
- warn: (m: string, d?: unknown) => emit(channel, 'warn', m, d),
189
- info: (m: string, d?: unknown) => emit(channel, 'info', m, d),
190
- debug: (m: string, d?: unknown) => emit(channel, 'debug', m, d),
191
- trace: (m: string, d?: unknown) => emit(channel, 'trace', m, d),
192
- }
193
- }
194
- export async function flushForTest() {
195
- flush()
196
- }
@@ -1,33 +0,0 @@
1
- export type CommandModalName =
2
- | 'openai-quota'
3
- | 'openai-account'
4
- | 'openai-routing'
5
- | 'openai-killswitch'
6
- | 'openai-dump'
7
- | 'openai-logging'
8
- | 'openai-cachekeep'
9
- | 'openai-reset'
10
-
11
- export interface OpenDialogPayload {
12
- command: CommandModalName
13
- text: string
14
- knobs: Record<string, unknown>
15
- }
16
-
17
- export interface RpcNotification {
18
- id: number
19
- type: 'open-dialog'
20
- payload: OpenDialogPayload
21
- sessionId?: string
22
- }
23
-
24
- export interface ApplyRequest {
25
- command: CommandModalName
26
- arguments: string
27
- sessionId?: string
28
- }
29
-
30
- export interface ApplyResult {
31
- text: string
32
- knobs: Record<string, unknown>
33
- }
package/src/util/error.ts DELETED
@@ -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
- }
@@ -1,42 +0,0 @@
1
- function proxyEnv(name: string) {
2
- return process.env[name] ?? process.env[name.toLowerCase()]
3
- }
4
-
5
- function shouldBypassProxy(url: URL): boolean {
6
- const noProxy = proxyEnv('NO_PROXY')
7
- if (!noProxy) return false
8
-
9
- const host = url.hostname.toLowerCase()
10
- const entries = noProxy
11
- .split(',')
12
- .map((s) => s.trim().toLowerCase())
13
- .filter(Boolean)
14
-
15
- for (const entry of entries) {
16
- if (entry === '*') return true
17
-
18
- if (entry.startsWith('.')) {
19
- if (host === entry.slice(1) || host.endsWith(entry)) {
20
- return true
21
- }
22
- } else {
23
- if (host === entry || host.endsWith(`.${entry}`)) {
24
- return true
25
- }
26
- }
27
- }
28
-
29
- return false
30
- }
31
-
32
- export function getProxyForUrl(url: string) {
33
- const parsed = new URL(url)
34
- if (shouldBypassProxy(parsed)) return undefined
35
-
36
- if (parsed.protocol === 'http:') return proxyEnv('HTTP_PROXY')
37
- if (parsed.protocol === 'https:')
38
- return proxyEnv('HTTPS_PROXY') ?? proxyEnv('HTTP_PROXY')
39
- return undefined
40
- }
41
-
42
- export const ProxyEnv = { getProxyForUrl }
@@ -1,3 +0,0 @@
1
- export function isRecord(input: unknown): input is Record<string, unknown> {
2
- return typeof input === 'object' && input !== null && !Array.isArray(input)
3
- }