sidekick-shared 0.21.0 → 0.21.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 +2 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +23 -6
- package/dist/multiProviderQuotaService.d.ts +10 -0
- package/dist/multiProviderQuotaService.js +34 -0
- package/dist/providerQuota.d.ts +2 -1
- package/dist/providers/detect.js +3 -13
- package/dist/providers/openCode.d.ts +31 -0
- package/dist/providers/openCode.js +108 -2
- package/dist/providers/openCodeDatabase.d.ts +19 -0
- package/dist/providers/openCodeDatabase.js +28 -0
- package/dist/quota.d.ts +1 -1
- package/dist/quotaHistory.d.ts +1 -1
- package/dist/quotaHistory.js +14 -2
- package/dist/quotaSnapshots.d.ts +8 -2
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +2 -1
- package/dist/schemas/quota.d.ts +165 -0
- package/dist/schemas/quota.js +8 -1
- package/dist/schemas/quotaHistory.d.ts +2 -0
- package/dist/schemas/quotaHistory.js +1 -0
- package/dist/zaiQuota.d.ts +209 -0
- package/dist/zaiQuota.js +290 -0
- package/dist/zaiQuotaWatcher.d.ts +97 -0
- package/dist/zaiQuotaWatcher.js +259 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,13 +40,13 @@ npm install sidekick-shared
|
|
|
40
40
|
| **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
|
|
41
41
|
| **Report** | Self-contained HTML session report generation |
|
|
42
42
|
| **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
|
|
43
|
-
| **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows)
|
|
43
|
+
| **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams, and **estimated** z.ai Coding Plan quota derivation from observed OpenCode traffic (`accumulateZaiUsage()`, `inferZaiQuotaState()`, `parseZaiQuotaError()`, `resolveZaiTier()`, `ZAI_TIER_BUDGETS`) — z.ai exposes no usage API, so this is a best-effort estimate, not authoritative |
|
|
44
44
|
| **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
|
|
45
45
|
| **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
|
|
46
46
|
| **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
|
|
47
47
|
| **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Fable 5 / Opus 4.8 / Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
|
|
48
48
|
| **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
|
|
49
|
-
| **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex quota updates behind one typed
|
|
49
|
+
| **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; `ZaiQuotaWatcher` derives an estimated z.ai Coding Plan quota from observed OpenCode turns (event-driven, snapshot + history) |
|
|
50
50
|
| **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, and `getActiveAccountStatus()` for a single-pass active-account read across providers |
|
|
51
51
|
| **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
|
|
52
52
|
| **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export type { ReadPlansOptions, PlanAnalytics } from './readers/plans';
|
|
|
28
28
|
export type { ProviderId, SessionProvider, SessionProviderBase, SessionFileStats, SessionFileInfo, SearchHit, ProjectFolderInfo, SessionReader, ProviderRuntimeStatus } from './providers/types';
|
|
29
29
|
export { detectProvider, getAllDetectedProviders } from './providers/detect';
|
|
30
30
|
export { ClaudeCodeProvider } from './providers/claudeCode';
|
|
31
|
-
export { OpenCodeProvider } from './providers/openCode';
|
|
31
|
+
export { OpenCodeProvider, getOpenCodeDataDir } from './providers/openCode';
|
|
32
32
|
export { CodexProvider } from './providers/codex';
|
|
33
33
|
export { JsonlParser, TRUNCATION_PATTERNS } from './parsers/jsonl';
|
|
34
34
|
export type { RawSessionEvent, JsonlParserCallbacks, JsonlParserOptions } from './parsers/jsonl';
|
|
@@ -121,9 +121,14 @@ export { fetchCodexQuotaFromApi, quotaFromCodexRateLimits, readLatestCodexQuotaF
|
|
|
121
121
|
export type { CodexQuotaApiOptions, CodexQuotaCreditsSnapshot, CodexQuotaResolveOptions, CodexQuotaResolveSource, } from './codexQuota';
|
|
122
122
|
export { CodexQuotaWatcher } from './codexQuotaWatcher';
|
|
123
123
|
export type { CodexQuotaWatcherOptions } from './codexQuotaWatcher';
|
|
124
|
+
export { accumulateZaiUsage, extractNextFlushTime, inferZaiQuotaState, isZaiProviderId, makeUnavailableZaiQuotaState, parseZaiQuotaError, resolveZaiTier, rowsToZaiTurnsAndErrors, turnTokenWeight, ZAI_PROMPT_INVOCATIONS, ZAI_PROVIDER_IDS, ZAI_TIER_BUDGETS, } from './zaiQuota';
|
|
125
|
+
export type { ZaiAccumulatedUsage, ZaiAssistantTurn, ZaiOpenCodeRow, ZaiProviderId, ZaiQuotaError, ZaiQuotaErrorKind, ZaiTier, InferZaiQuotaStateOptions, } from './zaiQuota';
|
|
126
|
+
export { ZaiQuotaWatcher } from './zaiQuotaWatcher';
|
|
127
|
+
export type { ZaiQuotaWatcherOptions } from './zaiQuotaWatcher';
|
|
124
128
|
export { MultiProviderQuotaService } from './multiProviderQuotaService';
|
|
125
129
|
export type { MultiProviderQuotaServiceOptions } from './multiProviderQuotaService';
|
|
126
130
|
export type { ProviderQuotaMap, ProviderQuotaState, RuntimeQuotaProvider } from './providerQuota';
|
|
131
|
+
export type { QuotaSnapshotProviderId } from './quotaSnapshots';
|
|
127
132
|
export { getModelContextWindowSize, DEFAULT_CONTEXT_WINDOW } from './modelContext';
|
|
128
133
|
export { parseModelId, getModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, } from './modelInfo';
|
|
129
134
|
export type { ModelPricing, CostTokenUsage, CostSource, CostProvenanceInput, CostWithProvenance, ModelInfo, ModelProvider, ParsedModelId, ModelDisplayInfo, } from './modelInfo';
|
|
@@ -138,7 +143,7 @@ export { readCodexAssets, codexSessions } from './extractors/sources/codexAssets
|
|
|
138
143
|
export { gatherAssetsForCwd } from './extractors/gatherAssets';
|
|
139
144
|
export type { GatherAssetsOptions, GatherAssetsResult } from './extractors/gatherAssets';
|
|
140
145
|
export { messageUsageSchema, sessionMessageSchema, sessionEventSchema, permissionModeSchema, extractSessionEvents, } from './schemas/sessionEvent';
|
|
141
|
-
export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, providerQuotaMapSchema, } from './schemas/quota';
|
|
146
|
+
export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, zaiProviderQuotaStateSchema, providerQuotaMapSchema, } from './schemas/quota';
|
|
142
147
|
export { quotaHistoryRuntimeProviderSchema, quotaHistorySampleSchema, quotaHistoryDailyBucketSchema, } from './schemas/quotaHistory';
|
|
143
148
|
export { activeProviderAccountStatusSchema, activeAccountStatusSchema, } from './schemas/accountStatus';
|
|
144
149
|
export { accountProviderIdSchema, accountManagerResultSchema, beginAccountLoginResultSchema, accountLoginStatusSchema, accountEntrySchema, savedAccountProfileSchema, listAllAccountsResultSchema, } from './schemas/accountManager';
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Public API for sidekick-shared.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
10
|
-
exports.
|
|
11
|
-
exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = void 0;
|
|
6
|
+
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.getOpenCodeDataDir = 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
|
+
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.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = 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 = exports.findActiveClaudeSession = void 0;
|
|
8
|
+
exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = void 0;
|
|
9
|
+
exports.isZaiProviderId = exports.inferZaiQuotaState = exports.extractNextFlushTime = exports.accumulateZaiUsage = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = 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.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = void 0;
|
|
10
|
+
exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = 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.ZaiQuotaWatcher = exports.ZAI_TIER_BUDGETS = exports.ZAI_PROVIDER_IDS = exports.ZAI_PROMPT_INVOCATIONS = exports.turnTokenWeight = exports.rowsToZaiTurnsAndErrors = exports.resolveZaiTier = exports.parseZaiQuotaError = exports.makeUnavailableZaiQuotaState = void 0;
|
|
11
|
+
exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = void 0;
|
|
12
12
|
var taskPersistence_1 = require("./types/taskPersistence");
|
|
13
13
|
Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
|
|
14
14
|
Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
|
|
@@ -56,6 +56,7 @@ var claudeCode_1 = require("./providers/claudeCode");
|
|
|
56
56
|
Object.defineProperty(exports, "ClaudeCodeProvider", { enumerable: true, get: function () { return claudeCode_1.ClaudeCodeProvider; } });
|
|
57
57
|
var openCode_1 = require("./providers/openCode");
|
|
58
58
|
Object.defineProperty(exports, "OpenCodeProvider", { enumerable: true, get: function () { return openCode_1.OpenCodeProvider; } });
|
|
59
|
+
Object.defineProperty(exports, "getOpenCodeDataDir", { enumerable: true, get: function () { return openCode_1.getOpenCodeDataDir; } });
|
|
59
60
|
var codex_1 = require("./providers/codex");
|
|
60
61
|
Object.defineProperty(exports, "CodexProvider", { enumerable: true, get: function () { return codex_1.CodexProvider; } });
|
|
61
62
|
// Parsers — JSONL
|
|
@@ -295,6 +296,21 @@ Object.defineProperty(exports, "resolveCodexQuota", { enumerable: true, get: fun
|
|
|
295
296
|
Object.defineProperty(exports, "resolveCodexQuotaFromLocalSources", { enumerable: true, get: function () { return codexQuota_1.resolveCodexQuotaFromLocalSources; } });
|
|
296
297
|
var codexQuotaWatcher_1 = require("./codexQuotaWatcher");
|
|
297
298
|
Object.defineProperty(exports, "CodexQuotaWatcher", { enumerable: true, get: function () { return codexQuotaWatcher_1.CodexQuotaWatcher; } });
|
|
299
|
+
var zaiQuota_1 = require("./zaiQuota");
|
|
300
|
+
Object.defineProperty(exports, "accumulateZaiUsage", { enumerable: true, get: function () { return zaiQuota_1.accumulateZaiUsage; } });
|
|
301
|
+
Object.defineProperty(exports, "extractNextFlushTime", { enumerable: true, get: function () { return zaiQuota_1.extractNextFlushTime; } });
|
|
302
|
+
Object.defineProperty(exports, "inferZaiQuotaState", { enumerable: true, get: function () { return zaiQuota_1.inferZaiQuotaState; } });
|
|
303
|
+
Object.defineProperty(exports, "isZaiProviderId", { enumerable: true, get: function () { return zaiQuota_1.isZaiProviderId; } });
|
|
304
|
+
Object.defineProperty(exports, "makeUnavailableZaiQuotaState", { enumerable: true, get: function () { return zaiQuota_1.makeUnavailableZaiQuotaState; } });
|
|
305
|
+
Object.defineProperty(exports, "parseZaiQuotaError", { enumerable: true, get: function () { return zaiQuota_1.parseZaiQuotaError; } });
|
|
306
|
+
Object.defineProperty(exports, "resolveZaiTier", { enumerable: true, get: function () { return zaiQuota_1.resolveZaiTier; } });
|
|
307
|
+
Object.defineProperty(exports, "rowsToZaiTurnsAndErrors", { enumerable: true, get: function () { return zaiQuota_1.rowsToZaiTurnsAndErrors; } });
|
|
308
|
+
Object.defineProperty(exports, "turnTokenWeight", { enumerable: true, get: function () { return zaiQuota_1.turnTokenWeight; } });
|
|
309
|
+
Object.defineProperty(exports, "ZAI_PROMPT_INVOCATIONS", { enumerable: true, get: function () { return zaiQuota_1.ZAI_PROMPT_INVOCATIONS; } });
|
|
310
|
+
Object.defineProperty(exports, "ZAI_PROVIDER_IDS", { enumerable: true, get: function () { return zaiQuota_1.ZAI_PROVIDER_IDS; } });
|
|
311
|
+
Object.defineProperty(exports, "ZAI_TIER_BUDGETS", { enumerable: true, get: function () { return zaiQuota_1.ZAI_TIER_BUDGETS; } });
|
|
312
|
+
var zaiQuotaWatcher_1 = require("./zaiQuotaWatcher");
|
|
313
|
+
Object.defineProperty(exports, "ZaiQuotaWatcher", { enumerable: true, get: function () { return zaiQuotaWatcher_1.ZaiQuotaWatcher; } });
|
|
298
314
|
var multiProviderQuotaService_1 = require("./multiProviderQuotaService");
|
|
299
315
|
Object.defineProperty(exports, "MultiProviderQuotaService", { enumerable: true, get: function () { return multiProviderQuotaService_1.MultiProviderQuotaService; } });
|
|
300
316
|
// Model Context
|
|
@@ -361,6 +377,7 @@ Object.defineProperty(exports, "runtimeQuotaProviderSchema", { enumerable: true,
|
|
|
361
377
|
Object.defineProperty(exports, "providerQuotaStateSchema", { enumerable: true, get: function () { return quota_2.providerQuotaStateSchema; } });
|
|
362
378
|
Object.defineProperty(exports, "claudeProviderQuotaStateSchema", { enumerable: true, get: function () { return quota_2.claudeProviderQuotaStateSchema; } });
|
|
363
379
|
Object.defineProperty(exports, "codexProviderQuotaStateSchema", { enumerable: true, get: function () { return quota_2.codexProviderQuotaStateSchema; } });
|
|
380
|
+
Object.defineProperty(exports, "zaiProviderQuotaStateSchema", { enumerable: true, get: function () { return quota_2.zaiProviderQuotaStateSchema; } });
|
|
364
381
|
Object.defineProperty(exports, "providerQuotaMapSchema", { enumerable: true, get: function () { return quota_2.providerQuotaMapSchema; } });
|
|
365
382
|
// Schemas — Zod runtime validation for quota history
|
|
366
383
|
var quotaHistory_2 = require("./schemas/quotaHistory");
|
|
@@ -10,6 +10,11 @@ interface CodexWatcherLike extends Disposable {
|
|
|
10
10
|
stop(): void;
|
|
11
11
|
onUpdate(cb: (state: ProviderQuotaState<'codex'>) => void): Disposable;
|
|
12
12
|
}
|
|
13
|
+
interface ZaiWatcherLike extends Disposable {
|
|
14
|
+
start(): void;
|
|
15
|
+
stop(): void;
|
|
16
|
+
onUpdate(cb: (state: ProviderQuotaState<'zai'>) => void): Disposable;
|
|
17
|
+
}
|
|
13
18
|
export interface MultiProviderQuotaServiceOptions {
|
|
14
19
|
activeIntervalMs?: number;
|
|
15
20
|
idleIntervalMs?: number;
|
|
@@ -18,6 +23,7 @@ export interface MultiProviderQuotaServiceOptions {
|
|
|
18
23
|
includePeakHours?: boolean;
|
|
19
24
|
codexWorkspacePath?: string;
|
|
20
25
|
codexWatcher?: CodexWatcherLike | null;
|
|
26
|
+
zaiWatcher?: ZaiWatcherLike | null;
|
|
21
27
|
readClaudeCredentials?: () => Promise<ClaudeMaxCredentials | null>;
|
|
22
28
|
readClaudeAccount?: typeof readActiveClaudeAccount;
|
|
23
29
|
fetchClaudeQuota?: (accessToken: string) => Promise<QuotaState>;
|
|
@@ -39,6 +45,9 @@ export declare class MultiProviderQuotaService implements Disposable {
|
|
|
39
45
|
private readonly codexWatcher;
|
|
40
46
|
private readonly ownsCodexWatcher;
|
|
41
47
|
private codexSubscription;
|
|
48
|
+
private readonly zaiWatcher;
|
|
49
|
+
private readonly ownsZaiWatcher;
|
|
50
|
+
private zaiSubscription;
|
|
42
51
|
private timer;
|
|
43
52
|
private polling;
|
|
44
53
|
private quotaByProvider;
|
|
@@ -60,6 +69,7 @@ export declare class MultiProviderQuotaService implements Disposable {
|
|
|
60
69
|
getLatest(): ProviderQuotaMap;
|
|
61
70
|
updateProviderQuota(provider: 'claude', state: ProviderQuotaState<'claude'>): void;
|
|
62
71
|
updateProviderQuota(provider: 'codex', state: ProviderQuotaState<'codex'>): void;
|
|
72
|
+
updateProviderQuota(provider: 'zai', state: ProviderQuotaState<'zai'>): void;
|
|
63
73
|
dispose(): void;
|
|
64
74
|
private refreshPeakHours;
|
|
65
75
|
private poll;
|
|
@@ -59,6 +59,9 @@ class MultiProviderQuotaService {
|
|
|
59
59
|
codexWatcher;
|
|
60
60
|
ownsCodexWatcher;
|
|
61
61
|
codexSubscription = null;
|
|
62
|
+
zaiWatcher;
|
|
63
|
+
ownsZaiWatcher;
|
|
64
|
+
zaiSubscription = null;
|
|
62
65
|
timer;
|
|
63
66
|
polling = false;
|
|
64
67
|
quotaByProvider = {};
|
|
@@ -97,12 +100,24 @@ class MultiProviderQuotaService {
|
|
|
97
100
|
this.codexSubscription = this.codexWatcher?.onUpdate((state) => {
|
|
98
101
|
this.updateProviderQuota('codex', state);
|
|
99
102
|
}) ?? null;
|
|
103
|
+
if (options.zaiWatcher !== undefined) {
|
|
104
|
+
this.zaiWatcher = options.zaiWatcher;
|
|
105
|
+
this.ownsZaiWatcher = false;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.zaiWatcher = null;
|
|
109
|
+
this.ownsZaiWatcher = false;
|
|
110
|
+
}
|
|
111
|
+
this.zaiSubscription = this.zaiWatcher?.onUpdate((state) => {
|
|
112
|
+
this.updateProviderQuota('zai', state);
|
|
113
|
+
}) ?? null;
|
|
100
114
|
}
|
|
101
115
|
startPolling() {
|
|
102
116
|
if (this.polling)
|
|
103
117
|
return;
|
|
104
118
|
this.polling = true;
|
|
105
119
|
this.codexWatcher?.start();
|
|
120
|
+
this.zaiWatcher?.start();
|
|
106
121
|
void this.poll();
|
|
107
122
|
this.log?.('[Quota] Polling started');
|
|
108
123
|
}
|
|
@@ -115,6 +130,7 @@ class MultiProviderQuotaService {
|
|
|
115
130
|
this.timer = undefined;
|
|
116
131
|
}
|
|
117
132
|
this.codexWatcher?.stop();
|
|
133
|
+
this.zaiWatcher?.stop();
|
|
118
134
|
this.log?.('[Quota] Polling stopped');
|
|
119
135
|
}
|
|
120
136
|
setPollingMode(mode) {
|
|
@@ -165,6 +181,19 @@ class MultiProviderQuotaService {
|
|
|
165
181
|
this.emit();
|
|
166
182
|
return;
|
|
167
183
|
}
|
|
184
|
+
if (provider === 'zai') {
|
|
185
|
+
const nextState = {
|
|
186
|
+
...state,
|
|
187
|
+
runtimeProvider: 'zai',
|
|
188
|
+
providerId: 'zai',
|
|
189
|
+
};
|
|
190
|
+
this.quotaByProvider = {
|
|
191
|
+
...this.quotaByProvider,
|
|
192
|
+
zai: nextState,
|
|
193
|
+
};
|
|
194
|
+
this.emit();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
168
197
|
const nextState = {
|
|
169
198
|
...state,
|
|
170
199
|
runtimeProvider: 'codex',
|
|
@@ -183,6 +212,11 @@ class MultiProviderQuotaService {
|
|
|
183
212
|
if (this.ownsCodexWatcher) {
|
|
184
213
|
this.codexWatcher?.dispose();
|
|
185
214
|
}
|
|
215
|
+
this.zaiSubscription?.dispose();
|
|
216
|
+
this.zaiSubscription = null;
|
|
217
|
+
if (this.ownsZaiWatcher) {
|
|
218
|
+
this.zaiWatcher?.dispose();
|
|
219
|
+
}
|
|
186
220
|
this.listeners.splice(0, this.listeners.length);
|
|
187
221
|
}
|
|
188
222
|
async refreshPeakHours() {
|
package/dist/providerQuota.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PeakHoursState } from './peakHours';
|
|
2
2
|
import type { QuotaState } from './quota';
|
|
3
3
|
import type { QuotaFailureDescriptor } from './quotaPresentation';
|
|
4
|
-
export type RuntimeQuotaProvider = 'claude' | 'codex';
|
|
4
|
+
export type RuntimeQuotaProvider = 'claude' | 'codex' | 'zai';
|
|
5
5
|
export interface ProviderQuotaState<TProvider extends RuntimeQuotaProvider = RuntimeQuotaProvider> extends QuotaState {
|
|
6
6
|
runtimeProvider: TProvider;
|
|
7
7
|
accountLabel?: string;
|
|
@@ -12,4 +12,5 @@ export interface ProviderQuotaState<TProvider extends RuntimeQuotaProvider = Run
|
|
|
12
12
|
export interface ProviderQuotaMap {
|
|
13
13
|
claude?: ProviderQuotaState<'claude'>;
|
|
14
14
|
codex?: ProviderQuotaState<'codex'>;
|
|
15
|
+
zai?: ProviderQuotaState<'zai'>;
|
|
15
16
|
}
|
package/dist/providers/detect.js
CHANGED
|
@@ -44,17 +44,7 @@ const fs = __importStar(require("fs"));
|
|
|
44
44
|
const os = __importStar(require("os"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
46
|
const codexProfiles_1 = require("../codexProfiles");
|
|
47
|
-
|
|
48
|
-
const xdg = process.env.XDG_DATA_HOME;
|
|
49
|
-
if (xdg)
|
|
50
|
-
return path.join(xdg, 'opencode');
|
|
51
|
-
if (process.platform === 'darwin')
|
|
52
|
-
return path.join(os.homedir(), 'Library', 'Application Support', 'opencode');
|
|
53
|
-
if (process.platform === 'win32') {
|
|
54
|
-
return path.join(process.env.LOCALAPPDATA || process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'opencode');
|
|
55
|
-
}
|
|
56
|
-
return path.join(os.homedir(), '.local', 'share', 'opencode');
|
|
57
|
-
}
|
|
47
|
+
const openCode_1 = require("./openCode");
|
|
58
48
|
function getCodexHomes() {
|
|
59
49
|
return (0, codexProfiles_1.getCodexMonitoringHomes)();
|
|
60
50
|
}
|
|
@@ -109,7 +99,7 @@ function getMostRecentMtime(dir) {
|
|
|
109
99
|
}
|
|
110
100
|
}
|
|
111
101
|
function getOpenCodeActivityMtime() {
|
|
112
|
-
const dataDir = getOpenCodeDataDir();
|
|
102
|
+
const dataDir = (0, openCode_1.getOpenCodeDataDir)();
|
|
113
103
|
const dbPath = path.join(dataDir, 'opencode.db');
|
|
114
104
|
try {
|
|
115
105
|
const dbMtime = fs.statSync(dbPath).mtime.getTime();
|
|
@@ -137,7 +127,7 @@ function getCodexActivityMtime() {
|
|
|
137
127
|
}
|
|
138
128
|
function getProviderPaths() {
|
|
139
129
|
const claudeBase = path.join(os.homedir(), '.claude', 'projects');
|
|
140
|
-
const openCodeDataDir = getOpenCodeDataDir();
|
|
130
|
+
const openCodeDataDir = (0, openCode_1.getOpenCodeDataDir)();
|
|
141
131
|
const codexHomes = getCodexHomes();
|
|
142
132
|
return {
|
|
143
133
|
claudeBase,
|
|
@@ -16,8 +16,20 @@
|
|
|
16
16
|
* @module providers/openCode
|
|
17
17
|
*/
|
|
18
18
|
import type { ReadSessionContextSnapshotOptions, SessionContextSnapshot } from '../context/sessionContext';
|
|
19
|
+
import type { ZaiTier } from '../zaiQuota';
|
|
20
|
+
import type { QuotaState } from '../quota';
|
|
19
21
|
import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileStats, ProviderId, ProviderRuntimeStatus } from './types';
|
|
20
22
|
import type { TokenUsage, SubagentStats, ContextAttribution } from '../types/sessionEvent';
|
|
23
|
+
/**
|
|
24
|
+
* Gets the OpenCode data directory.
|
|
25
|
+
*
|
|
26
|
+
* Resolves in priority order: `XDG_DATA_HOME` (if set), then whichever of
|
|
27
|
+
* the platform-default candidates actually contains an `opencode.db` file.
|
|
28
|
+
* OpenCode's CLI has historically used the Linux-style `~/.local/share/opencode`
|
|
29
|
+
* path even on macOS, so we probe both and pick whichever exists — falling
|
|
30
|
+
* back to the platform default when neither is present yet.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getOpenCodeDataDir(): string;
|
|
21
33
|
/**
|
|
22
34
|
* Session provider for OpenCode CLI.
|
|
23
35
|
*
|
|
@@ -77,4 +89,23 @@ export declare class OpenCodeProvider implements SessionProviderBase {
|
|
|
77
89
|
/** Set a runtime-reported context window limit (overrides static map). */
|
|
78
90
|
setDynamicContextWindowLimit(limit: number): void;
|
|
79
91
|
dispose(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Derives a z.ai coding-plan `QuotaState` from OpenCode assistant turns
|
|
94
|
+
* tagged with `providerID ∈ {zai, zai-coding-plan}`.
|
|
95
|
+
*
|
|
96
|
+
* z.ai does not expose a quota API (verified vs. docs.z.ai/openapi.json),
|
|
97
|
+
* so this method accumulates observed per-turn tokens from OpenCode's DB
|
|
98
|
+
* into 5-hour / 7-day rolling windows and infers utilization against the
|
|
99
|
+
* configured tier budget. Returns null when no z.ai routing is detected.
|
|
100
|
+
*
|
|
101
|
+
* @param tier Plan tier; `'auto'` infers from observed weekly volume.
|
|
102
|
+
* When the caller has no opinion, the heuristic falls back to
|
|
103
|
+
* `'max'` (conservative — small overcount rather than large).
|
|
104
|
+
*/
|
|
105
|
+
getZaiQuotaState(tier?: ZaiTier | 'auto'): QuotaState | null;
|
|
106
|
+
/**
|
|
107
|
+
* Cheap boolean: does the local OpenCode install currently route at z.ai?
|
|
108
|
+
* Used by callers to decide whether to render a z.ai quota surface.
|
|
109
|
+
*/
|
|
110
|
+
isZaiRoutingActive(): boolean;
|
|
80
111
|
}
|
|
@@ -51,6 +51,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
51
51
|
})();
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
53
|
exports.OpenCodeProvider = void 0;
|
|
54
|
+
exports.getOpenCodeDataDir = getOpenCodeDataDir;
|
|
54
55
|
const fs = __importStar(require("fs"));
|
|
55
56
|
const os = __importStar(require("os"));
|
|
56
57
|
const path = __importStar(require("path"));
|
|
@@ -58,19 +59,42 @@ const child_process_1 = require("child_process");
|
|
|
58
59
|
const sessionContext_1 = require("../context/sessionContext");
|
|
59
60
|
const openCodeParser_1 = require("../parsers/openCodeParser");
|
|
60
61
|
const openCodeDatabase_1 = require("./openCodeDatabase");
|
|
62
|
+
const zaiQuota_1 = require("../zaiQuota");
|
|
61
63
|
const modelContext_1 = require("../modelContext");
|
|
62
64
|
// ---------------------------------------------------------------------------
|
|
63
65
|
// Helper functions
|
|
64
66
|
// ---------------------------------------------------------------------------
|
|
65
67
|
/**
|
|
66
68
|
* Gets the OpenCode data directory.
|
|
67
|
-
*
|
|
69
|
+
*
|
|
70
|
+
* Resolves in priority order: `XDG_DATA_HOME` (if set), then whichever of
|
|
71
|
+
* the platform-default candidates actually contains an `opencode.db` file.
|
|
72
|
+
* OpenCode's CLI has historically used the Linux-style `~/.local/share/opencode`
|
|
73
|
+
* path even on macOS, so we probe both and pick whichever exists — falling
|
|
74
|
+
* back to the platform default when neither is present yet.
|
|
68
75
|
*/
|
|
69
76
|
function getOpenCodeDataDir() {
|
|
70
77
|
const xdg = process.env.XDG_DATA_HOME;
|
|
71
|
-
if (xdg)
|
|
78
|
+
if (xdg)
|
|
72
79
|
return path.join(xdg, 'opencode');
|
|
80
|
+
const candidates = [path.join(os.homedir(), '.local', 'share', 'opencode')];
|
|
81
|
+
if (process.platform === 'darwin') {
|
|
82
|
+
candidates.push(path.join(os.homedir(), 'Library', 'Application Support', 'opencode'));
|
|
83
|
+
}
|
|
84
|
+
else if (process.platform === 'win32') {
|
|
85
|
+
candidates.push(path.join(process.env.LOCALAPPDATA || process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'opencode'));
|
|
86
|
+
}
|
|
87
|
+
for (const candidate of candidates) {
|
|
88
|
+
try {
|
|
89
|
+
if (fs.existsSync(path.join(candidate, 'opencode.db')))
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// ignore — try the next candidate.
|
|
94
|
+
}
|
|
73
95
|
}
|
|
96
|
+
// Nothing found yet — return the platform default so a fresh OpenCode
|
|
97
|
+
// install gets detected at the conventional location for this OS.
|
|
74
98
|
if (process.platform === 'darwin') {
|
|
75
99
|
return path.join(os.homedir(), 'Library', 'Application Support', 'opencode');
|
|
76
100
|
}
|
|
@@ -1548,5 +1572,87 @@ class OpenCodeProvider {
|
|
|
1548
1572
|
this.sessionMetaCache.clear();
|
|
1549
1573
|
this.dynamicContextWindowLimit = null;
|
|
1550
1574
|
}
|
|
1575
|
+
// --- z.ai (GLM Coding Plan) quota derivation ---
|
|
1576
|
+
/**
|
|
1577
|
+
* Derives a z.ai coding-plan `QuotaState` from OpenCode assistant turns
|
|
1578
|
+
* tagged with `providerID ∈ {zai, zai-coding-plan}`.
|
|
1579
|
+
*
|
|
1580
|
+
* z.ai does not expose a quota API (verified vs. docs.z.ai/openapi.json),
|
|
1581
|
+
* so this method accumulates observed per-turn tokens from OpenCode's DB
|
|
1582
|
+
* into 5-hour / 7-day rolling windows and infers utilization against the
|
|
1583
|
+
* configured tier budget. Returns null when no z.ai routing is detected.
|
|
1584
|
+
*
|
|
1585
|
+
* @param tier Plan tier; `'auto'` infers from observed weekly volume.
|
|
1586
|
+
* When the caller has no opinion, the heuristic falls back to
|
|
1587
|
+
* `'max'` (conservative — small overcount rather than large).
|
|
1588
|
+
*/
|
|
1589
|
+
getZaiQuotaState(tier = 'auto') {
|
|
1590
|
+
const db = this.ensureDb();
|
|
1591
|
+
if (!db)
|
|
1592
|
+
return null;
|
|
1593
|
+
const sinceMs = Date.now() - 7 * 86_400_000;
|
|
1594
|
+
let rawRows = [];
|
|
1595
|
+
try {
|
|
1596
|
+
rawRows = db.getAssistantMessagesByProviderId([...zaiQuota_1.ZAI_PROVIDER_IDS], sinceMs);
|
|
1597
|
+
}
|
|
1598
|
+
catch {
|
|
1599
|
+
return null;
|
|
1600
|
+
}
|
|
1601
|
+
if (rawRows.length === 0)
|
|
1602
|
+
return null;
|
|
1603
|
+
const turns = rawRows.map(row => ({
|
|
1604
|
+
timestampMs: row.timeCreated,
|
|
1605
|
+
model: row.modelId,
|
|
1606
|
+
inputTokens: row.inputTokens || 0,
|
|
1607
|
+
outputTokens: row.outputTokens || 0,
|
|
1608
|
+
cacheReadTokens: row.cacheReadTokens || 0,
|
|
1609
|
+
cacheWriteTokens: row.cacheWriteTokens || 0,
|
|
1610
|
+
reasoningTokens: row.reasoningTokens || 0,
|
|
1611
|
+
}));
|
|
1612
|
+
const accumulated = (0, zaiQuota_1.accumulateZaiUsage)(turns, Date.now());
|
|
1613
|
+
// Default to 'max' on auto when usage is non-zero — overestimating the
|
|
1614
|
+
// tier gives a lower (safer) utilization % until calibration runs.
|
|
1615
|
+
const resolvedTier = tier === 'auto'
|
|
1616
|
+
? (accumulated.weeklyTurns > 0 ? 'max' : 'lite')
|
|
1617
|
+
: tier;
|
|
1618
|
+
// Walk rows again to trap any captured z.ai business errors.
|
|
1619
|
+
let authoritativeFiveHourResetAt;
|
|
1620
|
+
let authoritativeWeeklyResetAt;
|
|
1621
|
+
for (const row of rawRows) {
|
|
1622
|
+
if (row.errorCode == null && !row.errorMessage)
|
|
1623
|
+
continue;
|
|
1624
|
+
const parsed = (0, zaiQuota_1.parseZaiQuotaError)({
|
|
1625
|
+
code: row.errorCode ?? undefined,
|
|
1626
|
+
message: row.errorMessage ?? undefined,
|
|
1627
|
+
});
|
|
1628
|
+
if (!parsed?.resetsAt)
|
|
1629
|
+
continue;
|
|
1630
|
+
if (parsed.kind === 'exhausted') {
|
|
1631
|
+
authoritativeFiveHourResetAt = parsed.resetsAt;
|
|
1632
|
+
if (String(parsed.code) === '1310')
|
|
1633
|
+
authoritativeWeeklyResetAt = parsed.resetsAt;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
return (0, zaiQuota_1.inferZaiQuotaState)(accumulated, resolvedTier, {
|
|
1637
|
+
authoritativeFiveHourResetAt,
|
|
1638
|
+
authoritativeWeeklyResetAt,
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Cheap boolean: does the local OpenCode install currently route at z.ai?
|
|
1643
|
+
* Used by callers to decide whether to render a z.ai quota surface.
|
|
1644
|
+
*/
|
|
1645
|
+
isZaiRoutingActive() {
|
|
1646
|
+
const db = this.ensureDb();
|
|
1647
|
+
if (!db)
|
|
1648
|
+
return false;
|
|
1649
|
+
try {
|
|
1650
|
+
const rows = db.getAssistantMessagesByProviderId([...zaiQuota_1.ZAI_PROVIDER_IDS], Date.now() - 7 * 86_400_000);
|
|
1651
|
+
return rows.length > 0;
|
|
1652
|
+
}
|
|
1653
|
+
catch {
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1551
1657
|
}
|
|
1552
1658
|
exports.OpenCodeProvider = OpenCodeProvider;
|
|
@@ -91,6 +91,25 @@ export declare class OpenCodeDatabase {
|
|
|
91
91
|
sessionCount: number;
|
|
92
92
|
maxTimeUpdated: number;
|
|
93
93
|
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Returns assistant messages tagged with the given providerID(s), completed
|
|
96
|
+
* within `sinceMs`. Used by the z.ai quota accumulator to walk OpenCode's
|
|
97
|
+
* per-turn token records without re-deriving the session filter.
|
|
98
|
+
*
|
|
99
|
+
* Results are ordered oldest-first by `time_created`.
|
|
100
|
+
*/
|
|
101
|
+
getAssistantMessagesByProviderId(providerIds: readonly string[], sinceMs: number): Array<{
|
|
102
|
+
timeCreated: number;
|
|
103
|
+
modelId: string;
|
|
104
|
+
providerId: string;
|
|
105
|
+
inputTokens: number;
|
|
106
|
+
outputTokens: number;
|
|
107
|
+
cacheReadTokens: number;
|
|
108
|
+
cacheWriteTokens: number;
|
|
109
|
+
reasoningTokens: number;
|
|
110
|
+
errorMessage: string | null;
|
|
111
|
+
errorCode: string | null;
|
|
112
|
+
}>;
|
|
94
113
|
/** Get the database file's mtime (ms epoch). Returns 0 if unavailable. */
|
|
95
114
|
getDbMtime(): number;
|
|
96
115
|
}
|
|
@@ -249,6 +249,34 @@ class OpenCodeDatabase {
|
|
|
249
249
|
getProjectSessionStats() {
|
|
250
250
|
return this.query('SELECT project_id AS projectId, COUNT(*) AS sessionCount, MAX(time_updated) AS maxTimeUpdated FROM session GROUP BY project_id');
|
|
251
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Returns assistant messages tagged with the given providerID(s), completed
|
|
254
|
+
* within `sinceMs`. Used by the z.ai quota accumulator to walk OpenCode's
|
|
255
|
+
* per-turn token records without re-deriving the session filter.
|
|
256
|
+
*
|
|
257
|
+
* Results are ordered oldest-first by `time_created`.
|
|
258
|
+
*/
|
|
259
|
+
getAssistantMessagesByProviderId(providerIds, sinceMs) {
|
|
260
|
+
if (providerIds.length === 0)
|
|
261
|
+
return [];
|
|
262
|
+
const providerClauses = providerIds.map(() => 'json_extract(data, \'$.providerID\') = ?').join(' OR ');
|
|
263
|
+
return this.query(`SELECT
|
|
264
|
+
time_created AS timeCreated,
|
|
265
|
+
COALESCE(json_extract(data, '$.modelID'), 'unknown') AS modelId,
|
|
266
|
+
COALESCE(json_extract(data, '$.providerID'), '') AS providerId,
|
|
267
|
+
COALESCE(json_extract(data, '$.tokens.input'), 0) AS inputTokens,
|
|
268
|
+
COALESCE(json_extract(data, '$.tokens.output'), 0) AS outputTokens,
|
|
269
|
+
COALESCE(json_extract(data, '$.tokens.cache.read'), 0) AS cacheReadTokens,
|
|
270
|
+
COALESCE(json_extract(data, '$.tokens.cache.write'), 0) AS cacheWriteTokens,
|
|
271
|
+
COALESCE(json_extract(data, '$.tokens.reasoning'), 0) AS reasoningTokens,
|
|
272
|
+
json_extract(data, '$.error.message') AS errorMessage,
|
|
273
|
+
json_extract(data, '$.error.code') AS errorCode
|
|
274
|
+
FROM message
|
|
275
|
+
WHERE time_created >= ?
|
|
276
|
+
AND json_extract(data, '$.role') = 'assistant'
|
|
277
|
+
AND (${providerClauses})
|
|
278
|
+
ORDER BY time_created ASC`, [sinceMs, ...providerIds]);
|
|
279
|
+
}
|
|
252
280
|
/** Get the database file's mtime (ms epoch). Returns 0 if unavailable. */
|
|
253
281
|
getDbMtime() {
|
|
254
282
|
try {
|
package/dist/quota.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface QuotaState {
|
|
|
28
28
|
/** Projected 7-day utilization at reset (percentage, capped at 200) */
|
|
29
29
|
projectedSevenDay?: number;
|
|
30
30
|
/** Provider that produced this quota sample */
|
|
31
|
-
providerId?: 'claude-code' | 'codex';
|
|
31
|
+
providerId?: 'claude-code' | 'codex' | 'zai';
|
|
32
32
|
/** Source of the sample */
|
|
33
33
|
source?: 'api' | 'session' | 'cache';
|
|
34
34
|
/** ISO timestamp when the sample was captured */
|
package/dist/quotaHistory.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* dashboard, the `sidekick quota history` CLI, contextful_desktop) can render heatmaps and
|
|
7
7
|
* trend visualisations over a 13-week window.
|
|
8
8
|
*/
|
|
9
|
-
export type QuotaHistoryRuntimeProvider = 'claude' | 'codex';
|
|
9
|
+
export type QuotaHistoryRuntimeProvider = 'claude' | 'codex' | 'zai';
|
|
10
10
|
export interface QuotaHistorySample {
|
|
11
11
|
timestamp: string;
|
|
12
12
|
runtimeProvider: QuotaHistoryRuntimeProvider;
|
package/dist/quotaHistory.js
CHANGED
|
@@ -201,7 +201,7 @@ function pruneFileSync(filePath, retentionDays) {
|
|
|
201
201
|
return { kept: keptLines.length, pruned };
|
|
202
202
|
}
|
|
203
203
|
function sampleToQuotaState(sample) {
|
|
204
|
-
const providerId = sample.runtimeProvider
|
|
204
|
+
const providerId = runtimeProviderToSnapshotProvider(sample.runtimeProvider);
|
|
205
205
|
return {
|
|
206
206
|
fiveHour: { utilization: sample.fiveHour.utilization, resetsAt: sample.fiveHour.resetsAt },
|
|
207
207
|
sevenDay: { utilization: sample.sevenDay.utilization, resetsAt: sample.sevenDay.resetsAt },
|
|
@@ -213,6 +213,18 @@ function sampleToQuotaState(sample) {
|
|
|
213
213
|
stale: sample.stale,
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Maps a runtime provider to the snapshot storage key.
|
|
218
|
+
* `'zai'` is allowed at the storage layer even though it is not part of
|
|
219
|
+
* `AccountProviderId` (z.ai has no full account management in v1).
|
|
220
|
+
*/
|
|
221
|
+
function runtimeProviderToSnapshotProvider(runtime) {
|
|
222
|
+
if (runtime === 'claude')
|
|
223
|
+
return 'claude-code';
|
|
224
|
+
if (runtime === 'zai')
|
|
225
|
+
return 'zai';
|
|
226
|
+
return 'codex';
|
|
227
|
+
}
|
|
216
228
|
async function runAppend(sample, filePath, options) {
|
|
217
229
|
let lastTs = lastWriteCache.get(filePath);
|
|
218
230
|
if (lastTs === undefined) {
|
|
@@ -243,7 +255,7 @@ async function runAppend(sample, filePath, options) {
|
|
|
243
255
|
// Backwards-compat: keep the latest-snapshot store hot so existing callers (DashboardViewProvider,
|
|
244
256
|
// codex session provider, contextful_desktop) don't have to query history for "latest".
|
|
245
257
|
try {
|
|
246
|
-
const providerId = sample.runtimeProvider
|
|
258
|
+
const providerId = runtimeProviderToSnapshotProvider(sample.runtimeProvider);
|
|
247
259
|
(0, quotaSnapshots_1.writeQuotaSnapshot)(providerId, sample.providerId, sampleToQuotaState(sample));
|
|
248
260
|
}
|
|
249
261
|
catch {
|
package/dist/quotaSnapshots.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { QuotaState } from './quota';
|
|
2
2
|
import type { AccountProviderId } from './accountRegistry';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Storage key for a quota snapshot. Extends `AccountProviderId` with `'zai'`
|
|
5
|
+
* because z.ai has no full account-management surface in v1, but the snapshot
|
|
6
|
+
* store still needs a stable key for the derived z.ai quota cache.
|
|
7
|
+
*/
|
|
8
|
+
export type QuotaSnapshotProviderId = AccountProviderId | 'zai';
|
|
9
|
+
export declare function writeQuotaSnapshot(providerId: QuotaSnapshotProviderId, accountId: string, quota: QuotaState): void;
|
|
10
|
+
export declare function readQuotaSnapshot(providerId: QuotaSnapshotProviderId, accountId: string): QuotaState | null;
|