@cortexkit/opencode-anthropic-auth 1.19.0 → 1.20.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 (40) hide show
  1. package/README.md +121 -8
  2. package/dist/cache-diagnostics.d.ts +79 -0
  3. package/dist/cli.js +2 -2
  4. package/dist/index-afyr2g77.js +2 -0
  5. package/dist/index-ebq5g321.js +427 -0
  6. package/dist/{index-k5enth8h.js → index-hmebes3f.js} +1 -1
  7. package/dist/index-j3323dyj.js +2 -0
  8. package/dist/{index-kckdabb4.js → index-jjx6dbj3.js} +1 -1
  9. package/dist/{index-9aeag0wm.js → index-nqs7chg0.js} +1 -1
  10. package/dist/{index-1ets4bf8.js → index-vkecgsm3.js} +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +22 -16
  13. package/dist/lane-start.d.ts +17 -0
  14. package/dist/{logger-whpn7rjx.js → logger-8b9bz0g8.js} +1 -1
  15. package/dist/prime-manager-registry.d.ts +7 -0
  16. package/dist/rpc/port-file.js +1 -1
  17. package/dist/rpc/protocol.d.ts +2 -1
  18. package/dist/rpc/protocol.js +1 -0
  19. package/dist/rpc/rpc-client.js +1 -1
  20. package/dist/rpc/rpc-dir.js +1 -1
  21. package/dist/server-fallback.d.ts +3 -0
  22. package/dist/sidebar-state.d.ts +26 -1
  23. package/dist/sidebar-state.js +1 -1
  24. package/dist/transform.d.ts +12 -1
  25. package/dist/tui/command-dialogs.d.ts +16 -1
  26. package/dist/tui-preferences.d.ts +4 -1
  27. package/dist/tui-preferences.js +2 -2
  28. package/package.json +4 -4
  29. package/src/rpc/protocol.ts +15 -10
  30. package/src/sidebar-state.ts +154 -17
  31. package/src/tui/command-dialogs.tsx +112 -0
  32. package/src/tui-compiled/rpc/protocol.ts +15 -10
  33. package/src/tui-compiled/sidebar-state.ts +154 -17
  34. package/src/tui-compiled/tui/command-dialogs.tsx +134 -18
  35. package/src/tui-compiled/tui-preferences.ts +57 -50
  36. package/src/tui-compiled/tui.tsx +43 -1
  37. package/src/tui-preferences.ts +57 -50
  38. package/src/tui.tsx +28 -0
  39. package/dist/index-h0pmqm96.js +0 -422
  40. package/dist/index-szybnfvk.js +0 -2
@@ -1,4 +1,4 @@
1
- import { watch } from 'node:fs'
1
+ import { readFileSync, watch } from 'node:fs'
2
2
  import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
3
3
  import { homedir } from 'node:os'
4
4
  import { basename, dirname, join } from 'node:path'
@@ -292,12 +292,22 @@ const WATCH_POLL_MS = 100
292
292
  // events, and against mtime granularity.
293
293
  //
294
294
  // Returns a disposer; never throws.
295
- export function watchTuiPreferences(onChange: () => void): () => void {
295
+ export function watchTuiPreferences(
296
+ onChange: () => void,
297
+ options: { watchDirectory?: typeof watch } = {},
298
+ ): () => void {
296
299
  const file = getTuiPreferencesFile()
297
300
  const name = basename(file)
298
301
  let timer: ReturnType<typeof nativeSetTimeout> | null = null
302
+ // Capture the baseline before returning. An asynchronous seed can resolve
303
+ // after the caller's first write and absorb that change as already seen.
299
304
  let lastSeen: string | null = null
300
- let observedEvent = false
305
+ try {
306
+ lastSeen = readFileSync(file, 'utf8')
307
+ } catch {
308
+ // Missing/unreadable files can still be created later and detected by the
309
+ // directory watcher or polling fallback.
310
+ }
301
311
  let disposed = false
302
312
 
303
313
  const checkForChange = async () => {
@@ -309,7 +319,6 @@ export function watchTuiPreferences(onChange: () => void): () => void {
309
319
  }
310
320
 
311
321
  const scheduleCheck = () => {
312
- observedEvent = true
313
322
  if (timer) nativeClearTimeout(timer)
314
323
  timer = nativeSetTimeout(() => {
315
324
  timer = null
@@ -317,54 +326,52 @@ export function watchTuiPreferences(onChange: () => void): () => void {
317
326
  }, WATCH_DEBOUNCE_MS)
318
327
  }
319
328
 
320
- // Seed asynchronously. If a real file event arrives before the read resolves,
321
- // do not let the seed overwrite that pending change; the debounced re-read
322
- // will compare against the previous value and fire.
323
- void readFile(file, 'utf8')
324
- .then((text) => {
325
- if (!observedEvent && lastSeen === null) lastSeen = text
326
- })
327
- .catch(() => {})
328
-
329
+ let watcher: ReturnType<typeof watch> | null = null
329
330
  try {
330
- const watcher = watch(dirname(file), (_event, filename) => {
331
- // Exact match against the preferences file name, plus the temp file
332
- // we use for atomic writes (carrying our pid + `.tmp`).
333
- const isOurs =
334
- filename === name ||
335
- (filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
336
- if (filename != null && !isOurs) return
337
- scheduleCheck()
338
- })
331
+ watcher = (options.watchDirectory ?? watch)(
332
+ dirname(file),
333
+ (_event, filename) => {
334
+ // Exact match against the preferences file name, plus the temp file
335
+ // we use for atomic writes (carrying our pid + `.tmp`).
336
+ const isOurs =
337
+ filename === name ||
338
+ (filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
339
+ if (filename != null && !isOurs) return
340
+ scheduleCheck()
341
+ },
342
+ )
343
+ } catch {
344
+ // If the file exists, polling remains a complete fallback even when Bun
345
+ // cannot allocate a directory watcher (for example, transient EBADF).
346
+ // If neither the file nor its directory can be observed, remain a no-op.
347
+ if (lastSeen === null) return () => {}
348
+ }
339
349
 
340
- // Bun's fs.watch can miss atomic temp-file rename updates on some
341
- // backends, so keep a low-cost polling loop as the correctness path.
342
- // Use recursive timers instead of fs.watchFile because watchFile can keep
343
- // Bun test processes alive long after unwatchFile().
344
- let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
345
- let pollInFlight = false
346
- const schedulePoll = () => {
347
- if (disposed || pollTimer) return
348
- pollTimer = nativeSetTimeout(() => {
349
- pollTimer = null
350
- if (disposed || pollInFlight) return
351
- pollInFlight = true
352
- void checkForChange().finally(() => {
353
- pollInFlight = false
354
- schedulePoll()
355
- })
356
- }, WATCH_POLL_MS)
357
- pollTimer.unref?.()
358
- }
359
- schedulePoll()
350
+ // Bun's fs.watch can miss atomic temp-file renames and can itself fail after
351
+ // repeated close/recreate cycles, so polling must be independent of watcher
352
+ // construction. Recursive unreferenced timers avoid fs.watchFile's lingering
353
+ // handles while the host's normal event loop keeps active watchers polling.
354
+ let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
355
+ let pollInFlight = false
356
+ const schedulePoll = () => {
357
+ if (disposed || pollTimer) return
358
+ pollTimer = nativeSetTimeout(() => {
359
+ pollTimer = null
360
+ if (disposed || pollInFlight) return
361
+ pollInFlight = true
362
+ void checkForChange().finally(() => {
363
+ pollInFlight = false
364
+ schedulePoll()
365
+ })
366
+ }, WATCH_POLL_MS)
367
+ pollTimer.unref?.()
368
+ }
369
+ schedulePoll()
360
370
 
361
- return () => {
362
- disposed = true
363
- if (timer) nativeClearTimeout(timer)
364
- if (pollTimer) nativeClearTimeout(pollTimer)
365
- watcher.close()
366
- }
367
- } catch {
368
- return () => {}
371
+ return () => {
372
+ disposed = true
373
+ if (timer) nativeClearTimeout(timer)
374
+ if (pollTimer) nativeClearTimeout(pollTimer)
375
+ watcher?.close()
369
376
  }
370
377
  }
@@ -15,7 +15,7 @@ import { formatQuotaMoney } from "@cortexkit/anthropic-auth-core";
15
15
  import { createEffect, createSignal, For, onCleanup, Show } from "opentui:runtime-module:solid-js";
16
16
  import { createRpcClient } from "./rpc/rpc-client.js";
17
17
  import { getRpcDir } from "./rpc/rpc-dir.js";
18
- import { computeQuotaPacing, DEFAULT_SIDEBAR_STATE, FIVE_HOUR_MS, formatScopedQuotaLabel, getCollapsedQuotaSummary, getFableRecoverySummary, getSidebarState, resolveActiveAccount, SEVEN_DAY_MS } from "./sidebar-state.js";
18
+ import { computeQuotaPacing, DEFAULT_SIDEBAR_STATE, FIVE_HOUR_MS, formatPrimeAccountValue, formatScopedQuotaLabel, getCollapsedQuotaSummary, getFableRecoverySummary, getSidebarState, resolveActiveAccount, SEVEN_DAY_MS } from "./sidebar-state.js";
19
19
  import { openCommandDialog } from "./tui/command-dialogs.js";
20
20
  import { computeEffectiveOrder, DEFAULT_SLOT_ORDER, PLUGIN_KEY, queueTuiPreferenceUpdate, readTuiPreferencesFile, resolveAnthropicAuthPrefs, watchTuiPreferences } from "./tui-preferences.js";
21
21
  const RPC_POLL_MS = 500;
@@ -685,6 +685,9 @@ function QuotaSidebar(props) {
685
685
  const fableRecoveryTone = () => state().fableRecoveries?.find(recovery => recovery.sessionId === props.sessionId)?.mode === 'fable' ? 'ok' : 'warn';
686
686
  const cacheKeep = () => state().cacheKeep;
687
687
  const showCache = () => prefs().sections.cache && cacheKeep() != null && cacheKeep()?.window != null;
688
+ const prime = () => state().prime;
689
+ const showPrime = () => prime() != null;
690
+ const primeHasError = () => prime()?.accounts.some(account => account.lastResult === 'error') ?? false;
688
691
  const relayValue = () => {
689
692
  const r = state().relay;
690
693
  if (!r) return '\u2014';
@@ -1024,6 +1027,45 @@ function QuotaSidebar(props) {
1024
1027
  }
1025
1028
  })];
1026
1029
  }
1030
+ }), _$createComponent(Show, {
1031
+ get when() {
1032
+ return showPrime();
1033
+ },
1034
+ get children() {
1035
+ return [_$createComponent(StatRow, {
1036
+ get theme() {
1037
+ return theme();
1038
+ },
1039
+ label: "Prime",
1040
+ get value() {
1041
+ return primeHasError() ? 'err' : 'on';
1042
+ },
1043
+ get tone() {
1044
+ return primeHasError() ? 'warn' : 'ok';
1045
+ }
1046
+ }), _$createComponent(For, {
1047
+ get each() {
1048
+ return prime()?.accounts ?? [];
1049
+ },
1050
+ children: account => {
1051
+ const value = () => formatPrimeAccountValue(account);
1052
+ return _$createComponent(StatRow, {
1053
+ get theme() {
1054
+ return theme();
1055
+ },
1056
+ get label() {
1057
+ return account.label;
1058
+ },
1059
+ get value() {
1060
+ return value().text;
1061
+ },
1062
+ get tone() {
1063
+ return value().hasError ? 'warn' : 'text';
1064
+ }
1065
+ });
1066
+ }
1067
+ })];
1068
+ }
1027
1069
  }), _$createComponent(Show, {
1028
1070
  get when() {
1029
1071
  return _$memo(() => !!degraded())() && prefs().sections.health;
@@ -1,4 +1,4 @@
1
- import { watch } from 'node:fs'
1
+ import { readFileSync, watch } from 'node:fs'
2
2
  import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
3
3
  import { homedir } from 'node:os'
4
4
  import { basename, dirname, join } from 'node:path'
@@ -292,12 +292,22 @@ const WATCH_POLL_MS = 100
292
292
  // events, and against mtime granularity.
293
293
  //
294
294
  // Returns a disposer; never throws.
295
- export function watchTuiPreferences(onChange: () => void): () => void {
295
+ export function watchTuiPreferences(
296
+ onChange: () => void,
297
+ options: { watchDirectory?: typeof watch } = {},
298
+ ): () => void {
296
299
  const file = getTuiPreferencesFile()
297
300
  const name = basename(file)
298
301
  let timer: ReturnType<typeof nativeSetTimeout> | null = null
302
+ // Capture the baseline before returning. An asynchronous seed can resolve
303
+ // after the caller's first write and absorb that change as already seen.
299
304
  let lastSeen: string | null = null
300
- let observedEvent = false
305
+ try {
306
+ lastSeen = readFileSync(file, 'utf8')
307
+ } catch {
308
+ // Missing/unreadable files can still be created later and detected by the
309
+ // directory watcher or polling fallback.
310
+ }
301
311
  let disposed = false
302
312
 
303
313
  const checkForChange = async () => {
@@ -309,7 +319,6 @@ export function watchTuiPreferences(onChange: () => void): () => void {
309
319
  }
310
320
 
311
321
  const scheduleCheck = () => {
312
- observedEvent = true
313
322
  if (timer) nativeClearTimeout(timer)
314
323
  timer = nativeSetTimeout(() => {
315
324
  timer = null
@@ -317,54 +326,52 @@ export function watchTuiPreferences(onChange: () => void): () => void {
317
326
  }, WATCH_DEBOUNCE_MS)
318
327
  }
319
328
 
320
- // Seed asynchronously. If a real file event arrives before the read resolves,
321
- // do not let the seed overwrite that pending change; the debounced re-read
322
- // will compare against the previous value and fire.
323
- void readFile(file, 'utf8')
324
- .then((text) => {
325
- if (!observedEvent && lastSeen === null) lastSeen = text
326
- })
327
- .catch(() => {})
328
-
329
+ let watcher: ReturnType<typeof watch> | null = null
329
330
  try {
330
- const watcher = watch(dirname(file), (_event, filename) => {
331
- // Exact match against the preferences file name, plus the temp file
332
- // we use for atomic writes (carrying our pid + `.tmp`).
333
- const isOurs =
334
- filename === name ||
335
- (filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
336
- if (filename != null && !isOurs) return
337
- scheduleCheck()
338
- })
331
+ watcher = (options.watchDirectory ?? watch)(
332
+ dirname(file),
333
+ (_event, filename) => {
334
+ // Exact match against the preferences file name, plus the temp file
335
+ // we use for atomic writes (carrying our pid + `.tmp`).
336
+ const isOurs =
337
+ filename === name ||
338
+ (filename?.startsWith(`${name}.`) && filename.endsWith('.tmp'))
339
+ if (filename != null && !isOurs) return
340
+ scheduleCheck()
341
+ },
342
+ )
343
+ } catch {
344
+ // If the file exists, polling remains a complete fallback even when Bun
345
+ // cannot allocate a directory watcher (for example, transient EBADF).
346
+ // If neither the file nor its directory can be observed, remain a no-op.
347
+ if (lastSeen === null) return () => {}
348
+ }
339
349
 
340
- // Bun's fs.watch can miss atomic temp-file rename updates on some
341
- // backends, so keep a low-cost polling loop as the correctness path.
342
- // Use recursive timers instead of fs.watchFile because watchFile can keep
343
- // Bun test processes alive long after unwatchFile().
344
- let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
345
- let pollInFlight = false
346
- const schedulePoll = () => {
347
- if (disposed || pollTimer) return
348
- pollTimer = nativeSetTimeout(() => {
349
- pollTimer = null
350
- if (disposed || pollInFlight) return
351
- pollInFlight = true
352
- void checkForChange().finally(() => {
353
- pollInFlight = false
354
- schedulePoll()
355
- })
356
- }, WATCH_POLL_MS)
357
- pollTimer.unref?.()
358
- }
359
- schedulePoll()
350
+ // Bun's fs.watch can miss atomic temp-file renames and can itself fail after
351
+ // repeated close/recreate cycles, so polling must be independent of watcher
352
+ // construction. Recursive unreferenced timers avoid fs.watchFile's lingering
353
+ // handles while the host's normal event loop keeps active watchers polling.
354
+ let pollTimer: ReturnType<typeof nativeSetTimeout> | null = null
355
+ let pollInFlight = false
356
+ const schedulePoll = () => {
357
+ if (disposed || pollTimer) return
358
+ pollTimer = nativeSetTimeout(() => {
359
+ pollTimer = null
360
+ if (disposed || pollInFlight) return
361
+ pollInFlight = true
362
+ void checkForChange().finally(() => {
363
+ pollInFlight = false
364
+ schedulePoll()
365
+ })
366
+ }, WATCH_POLL_MS)
367
+ pollTimer.unref?.()
368
+ }
369
+ schedulePoll()
360
370
 
361
- return () => {
362
- disposed = true
363
- if (timer) nativeClearTimeout(timer)
364
- if (pollTimer) nativeClearTimeout(pollTimer)
365
- watcher.close()
366
- }
367
- } catch {
368
- return () => {}
371
+ return () => {
372
+ disposed = true
373
+ if (timer) nativeClearTimeout(timer)
374
+ if (pollTimer) nativeClearTimeout(pollTimer)
375
+ watcher?.close()
369
376
  }
370
377
  }
package/src/tui.tsx CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  computeQuotaPacing,
25
25
  DEFAULT_SIDEBAR_STATE,
26
26
  FIVE_HOUR_MS,
27
+ formatPrimeAccountValue,
27
28
  formatScopedQuotaLabel,
28
29
  getCollapsedQuotaSummary,
29
30
  getFableRecoverySummary,
@@ -650,6 +651,10 @@ function QuotaSidebar(props: {
650
651
  const cacheKeep = () => state().cacheKeep
651
652
  const showCache = () =>
652
653
  prefs().sections.cache && cacheKeep() != null && cacheKeep()?.window != null
654
+ const prime = () => state().prime
655
+ const showPrime = () => prime() != null
656
+ const primeHasError = () =>
657
+ prime()?.accounts.some((account) => account.lastResult === 'error') ?? false
653
658
  const relayValue = () => {
654
659
  const r = state().relay
655
660
  if (!r) return '\u2014'
@@ -829,6 +834,29 @@ function QuotaSidebar(props: {
829
834
  </Show>
830
835
  </Show>
831
836
 
837
+ {/* Prime — only when enabled. Expanded view only (no collapsed surface). */}
838
+ <Show when={showPrime()}>
839
+ <StatRow
840
+ theme={theme()}
841
+ label='Prime'
842
+ value={primeHasError() ? 'err' : 'on'}
843
+ tone={primeHasError() ? 'warn' : 'ok'}
844
+ />
845
+ <For each={prime()?.accounts ?? []}>
846
+ {(account) => {
847
+ const value = () => formatPrimeAccountValue(account)
848
+ return (
849
+ <StatRow
850
+ theme={theme()}
851
+ label={account.label}
852
+ value={value().text}
853
+ tone={value().hasError ? 'warn' : 'text'}
854
+ />
855
+ )
856
+ }}
857
+ </For>
858
+ </Show>
859
+
832
860
  {/* Health — only when something is wrong */}
833
861
  <Show when={degraded() && prefs().sections.health}>
834
862
  <SectionHeader theme={theme()} title='Health' />