@triflux/core 10.79.0 → 10.80.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/hud/providers/claude.mjs
CHANGED
|
@@ -43,6 +43,18 @@ export function getClaudeEmail() {
|
|
|
43
43
|
return config?.oauthAccount?.emailAddress || null;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export function getClaudeAccount() {
|
|
47
|
+
const config = readJson(
|
|
48
|
+
join(process.env.CLAUDE_CONFIG_DIR || homedir(), ".claude.json"),
|
|
49
|
+
null,
|
|
50
|
+
);
|
|
51
|
+
return (
|
|
52
|
+
config?.oauthAccount?.accountUuid ||
|
|
53
|
+
config?.oauthAccount?.emailAddress ||
|
|
54
|
+
null
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
export const CLAUDE_USAGE_POLL_BASE_MS = 5_000;
|
|
47
59
|
export const CLAUDE_USAGE_POLL_JITTER_RATIO = 0.2;
|
|
48
60
|
const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
@@ -542,6 +554,19 @@ export function readClaudeUsageSnapshot() {
|
|
|
542
554
|
cache?.errorType === "rate_limit" &&
|
|
543
555
|
Number.isFinite(schedule.nextRefreshAt) &&
|
|
544
556
|
Date.now() < schedule.nextRefreshAt;
|
|
557
|
+
// account 가 없는 옛 버전 캐시는 같은 계정으로 본다.
|
|
558
|
+
const accountChanged =
|
|
559
|
+
cache != null && "account" in cache && cache.account !== getClaudeAccount();
|
|
560
|
+
if ((accountChanged || cache?.recheck === true) && !staleBackoffActive) {
|
|
561
|
+
schedule.shouldRefresh = true;
|
|
562
|
+
}
|
|
563
|
+
if (accountChanged || (cache?.recheck === true && !cache.data)) {
|
|
564
|
+
return {
|
|
565
|
+
data: null,
|
|
566
|
+
shouldRefresh: schedule.shouldRefresh,
|
|
567
|
+
isStale: staleBackoffActive,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
545
570
|
|
|
546
571
|
// 1차: 자체 캐시에 유효 데이터가 있는 경우
|
|
547
572
|
if (cache?.data) {
|
|
@@ -621,6 +646,7 @@ export function writeClaudeUsageCache(
|
|
|
621
646
|
data,
|
|
622
647
|
errorInfo = null,
|
|
623
648
|
pollState = null,
|
|
649
|
+
{ account = getClaudeAccount(), recheck = false } = {},
|
|
624
650
|
) {
|
|
625
651
|
const state =
|
|
626
652
|
pollState ||
|
|
@@ -639,6 +665,8 @@ export function writeClaudeUsageCache(
|
|
|
639
665
|
: computeClaudeUsagePollState({ outcome: "success" }));
|
|
640
666
|
const entry = {
|
|
641
667
|
timestamp: Date.now(),
|
|
668
|
+
account,
|
|
669
|
+
recheck,
|
|
642
670
|
data,
|
|
643
671
|
error: !!errorInfo,
|
|
644
672
|
errorType: errorInfo?.type || null, // "rate_limit" | "auth" | "network" | "unknown"
|
|
@@ -648,9 +676,13 @@ export function writeClaudeUsageCache(
|
|
|
648
676
|
nextRefreshAt: Date.now() + state.delayMs,
|
|
649
677
|
};
|
|
650
678
|
// 에러 시 기존 유효 데이터 보존 (--% n/a 방지)
|
|
651
|
-
if (errorInfo
|
|
679
|
+
if (errorInfo) {
|
|
652
680
|
const prev = readJson(CLAUDE_USAGE_CACHE_PATH, null);
|
|
653
|
-
if (prev
|
|
681
|
+
if (prev) {
|
|
682
|
+
entry.account = prev.account;
|
|
683
|
+
entry.recheck = prev.recheck === true;
|
|
684
|
+
}
|
|
685
|
+
if (data == null && prev?.data) {
|
|
654
686
|
entry.data = prev.data;
|
|
655
687
|
entry.stale = true;
|
|
656
688
|
}
|
|
@@ -664,6 +696,7 @@ export async function fetchClaudeUsage(forceRefresh = false) {
|
|
|
664
696
|
return existingSnapshot.data || null;
|
|
665
697
|
}
|
|
666
698
|
const cache = readJson(CLAUDE_USAGE_CACHE_PATH, null);
|
|
699
|
+
const account = getClaudeAccount();
|
|
667
700
|
const consecutive429s = Number.isFinite(cache?.consecutive429s)
|
|
668
701
|
? cache.consecutive429s
|
|
669
702
|
: 0;
|
|
@@ -725,10 +758,26 @@ export async function fetchClaudeUsage(forceRefresh = false) {
|
|
|
725
758
|
const pollState = usage
|
|
726
759
|
? computeClaudeUsagePollState({ outcome: "success" })
|
|
727
760
|
: null;
|
|
761
|
+
const accountChanged = cache?.account !== account;
|
|
762
|
+
const reset =
|
|
763
|
+
!accountChanged &&
|
|
764
|
+
["fiveHour", "weekly"].some((window) => {
|
|
765
|
+
const beforePercent = cache?.data?.[`${window}Percent`];
|
|
766
|
+
const afterPercent = usage?.[`${window}Percent`];
|
|
767
|
+
const beforeReset = Date.parse(cache?.data?.[`${window}ResetsAt`]);
|
|
768
|
+
const afterReset = Date.parse(usage?.[`${window}ResetsAt`]);
|
|
769
|
+
return (
|
|
770
|
+
(beforePercent != null &&
|
|
771
|
+
afterPercent != null &&
|
|
772
|
+
afterPercent < beforePercent) ||
|
|
773
|
+
Math.abs(afterReset - beforeReset) > 60 * 60 * 1000
|
|
774
|
+
);
|
|
775
|
+
});
|
|
728
776
|
writeClaudeUsageCache(
|
|
729
777
|
usage,
|
|
730
778
|
usage ? null : { type: "unknown", status: 0 },
|
|
731
779
|
pollState,
|
|
780
|
+
{ account, recheck: accountChanged || reset },
|
|
732
781
|
);
|
|
733
782
|
return usage;
|
|
734
783
|
}
|
|
@@ -736,11 +785,22 @@ export async function fetchClaudeUsage(forceRefresh = false) {
|
|
|
736
785
|
export function scheduleClaudeUsageRefresh() {
|
|
737
786
|
const scriptPath = process.argv[1];
|
|
738
787
|
if (!scriptPath) return;
|
|
788
|
+
const cache = readJson(CLAUDE_USAGE_CACHE_PATH, null);
|
|
789
|
+
if (
|
|
790
|
+
cache?.errorType === "rate_limit" &&
|
|
791
|
+
!getSnapshotSchedule(cache).shouldRefresh
|
|
792
|
+
)
|
|
793
|
+
return;
|
|
794
|
+
const directRefresh =
|
|
795
|
+
(cache != null &&
|
|
796
|
+
"account" in cache &&
|
|
797
|
+
cache.account !== getClaudeAccount()) ||
|
|
798
|
+
cache?.recheck === true;
|
|
739
799
|
|
|
740
800
|
// OMC 플러그인이 이미 fresh 데이터를 가지고 있으면 HUD 리프레시 불필요 (429 방지)
|
|
741
801
|
try {
|
|
742
802
|
const omcCache = readJson(OMC_PLUGIN_USAGE_CACHE_PATH, null);
|
|
743
|
-
if (omcCache?.data?.fiveHourPercent != null) {
|
|
803
|
+
if (!directRefresh && omcCache?.data?.fiveHourPercent != null) {
|
|
744
804
|
const omcAge = Number.isFinite(omcCache.timestamp)
|
|
745
805
|
? Date.now() - omcCache.timestamp
|
|
746
806
|
: Infinity;
|
|
@@ -49,6 +49,7 @@ export function listProbeTargets({
|
|
|
49
49
|
stateFilePath,
|
|
50
50
|
ttlMs,
|
|
51
51
|
nowMs,
|
|
52
|
+
force = false,
|
|
52
53
|
}) {
|
|
53
54
|
const state = readJsonFile(stateFilePath) || {};
|
|
54
55
|
const due = (key) =>
|
|
@@ -56,7 +57,11 @@ export function listProbeTargets({
|
|
|
56
57
|
const targets = [];
|
|
57
58
|
|
|
58
59
|
const currentAuth = readJsonFile(codexAuthPath);
|
|
59
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
currentAuth &&
|
|
62
|
+
accessTokenUsable(currentAuth, nowMs) &&
|
|
63
|
+
(force || due("current"))
|
|
64
|
+
) {
|
|
60
65
|
targets.push({ key: "current", codexHome: dirname(codexAuthPath) });
|
|
61
66
|
}
|
|
62
67
|
return targets;
|
package/hud/providers/codex.mjs
CHANGED
|
@@ -600,11 +600,14 @@ export function readCodexRateLimitSnapshot() {
|
|
|
600
600
|
return {
|
|
601
601
|
buckets,
|
|
602
602
|
promptCache: cache.promptCache ?? null,
|
|
603
|
-
shouldRefresh:
|
|
603
|
+
shouldRefresh:
|
|
604
|
+
!isFresh ||
|
|
605
|
+
cache.owner !== getCodexProbeOwner() ||
|
|
606
|
+
cache.recheck === true,
|
|
604
607
|
};
|
|
605
608
|
}
|
|
606
609
|
|
|
607
|
-
export async function collectProbeSnapshots(now = new Date()) {
|
|
610
|
+
export async function collectProbeSnapshots(now = new Date(), force = false) {
|
|
608
611
|
if (process.env.TFX_CODEX_PROBE === "0") return [];
|
|
609
612
|
let targets = [];
|
|
610
613
|
try {
|
|
@@ -613,6 +616,7 @@ export async function collectProbeSnapshots(now = new Date()) {
|
|
|
613
616
|
stateFilePath: CODEX_PROBE_STATE_PATH,
|
|
614
617
|
ttlMs: Number(process.env.TFX_CODEX_PROBE_TTL_MS) || CODEX_PROBE_TTL_MS,
|
|
615
618
|
nowMs: now.getTime(),
|
|
619
|
+
force,
|
|
616
620
|
});
|
|
617
621
|
} catch {
|
|
618
622
|
return [];
|
|
@@ -658,11 +662,13 @@ export async function refreshCodexRateLimitsCache({
|
|
|
658
662
|
owner = getCodexProbeOwner(),
|
|
659
663
|
} = {}) {
|
|
660
664
|
const now = new Date();
|
|
661
|
-
const
|
|
665
|
+
const previous = readJson(cachePath, null);
|
|
666
|
+
const ownerChanged = previous?.owner !== owner;
|
|
667
|
+
const probed = await collect(now, ownerChanged || previous?.recheck === true);
|
|
662
668
|
const probe =
|
|
663
669
|
probed.length > 0
|
|
664
670
|
? { owner, at: now.getTime(), snapshots: probed }
|
|
665
|
-
: carryProbe(
|
|
671
|
+
: carryProbe(previous?.probe, owner, now.getTime());
|
|
666
672
|
const buckets = getCodexRateLimits({
|
|
667
673
|
sessionsRoot,
|
|
668
674
|
now,
|
|
@@ -671,9 +677,25 @@ export async function refreshCodexRateLimitsCache({
|
|
|
671
677
|
const promptCache = readCodexPromptCache(
|
|
672
678
|
sessionsRoot || join(getCodexHome(), "sessions"),
|
|
673
679
|
);
|
|
680
|
+
const reset = ["primary", "secondary"].some((window) => {
|
|
681
|
+
const before = previous?.buckets?.codex?.[window];
|
|
682
|
+
const after = buckets?.codex?.[window];
|
|
683
|
+
return (
|
|
684
|
+
before &&
|
|
685
|
+
after &&
|
|
686
|
+
((before.used_percent != null &&
|
|
687
|
+
after.used_percent != null &&
|
|
688
|
+
after.used_percent < before.used_percent) ||
|
|
689
|
+
(before.resets_at != null &&
|
|
690
|
+
after.resets_at != null &&
|
|
691
|
+
Math.abs(after.resets_at - before.resets_at) > 3600))
|
|
692
|
+
);
|
|
693
|
+
});
|
|
674
694
|
// buckets가 null이어도 캐시 갱신 (stale 데이터 제거)
|
|
675
695
|
writeJsonSafe(cachePath, {
|
|
676
696
|
timestamp: Date.now(),
|
|
697
|
+
owner,
|
|
698
|
+
recheck: ownerChanged || reset,
|
|
677
699
|
buckets,
|
|
678
700
|
promptCache,
|
|
679
701
|
probe,
|