triflux 4.2.0 → 4.2.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/hud-qos-status.mjs +38 -2
- package/package.json +1 -1
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -355,7 +355,7 @@ function readJson(filePath, fallback) {
|
|
|
355
355
|
function writeJsonSafe(filePath, data) {
|
|
356
356
|
try {
|
|
357
357
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
358
|
-
writeFileSync(filePath, JSON.stringify(data));
|
|
358
|
+
writeFileSync(filePath, JSON.stringify(data), { mode: 0o600 });
|
|
359
359
|
} catch { /* 쓰기 실패 무시 */ }
|
|
360
360
|
}
|
|
361
361
|
|
|
@@ -731,7 +731,16 @@ function readClaudeUsageSnapshot() {
|
|
|
731
731
|
return { data: stripStaleResets(cache.data), shouldRefresh: ageMs >= backoffMs };
|
|
732
732
|
}
|
|
733
733
|
const isFresh = ageMs < getClaudeUsageStaleMs();
|
|
734
|
-
|
|
734
|
+
// resets_at이 지난 윈도우의 percent를 0으로 보정 (stale 캐시 방지)
|
|
735
|
+
const data = { ...cache.data };
|
|
736
|
+
const now = Date.now();
|
|
737
|
+
if (data.fiveHourResetsAt && new Date(data.fiveHourResetsAt).getTime() <= now) {
|
|
738
|
+
data.fiveHourPercent = 0;
|
|
739
|
+
}
|
|
740
|
+
if (data.weeklyResetsAt && new Date(data.weeklyResetsAt).getTime() <= now) {
|
|
741
|
+
data.weeklyPercent = 0;
|
|
742
|
+
}
|
|
743
|
+
return { data, shouldRefresh: !isFresh };
|
|
735
744
|
}
|
|
736
745
|
|
|
737
746
|
// 2차: 에러 backoff — 최근 에러 시 재시도 억제 (무한 spawn 방지)
|
|
@@ -996,6 +1005,22 @@ function getGeminiEmail() {
|
|
|
996
1005
|
} catch { return null; }
|
|
997
1006
|
}
|
|
998
1007
|
|
|
1008
|
+
// resets_at이 지난 윈도우의 used_percent를 0으로 보정
|
|
1009
|
+
function expireStaleCodexBuckets(buckets) {
|
|
1010
|
+
if (!buckets) return buckets;
|
|
1011
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
1012
|
+
for (const bucket of Object.values(buckets)) {
|
|
1013
|
+
if (!bucket) continue;
|
|
1014
|
+
if (bucket.primary?.resets_at && bucket.primary.resets_at <= nowSec) {
|
|
1015
|
+
bucket.primary.used_percent = 0;
|
|
1016
|
+
}
|
|
1017
|
+
if (bucket.secondary?.resets_at && bucket.secondary.resets_at <= nowSec) {
|
|
1018
|
+
bucket.secondary.used_percent = 0;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return buckets;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
999
1024
|
// ============================================================================
|
|
1000
1025
|
// Codex 세션 JSONL에서 실제 rate limits 추출
|
|
1001
1026
|
// 한계: rate_limits는 세션별 스냅샷이므로 여러 세션 간 토큰 합산은 불가.
|
|
@@ -1061,6 +1086,7 @@ function getCodexRateLimits() {
|
|
|
1061
1086
|
const main = mergedBuckets.codex || mergedBuckets[Object.keys(mergedBuckets)[0]];
|
|
1062
1087
|
if (main && !main.tokens) main.tokens = syntheticBucket.tokens;
|
|
1063
1088
|
}
|
|
1089
|
+
expireStaleCodexBuckets(mergedBuckets);
|
|
1064
1090
|
return mergedBuckets;
|
|
1065
1091
|
}
|
|
1066
1092
|
}
|
|
@@ -1213,6 +1239,15 @@ function readGeminiQuotaSnapshot(accountId, authContext) {
|
|
|
1213
1239
|
const isFresh = ageMs < GEMINI_QUOTA_STALE_MS;
|
|
1214
1240
|
|
|
1215
1241
|
if (keyMatched) {
|
|
1242
|
+
// resetTime이 지난 버킷의 remainingFraction을 1로 보정 (stale 캐시 방지)
|
|
1243
|
+
if (Array.isArray(cache.buckets)) {
|
|
1244
|
+
const now = Date.now();
|
|
1245
|
+
for (const b of cache.buckets) {
|
|
1246
|
+
if (b?.resetTime && new Date(b.resetTime).getTime() <= now) {
|
|
1247
|
+
b.remainingFraction = 1;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1216
1251
|
return { quota: cache, shouldRefresh: !isFresh };
|
|
1217
1252
|
}
|
|
1218
1253
|
if (isLegacyCache) {
|
|
@@ -1250,6 +1285,7 @@ function readCodexRateLimitSnapshot() {
|
|
|
1250
1285
|
if (!cache?.buckets) {
|
|
1251
1286
|
return { buckets: null, shouldRefresh: true };
|
|
1252
1287
|
}
|
|
1288
|
+
expireStaleCodexBuckets(cache.buckets);
|
|
1253
1289
|
const ts = Number(cache.timestamp);
|
|
1254
1290
|
const ageMs = Number.isFinite(ts) ? Date.now() - ts : Number.MAX_SAFE_INTEGER;
|
|
1255
1291
|
const isFresh = ageMs < CODEX_QUOTA_STALE_MS;
|