sidekick-shared 0.18.3 → 0.18.4
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/dist/codexQuota.js +87 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/dist/peakHours.d.ts +5 -0
- package/dist/peakHours.js +32 -0
- package/dist/quotaSnapshots.js +28 -0
- package/package.json +1 -1
package/dist/codexQuota.js
CHANGED
|
@@ -46,6 +46,36 @@ const codex_1 = require("./providers/codex");
|
|
|
46
46
|
const DEFAULT_TAIL_BYTES = 2 * 1024 * 1024;
|
|
47
47
|
const DEFAULT_MAX_SESSION_FILES = 50;
|
|
48
48
|
const CHATGPT_USAGE_URL = 'https://chatgpt.com/backend-api/wham/usage';
|
|
49
|
+
function timestampMs(value, fallbackMs = 0) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return fallbackMs;
|
|
52
|
+
const ms = Date.parse(value);
|
|
53
|
+
return Number.isFinite(ms) ? ms : fallbackMs;
|
|
54
|
+
}
|
|
55
|
+
// Codex can report slightly different percentages for the same reset window
|
|
56
|
+
// across recent sessions. Prefer newer reset windows, then the highest observed
|
|
57
|
+
// same-window utilization so local fallbacks do not under-report quota usage.
|
|
58
|
+
function isPreferredQuotaHit(candidate, current) {
|
|
59
|
+
if (!current)
|
|
60
|
+
return true;
|
|
61
|
+
const candidatePrimaryReset = timestampMs(candidate.quota.fiveHour.resetsAt);
|
|
62
|
+
const currentPrimaryReset = timestampMs(current.quota.fiveHour.resetsAt);
|
|
63
|
+
if (candidatePrimaryReset !== currentPrimaryReset)
|
|
64
|
+
return candidatePrimaryReset > currentPrimaryReset;
|
|
65
|
+
const candidateSecondaryReset = timestampMs(candidate.quota.sevenDay.resetsAt);
|
|
66
|
+
const currentSecondaryReset = timestampMs(current.quota.sevenDay.resetsAt);
|
|
67
|
+
if (candidateSecondaryReset !== currentSecondaryReset)
|
|
68
|
+
return candidateSecondaryReset > currentSecondaryReset;
|
|
69
|
+
const candidateUtilization = candidate.quota.fiveHour.utilization + candidate.quota.sevenDay.utilization;
|
|
70
|
+
const currentUtilization = current.quota.fiveHour.utilization + current.quota.sevenDay.utilization;
|
|
71
|
+
if (candidateUtilization !== currentUtilization)
|
|
72
|
+
return candidateUtilization > currentUtilization;
|
|
73
|
+
const candidateMs = timestampMs(candidate.quota.capturedAt, candidate.mtimeMs);
|
|
74
|
+
const currentMs = timestampMs(current.quota.capturedAt, current.mtimeMs);
|
|
75
|
+
if (candidateMs !== currentMs)
|
|
76
|
+
return candidateMs > currentMs;
|
|
77
|
+
return candidate.mtimeMs > current.mtimeMs;
|
|
78
|
+
}
|
|
49
79
|
function normalizePercent(value) {
|
|
50
80
|
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
51
81
|
}
|
|
@@ -175,14 +205,53 @@ function quotaFromCodexRateLimits(rateLimits, source = 'session', capturedAt = n
|
|
|
175
205
|
};
|
|
176
206
|
}
|
|
177
207
|
function readLatestCodexQuotaFromRollouts(sessionPaths, options = {}) {
|
|
208
|
+
return readLatestCodexQuotaHitFromRollouts(sessionPaths, options)?.quota ?? null;
|
|
209
|
+
}
|
|
210
|
+
function readLatestCodexQuotaHitFromRollouts(sessionPaths, options = {}) {
|
|
178
211
|
const maxSessionFiles = options.maxSessionFiles ?? DEFAULT_MAX_SESSION_FILES;
|
|
179
212
|
const maxTailBytes = options.maxTailBytes ?? DEFAULT_TAIL_BYTES;
|
|
213
|
+
let latest = null;
|
|
180
214
|
for (const sessionPath of sessionPaths.slice(0, maxSessionFiles)) {
|
|
181
215
|
const hit = readLatestQuotaFromRollout(sessionPath, maxTailBytes, options.source ?? 'session');
|
|
182
|
-
if (hit)
|
|
183
|
-
|
|
216
|
+
if (hit && isPreferredQuotaHit(hit, latest)) {
|
|
217
|
+
latest = hit;
|
|
218
|
+
}
|
|
184
219
|
}
|
|
185
|
-
return
|
|
220
|
+
return latest;
|
|
221
|
+
}
|
|
222
|
+
function dedupePaths(paths) {
|
|
223
|
+
const seen = new Set();
|
|
224
|
+
const unique = [];
|
|
225
|
+
for (const filePath of paths) {
|
|
226
|
+
if (seen.has(filePath))
|
|
227
|
+
continue;
|
|
228
|
+
seen.add(filePath);
|
|
229
|
+
unique.push(filePath);
|
|
230
|
+
}
|
|
231
|
+
return unique;
|
|
232
|
+
}
|
|
233
|
+
function sortPathsByMtimeDesc(paths) {
|
|
234
|
+
return [...paths].sort((a, b) => {
|
|
235
|
+
const aMtime = safeMtimeMs(a);
|
|
236
|
+
const bMtime = safeMtimeMs(b);
|
|
237
|
+
return bMtime - aMtime;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function safeMtimeMs(filePath) {
|
|
241
|
+
try {
|
|
242
|
+
return fs.statSync(filePath).mtime.getTime();
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function findAccountRolloutFiles(codexHome) {
|
|
249
|
+
const homes = codexHome ? [codexHome] : (0, codexProfiles_1.getCodexMonitoringHomes)();
|
|
250
|
+
const files = [];
|
|
251
|
+
for (const home of homes) {
|
|
252
|
+
files.push(...findRolloutFiles(path.join(home, 'sessions')));
|
|
253
|
+
}
|
|
254
|
+
return sortPathsByMtimeDesc(dedupePaths(files));
|
|
186
255
|
}
|
|
187
256
|
function resolveCodexQuotaFromLocalSources(options = {}) {
|
|
188
257
|
const account = options.activeAccount !== undefined ? options.activeAccount : (0, codexProfiles_1.getActiveCodexAccount)();
|
|
@@ -196,20 +265,22 @@ function resolveCodexQuotaFromLocalSources(options = {}) {
|
|
|
196
265
|
return new codex_1.CodexProvider();
|
|
197
266
|
})();
|
|
198
267
|
try {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
268
|
+
const candidates = [];
|
|
269
|
+
if (options.workspacePath) {
|
|
270
|
+
const workspaceSessions = provider.findAllSessions(options.workspacePath);
|
|
271
|
+
const workspaceHit = readLatestCodexQuotaHitFromRollouts(workspaceSessions, { maxTailBytes, maxSessionFiles });
|
|
272
|
+
if (workspaceHit)
|
|
273
|
+
candidates.push(workspaceHit);
|
|
205
274
|
}
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
275
|
+
const accountSessions = findAccountRolloutFiles(options.codexHome);
|
|
276
|
+
const accountHit = readLatestCodexQuotaHitFromRollouts(accountSessions, { maxTailBytes, maxSessionFiles });
|
|
277
|
+
if (accountHit)
|
|
278
|
+
candidates.push(accountHit);
|
|
279
|
+
const latestHit = candidates.reduce((latest, candidate) => isPreferredQuotaHit(candidate, latest) ? candidate : latest, null);
|
|
280
|
+
if (latestHit) {
|
|
210
281
|
if (account)
|
|
211
|
-
writeSnapshot('codex', account.id,
|
|
212
|
-
return enrichCodexQuota(
|
|
282
|
+
writeSnapshot('codex', account.id, latestHit.quota);
|
|
283
|
+
return enrichCodexQuota(latestHit.quota, account);
|
|
213
284
|
}
|
|
214
285
|
const cached = account ? readSnapshot('codex', account.id) : null;
|
|
215
286
|
if (cached) {
|
|
@@ -378,7 +449,7 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
|
|
|
378
449
|
continue;
|
|
379
450
|
const quota = quotaFromCodexRateLimits(parsed.payload.rate_limits, source, parsed.timestamp ?? new Date(stat.mtime).toISOString());
|
|
380
451
|
if (quota)
|
|
381
|
-
return { quota, filePath: sessionPath };
|
|
452
|
+
return { quota, filePath: sessionPath, mtimeMs: stat.mtime.getTime() };
|
|
382
453
|
}
|
|
383
454
|
catch {
|
|
384
455
|
// Ignore malformed or partial lines.
|
package/dist/index.d.ts
CHANGED
|
@@ -123,5 +123,5 @@ export { extractToolCall, extractToolCalls } from './extractors/toolCall';
|
|
|
123
123
|
export { messageUsageSchema, sessionMessageSchema, sessionEventSchema, permissionModeSchema, } from './schemas/sessionEvent';
|
|
124
124
|
export { fetchProviderStatus, fetchOpenAIStatus } from './providerStatus';
|
|
125
125
|
export type { ProviderStatusState } from './providerStatus';
|
|
126
|
-
export { fetchPeakHoursStatus } from './peakHours';
|
|
126
|
+
export { createPeakHoursNotApplicableState, fetchPeakHoursStatus, isClaudeCodeSessionProvider, scopePeakHoursToSessionProvider, } from './peakHours';
|
|
127
127
|
export type { PeakHoursState } from './peakHours';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.findActiveClaudeSession = exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
|
|
7
7
|
exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = void 0;
|
|
8
8
|
exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = void 0;
|
|
9
|
-
exports.fetchPeakHoursStatus = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractToolCall = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.MultiProviderQuotaService = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = void 0;
|
|
9
|
+
exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractToolCall = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.MultiProviderQuotaService = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = void 0;
|
|
10
10
|
var taskPersistence_1 = require("./types/taskPersistence");
|
|
11
11
|
Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
|
|
12
12
|
Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
|
|
@@ -295,4 +295,7 @@ Object.defineProperty(exports, "fetchProviderStatus", { enumerable: true, get: f
|
|
|
295
295
|
Object.defineProperty(exports, "fetchOpenAIStatus", { enumerable: true, get: function () { return providerStatus_1.fetchOpenAIStatus; } });
|
|
296
296
|
// Peak Hours (PromoClock — third-party)
|
|
297
297
|
var peakHours_1 = require("./peakHours");
|
|
298
|
+
Object.defineProperty(exports, "createPeakHoursNotApplicableState", { enumerable: true, get: function () { return peakHours_1.createPeakHoursNotApplicableState; } });
|
|
298
299
|
Object.defineProperty(exports, "fetchPeakHoursStatus", { enumerable: true, get: function () { return peakHours_1.fetchPeakHoursStatus; } });
|
|
300
|
+
Object.defineProperty(exports, "isClaudeCodeSessionProvider", { enumerable: true, get: function () { return peakHours_1.isClaudeCodeSessionProvider; } });
|
|
301
|
+
Object.defineProperty(exports, "scopePeakHoursToSessionProvider", { enumerable: true, get: function () { return peakHours_1.scopePeakHoursToSessionProvider; } });
|
package/dist/peakHours.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Polling / eventing is the caller's responsibility.
|
|
11
11
|
*/
|
|
12
|
+
import type { ProviderId } from './providers/types';
|
|
12
13
|
export interface PeakHoursState {
|
|
13
14
|
status: 'peak' | 'off_peak' | 'unknown';
|
|
14
15
|
isPeak: boolean;
|
|
@@ -20,7 +21,11 @@ export interface PeakHoursState {
|
|
|
20
21
|
note: string;
|
|
21
22
|
updatedAt: string;
|
|
22
23
|
unavailable: boolean;
|
|
24
|
+
notApplicable?: boolean;
|
|
23
25
|
}
|
|
26
|
+
export declare function isClaudeCodeSessionProvider(providerId: ProviderId): boolean;
|
|
27
|
+
export declare function createPeakHoursNotApplicableState(providerId: ProviderId): PeakHoursState;
|
|
28
|
+
export declare function scopePeakHoursToSessionProvider(providerId: ProviderId, status: PeakHoursState | null | undefined): PeakHoursState | null;
|
|
24
29
|
/**
|
|
25
30
|
* Fetch current Claude peak-hours state from promoclock.co.
|
|
26
31
|
*
|
package/dist/peakHours.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* Polling / eventing is the caller's responsibility.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.isClaudeCodeSessionProvider = isClaudeCodeSessionProvider;
|
|
15
|
+
exports.createPeakHoursNotApplicableState = createPeakHoursNotApplicableState;
|
|
16
|
+
exports.scopePeakHoursToSessionProvider = scopePeakHoursToSessionProvider;
|
|
14
17
|
exports.fetchPeakHoursStatus = fetchPeakHoursStatus;
|
|
15
18
|
const PROMOCLOCK_ENDPOINT = 'https://promoclock.co/api/status';
|
|
16
19
|
function unavailableState() {
|
|
@@ -27,6 +30,35 @@ function unavailableState() {
|
|
|
27
30
|
unavailable: true,
|
|
28
31
|
};
|
|
29
32
|
}
|
|
33
|
+
const PROVIDER_DISPLAY_NAMES = {
|
|
34
|
+
'claude-code': 'Claude Code',
|
|
35
|
+
opencode: 'OpenCode',
|
|
36
|
+
codex: 'Codex CLI',
|
|
37
|
+
};
|
|
38
|
+
function isClaudeCodeSessionProvider(providerId) {
|
|
39
|
+
return providerId === 'claude-code';
|
|
40
|
+
}
|
|
41
|
+
function createPeakHoursNotApplicableState(providerId) {
|
|
42
|
+
const providerName = PROVIDER_DISPLAY_NAMES[providerId] ?? providerId;
|
|
43
|
+
return {
|
|
44
|
+
status: 'unknown',
|
|
45
|
+
isPeak: false,
|
|
46
|
+
sessionLimitSpeed: 'unknown',
|
|
47
|
+
label: 'Claude peak hours not applicable',
|
|
48
|
+
peakHoursDescription: '',
|
|
49
|
+
nextChange: null,
|
|
50
|
+
minutesUntilChange: null,
|
|
51
|
+
note: `Claude peak hours apply only to Claude Code sessions, not ${providerName}.`,
|
|
52
|
+
updatedAt: new Date().toISOString(),
|
|
53
|
+
unavailable: true,
|
|
54
|
+
notApplicable: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function scopePeakHoursToSessionProvider(providerId, status) {
|
|
58
|
+
if (!isClaudeCodeSessionProvider(providerId))
|
|
59
|
+
return null;
|
|
60
|
+
return status ?? null;
|
|
61
|
+
}
|
|
30
62
|
function normalizeStatus(raw) {
|
|
31
63
|
if (raw === 'peak' || raw === 'off_peak')
|
|
32
64
|
return raw;
|
package/dist/quotaSnapshots.js
CHANGED
|
@@ -83,6 +83,31 @@ function writeStore(store) {
|
|
|
83
83
|
ensureConfigDir();
|
|
84
84
|
atomicWriteJson(getQuotaSnapshotPath(), store);
|
|
85
85
|
}
|
|
86
|
+
function snapshotTimeMs(quota) {
|
|
87
|
+
const capturedAt = quota.capturedAt ? Date.parse(quota.capturedAt) : NaN;
|
|
88
|
+
return Number.isFinite(capturedAt) ? capturedAt : 0;
|
|
89
|
+
}
|
|
90
|
+
function windowResetMs(value) {
|
|
91
|
+
const ms = Date.parse(value);
|
|
92
|
+
return Number.isFinite(ms) ? ms : 0;
|
|
93
|
+
}
|
|
94
|
+
// Preserve the best-known same-window snapshot while still allowing lower
|
|
95
|
+
// utilization after Codex advances to a newer reset window.
|
|
96
|
+
function shouldKeepExistingSnapshot(existing, next) {
|
|
97
|
+
const existingPrimaryReset = windowResetMs(existing.fiveHour.resetsAt);
|
|
98
|
+
const nextPrimaryReset = windowResetMs(next.fiveHour.resetsAt);
|
|
99
|
+
if (existingPrimaryReset !== nextPrimaryReset)
|
|
100
|
+
return existingPrimaryReset > nextPrimaryReset;
|
|
101
|
+
const existingSecondaryReset = windowResetMs(existing.sevenDay.resetsAt);
|
|
102
|
+
const nextSecondaryReset = windowResetMs(next.sevenDay.resetsAt);
|
|
103
|
+
if (existingSecondaryReset !== nextSecondaryReset)
|
|
104
|
+
return existingSecondaryReset > nextSecondaryReset;
|
|
105
|
+
const existingUtilization = existing.fiveHour.utilization + existing.sevenDay.utilization;
|
|
106
|
+
const nextUtilization = next.fiveHour.utilization + next.sevenDay.utilization;
|
|
107
|
+
if (existingUtilization !== nextUtilization)
|
|
108
|
+
return existingUtilization > nextUtilization;
|
|
109
|
+
return snapshotTimeMs(existing) > snapshotTimeMs(next);
|
|
110
|
+
}
|
|
86
111
|
function writeQuotaSnapshot(providerId, accountId, quota) {
|
|
87
112
|
const store = readStore();
|
|
88
113
|
const snapshot = {
|
|
@@ -93,6 +118,9 @@ function writeQuotaSnapshot(providerId, accountId, quota) {
|
|
|
93
118
|
stale: false,
|
|
94
119
|
};
|
|
95
120
|
const index = store.snapshots.findIndex(item => item.providerId === providerId && item.accountId === accountId);
|
|
121
|
+
if (index >= 0 && shouldKeepExistingSnapshot(store.snapshots[index].quota, snapshot)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
96
124
|
const record = {
|
|
97
125
|
providerId,
|
|
98
126
|
accountId,
|
package/package.json
CHANGED