@cortexkit/opencode-anthropic-auth 1.19.0 → 1.19.1
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.
- package/README.md +8 -6
- package/dist/cli.js +1 -1
- package/dist/{index-k5enth8h.js → index-075t8qec.js} +1 -1
- package/dist/{index-kckdabb4.js → index-2k47hc8b.js} +1 -1
- package/dist/{index-szybnfvk.js → index-a13w4gm9.js} +1 -1
- package/dist/{index-1ets4bf8.js → index-dex8fs0m.js} +1 -1
- package/dist/{index-9aeag0wm.js → index-dzs7w297.js} +1 -1
- package/dist/index-vwgzgh5x.js +422 -0
- package/dist/index.js +15 -13
- package/dist/{logger-whpn7rjx.js → logger-n49md99d.js} +1 -1
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/server-fallback.d.ts +1 -0
- package/dist/sidebar-state.js +1 -1
- package/dist/transform.d.ts +3 -1
- package/dist/tui-preferences.d.ts +4 -1
- package/dist/tui-preferences.js +2 -2
- package/package.json +2 -2
- package/src/tui-compiled/tui-preferences.ts +57 -50
- package/src/tui-preferences.ts +57 -50
- package/dist/index-h0pmqm96.js +0 -422
package/dist/sidebar-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r}from"./index-
|
|
1
|
+
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r}from"./index-a13w4gm9.js";import"./index-vwgzgh5x.js";import"./index-dex8fs0m.js";import"./index-dzs7w297.js";export{i as setSidebarState,k as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,h as getSidebarState,f as getInitialSidebarRoutingTestHooks,n as getFableRecoverySummary,o as getCollapsedQuotaSummary,l as formatScopedQuotaLabel,m as formatFallbackModelLabel,j as drainSidebarWrites,r as computeQuotaPacing,d as __setSidebarStateWriteTestHooks,e as __setInitialSidebarRoutingTestHooks,g as __acquireSidebarStateLockForTest,q as SEVEN_DAY_MS,p as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
|
package/dist/transform.d.ts
CHANGED
|
@@ -92,7 +92,9 @@ export declare function rewriteRequestBody(body: string, options?: {
|
|
|
92
92
|
*/
|
|
93
93
|
export declare function createStrippedStream(response: Response, options?: {
|
|
94
94
|
perf?: RewritePerfCallback;
|
|
95
|
-
onContentFilter?: (
|
|
95
|
+
onContentFilter?: (context?: {
|
|
96
|
+
completedToolUse: boolean;
|
|
97
|
+
}) => boolean | undefined;
|
|
96
98
|
onComplete?: (finishReason: string) => void;
|
|
97
99
|
contentFilterModel?: unknown;
|
|
98
100
|
serverSideFallbackModel?: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { watch } from 'node:fs';
|
|
1
2
|
export declare const TUI_PREFS_FILE_ENV = "OPENCODE_TUI_PREFERENCES_FILE";
|
|
2
3
|
export declare function getTuiPreferencesFile(): string;
|
|
3
4
|
export declare function readTuiPreferencesFile(): Promise<Record<string, unknown>>;
|
|
@@ -37,5 +38,7 @@ export declare function resolveAnthropicAuthPrefs(root: Record<string, unknown>)
|
|
|
37
38
|
export declare function computeEffectiveOrder(root: Record<string, unknown>, pluginKey: string, defaultOrder: number): number;
|
|
38
39
|
type JsonValue = string | number | boolean | null;
|
|
39
40
|
export declare function queueTuiPreferenceUpdate(pluginKey: string, path: string[], value: JsonValue): Promise<void>;
|
|
40
|
-
export declare function watchTuiPreferences(onChange: () => void
|
|
41
|
+
export declare function watchTuiPreferences(onChange: () => void, options?: {
|
|
42
|
+
watchDirectory?: typeof watch;
|
|
43
|
+
}): () => void;
|
|
41
44
|
export {};
|
package/dist/tui-preferences.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{tc as c,uc as C,vc as v}from"./index-dzs7w297.js";var m=C((h,k)=>{(function(Q){if(typeof k==="object"&&typeof k.exports==="object"){var J=Q(v,h);if(J!==void 0)k.exports=J}else if(typeof define==="function"&&define.amd)define(["require","exports","./impl/format","./impl/edit","./impl/scanner","./impl/parser"],Q)})(function(Q,J){Object.defineProperty(J,"__esModule",{value:!0}),J.applyEdits=J.modify=J.format=J.printParseErrorCode=J.ParseErrorCode=J.stripComments=J.visit=J.getNodeValue=J.getNodePath=J.findNodeAtOffset=J.findNodeAtLocation=J.parseTree=J.parse=J.getLocation=J.SyntaxKind=J.ScanError=J.createScanner=void 0;let X=Q("./impl/format"),G=Q("./impl/edit"),Z=Q("./impl/scanner"),$=Q("./impl/parser");J.createScanner=Z.createScanner;var V;(function(z){z[z.None=0]="None",z[z.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=2]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",z[z.InvalidUnicode=4]="InvalidUnicode",z[z.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",z[z.InvalidCharacter=6]="InvalidCharacter"})(V||(J.ScanError=V={}));var Y;(function(z){z[z.OpenBraceToken=1]="OpenBraceToken",z[z.CloseBraceToken=2]="CloseBraceToken",z[z.OpenBracketToken=3]="OpenBracketToken",z[z.CloseBracketToken=4]="CloseBracketToken",z[z.CommaToken=5]="CommaToken",z[z.ColonToken=6]="ColonToken",z[z.NullKeyword=7]="NullKeyword",z[z.TrueKeyword=8]="TrueKeyword",z[z.FalseKeyword=9]="FalseKeyword",z[z.StringLiteral=10]="StringLiteral",z[z.NumericLiteral=11]="NumericLiteral",z[z.LineCommentTrivia=12]="LineCommentTrivia",z[z.BlockCommentTrivia=13]="BlockCommentTrivia",z[z.LineBreakTrivia=14]="LineBreakTrivia",z[z.Trivia=15]="Trivia",z[z.Unknown=16]="Unknown",z[z.EOF=17]="EOF"})(Y||(J.SyntaxKind=Y={})),J.getLocation=$.getLocation,J.parse=$.parse,J.parseTree=$.parseTree,J.findNodeAtLocation=$.findNodeAtLocation,J.findNodeAtOffset=$.findNodeAtOffset,J.getNodePath=$.getNodePath,J.getNodeValue=$.getNodeValue,J.visit=$.visit,J.stripComments=$.stripComments;var A;(function(z){z[z.InvalidSymbol=1]="InvalidSymbol",z[z.InvalidNumberFormat=2]="InvalidNumberFormat",z[z.PropertyNameExpected=3]="PropertyNameExpected",z[z.ValueExpected=4]="ValueExpected",z[z.ColonExpected=5]="ColonExpected",z[z.CommaExpected=6]="CommaExpected",z[z.CloseBraceExpected=7]="CloseBraceExpected",z[z.CloseBracketExpected=8]="CloseBracketExpected",z[z.EndOfFileExpected=9]="EndOfFileExpected",z[z.InvalidCommentToken=10]="InvalidCommentToken",z[z.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=12]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",z[z.InvalidUnicode=14]="InvalidUnicode",z[z.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",z[z.InvalidCharacter=16]="InvalidCharacter"})(A||(J.ParseErrorCode=A={}));function L(z){switch(z){case 1:return"InvalidSymbol";case 2:return"InvalidNumberFormat";case 3:return"PropertyNameExpected";case 4:return"ValueExpected";case 5:return"ColonExpected";case 6:return"CommaExpected";case 7:return"CloseBraceExpected";case 8:return"CloseBracketExpected";case 9:return"EndOfFileExpected";case 10:return"InvalidCommentToken";case 11:return"UnexpectedEndOfComment";case 12:return"UnexpectedEndOfString";case 13:return"UnexpectedEndOfNumber";case 14:return"InvalidUnicode";case 15:return"InvalidEscapeCharacter";case 16:return"InvalidCharacter"}return"<unknown ParseErrorCode>"}J.printParseErrorCode=L;function W(z,B,j){return X.format(z,B,j)}J.format=W;function q(z,B,j,R){return G.setProperty(z,B,j,R)}J.modify=q;function O(z,B){let j=B.slice(0).sort((M,N)=>{let T=M.offset-N.offset;if(T===0)return M.length-N.length;return T}),R=z.length;for(let M=j.length-1;M>=0;M--){let N=j[M];if(N.offset+N.length<=R)z=G.applyEdit(z,N);else throw Error("Overlapping edit");R=N.offset}return z}J.applyEdits=O})});var I=c(m(),1);import{readFileSync as y,watch as S}from"node:fs";import{mkdir as E,readFile as g,rename as p,writeFile as l}from"node:fs/promises";import{homedir as x}from"node:os";import{basename as i,dirname as P,join as w}from"node:path";import{clearTimeout as _,setTimeout as f}from"node:timers";var s="OPENCODE_TUI_PREFERENCES_FILE",d="tui-preferences.jsonc";function b(){let Q=process.env[s];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||w(process.env.XDG_CONFIG_HOME||w(x(),".config"),"opencode");return w(J,d)}function U(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Yz(){try{let Q=await g(b(),"utf8"),J=[],X=I.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return U(X)?X:{}}catch{return{}}}var n="anthropic-auth",t=160,u={forceToTop:!1,order:t,startCollapsed:!1,rememberCollapsed:!0,collapsed:null,pollMs:1500,refreshDebounceMs:200,header:{label:"CLAUDE",showVersion:!0},sections:{quota:!0,fallbackAccounts:!0,routing:!0,cache:!0,health:!0,pacing:!0},appearance:{barWidth:10,barFilledChar:"█",barEmptyChar:"░",warnThreshold:50,errorThreshold:80}};function H(Q,J){return typeof Q==="boolean"?Q:J}function D(Q,J,X,G){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),G)}function a(Q,J,X){if(typeof Q!=="string"||Q.length===0)return J;return Q.slice(0,X)}function K(Q,J){if(typeof Q!=="string")return J;return[...Q][0]??J}function Bz(Q){let J=Q[n];if(!U(J))return structuredClone(u);let X=u,G=U(J.header)?J.header:{},Z=U(J.sections)?J.sections:{},$=U(J.appearance)?J.appearance:{},V=D($.warnThreshold,X.appearance.warnThreshold,0,99),Y=Math.max(D($.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(V+1,100));return{forceToTop:H(J.forceToTop,X.forceToTop),order:D(J.order,X.order,-1e4,1e4),startCollapsed:H(J.startCollapsed,X.startCollapsed),rememberCollapsed:H(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:D(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:D(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:a(G.label,X.header.label,20),showVersion:H(G.showVersion,X.header.showVersion)},sections:{quota:H(Z.quota,X.sections.quota),fallbackAccounts:H(Z.fallbackAccounts,X.sections.fallbackAccounts),routing:H(Z.routing,X.sections.routing),cache:H(Z.cache,X.sections.cache),health:H(Z.health,X.sections.health),pacing:H(Z.pacing,X.sections.pacing)},appearance:{barWidth:D($.barWidth,X.appearance.barWidth,4,40),barFilledChar:K($.barFilledChar,X.appearance.barFilledChar),barEmptyChar:K($.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:V,errorThreshold:Y}}}var o=-1e5;function Hz(Q,J,X){let G=Q[J];if(!U(G))return X;if(G.forceToTop===!0)return o+Object.keys(Q).indexOf(J);return D(G.order,X,-1e4,1e4)}var e=`// Shared preferences for opencode TUI plugins.
|
|
2
2
|
// One top-level key per plugin (short name). See each plugin's README for
|
|
3
3
|
// its supported settings. This file is safe to hand-edit; plugins update
|
|
4
4
|
// individual keys in place and preserve comments.
|
|
5
5
|
{}
|
|
6
|
-
`;async function
|
|
6
|
+
`;async function r(Q,J,X){let G=b();await E(P(G),{recursive:!0});let Z;try{Z=await g(G,"utf8")}catch{Z=""}if(Z.trim()==="")Z=e;let $=I.modify(Z,[Q,...J],X,{formattingOptions:{insertSpaces:!0,tabSize:2}}),V=I.applyEdits(Z,$),Y=`${G}.${process.pid}.tmp`;await l(Y,V,"utf8"),await p(Y,G)}var F=Promise.resolve();function Wz(Q,J,X){return F=F.then(()=>r(Q,J,X)).catch(()=>{}),F}var zz=150,Jz=100;function jz(Q,J={}){let X=b(),G=i(X),Z=null,$=null;try{$=y(X,"utf8")}catch{}let V=!1,Y=async()=>{let z=await g(X,"utf8").catch(()=>null);if(V||z===null)return;if(z===$)return;$=z,Q()},A=()=>{if(Z)_(Z);Z=f(()=>{Z=null,Y()},zz)},L=null;try{L=(J.watchDirectory??S)(P(X),(z,B)=>{let j=B===G||B?.startsWith(`${G}.`)&&B.endsWith(".tmp");if(B!=null&&!j)return;A()})}catch{if($===null)return()=>{}}let W=null,q=!1,O=()=>{if(V||W)return;W=f(()=>{if(W=null,V||q)return;q=!0,Y().finally(()=>{q=!1,O()})},Jz),W.unref?.()};return O(),()=>{if(V=!0,Z)_(Z);if(W)_(W);L?.close()}}export{jz as watchTuiPreferences,Bz as resolveAnthropicAuthPrefs,Yz as readTuiPreferencesFile,Wz as queueTuiPreferenceUpdate,b as getTuiPreferencesFile,Hz as computeEffectiveOrder,s as TUI_PREFS_FILE_ENV,n as PLUGIN_KEY,t as DEFAULT_SLOT_ORDER,u as DEFAULT_PREFS};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-anthropic-auth",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@opencode-ai/plugin": "*"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@cortexkit/anthropic-auth-core": "1.19.
|
|
64
|
+
"@cortexkit/anthropic-auth-core": "1.19.1",
|
|
65
65
|
"@opentui/core": "0.4.3",
|
|
66
66
|
"@opentui/solid": "0.4.3",
|
|
67
67
|
"jsonc-parser": "^3.3.1",
|
|
@@ -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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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-preferences.ts
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
}
|