sidekick-shared 0.28.2 → 0.29.0

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.
Files changed (142) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +176 -30
  3. package/dist/authStatus.d.ts +54 -0
  4. package/dist/authStatus.js +91 -0
  5. package/dist/browser.d.ts +14 -6
  6. package/dist/browser.js +26 -1
  7. package/dist/codex.d.ts +23 -0
  8. package/dist/codex.js +44 -0
  9. package/dist/codexProfiles.d.ts +1 -0
  10. package/dist/codexProfiles.js +8 -11
  11. package/dist/codexQuota.js +17 -4
  12. package/dist/esm/authStatus.js +87 -0
  13. package/dist/esm/browser.js +6 -2
  14. package/dist/esm/codex.js +18 -0
  15. package/dist/esm/codexProfiles.js +8 -11
  16. package/dist/esm/codexQuota.js +18 -5
  17. package/dist/esm/formatting.js +10 -1
  18. package/dist/esm/index.js +12 -9
  19. package/dist/esm/modelCatalog.js +2 -1
  20. package/dist/esm/modelInfo.js +422 -45
  21. package/dist/esm/multiProviderQuotaService.js +1 -1
  22. package/dist/esm/node.js +9 -3
  23. package/dist/esm/parsers/codexParser.js +175 -54
  24. package/dist/esm/parsers/codexRolloutOrigin.js +107 -0
  25. package/dist/esm/parsers/openCodeParser.js +83 -21
  26. package/dist/esm/pricing.js +18 -0
  27. package/dist/esm/pricingCatalog.js +14 -7
  28. package/dist/esm/promptHistory.js +66 -4
  29. package/dist/esm/providerFailure.js +124 -3
  30. package/dist/esm/providers/codex.js +2 -24
  31. package/dist/esm/providers/codexDatabase.js +3 -19
  32. package/dist/esm/providers/codexRollouts.js +24 -1
  33. package/dist/esm/providers/openCode.js +9 -17
  34. package/dist/esm/quota.js +356 -24
  35. package/dist/esm/quotaHistory.js +33 -8
  36. package/dist/esm/quotaPoller.js +5 -2
  37. package/dist/esm/quotaResolver.js +31 -6
  38. package/dist/esm/quotaSnapshots.js +65 -10
  39. package/dist/esm/quotaThresholds.js +7 -2
  40. package/dist/esm/report/htmlReportGenerator.js +42 -5
  41. package/dist/esm/report/transcriptParser.js +13 -11
  42. package/dist/esm/schemas/index.js +2 -2
  43. package/dist/esm/schemas/providerFailure.js +3 -1
  44. package/dist/esm/schemas/quota.js +30 -0
  45. package/dist/esm/schemas/quotaHistory.js +10 -2
  46. package/dist/esm/schemas/stateFile.js +9 -0
  47. package/dist/esm/schemas/transcript.js +4 -0
  48. package/dist/esm/sessionPromptHistory.js +2 -2
  49. package/dist/esm/stateFile.js +18 -3
  50. package/dist/esm/statusline/claudeStatuslinePayload.js +16 -4
  51. package/dist/esm/statusline/formatter.js +57 -18
  52. package/dist/esm/statusline/index.js +2 -2
  53. package/dist/esm/statusline/payload.js +9 -0
  54. package/dist/esm/testing/credentialGuard.js +87 -5
  55. package/dist/esm/toolCallOrigins.js +310 -0
  56. package/dist/esm/transcript.js +42 -3
  57. package/dist/esm/usage/usageEvents.js +146 -17
  58. package/dist/esm/usage/usageReports.js +39 -6
  59. package/dist/esm/watchers/jsonlTail.js +378 -59
  60. package/dist/esm/zaiQuotaApi.js +4 -2
  61. package/dist/formatting.d.ts +26 -1
  62. package/dist/formatting.js +11 -1
  63. package/dist/index.d.ts +22 -16
  64. package/dist/index.js +48 -12
  65. package/dist/modelCatalog.js +2 -1
  66. package/dist/modelInfo.d.ts +185 -16
  67. package/dist/modelInfo.js +431 -47
  68. package/dist/multiProviderQuotaService.js +1 -1
  69. package/dist/node.d.ts +16 -5
  70. package/dist/node.js +31 -2
  71. package/dist/parsers/codexParser.d.ts +55 -0
  72. package/dist/parsers/codexParser.js +175 -54
  73. package/dist/parsers/codexRolloutOrigin.d.ts +49 -0
  74. package/dist/parsers/codexRolloutOrigin.js +111 -0
  75. package/dist/parsers/openCodeParser.d.ts +38 -0
  76. package/dist/parsers/openCodeParser.js +86 -21
  77. package/dist/pricing.d.ts +22 -0
  78. package/dist/pricing.js +50 -0
  79. package/dist/pricingCatalog.d.ts +10 -0
  80. package/dist/pricingCatalog.js +13 -6
  81. package/dist/promptHistory.d.ts +65 -4
  82. package/dist/promptHistory.js +68 -5
  83. package/dist/providerFailure.d.ts +44 -0
  84. package/dist/providerFailure.js +126 -4
  85. package/dist/providers/codex.d.ts +3 -22
  86. package/dist/providers/codex.js +3 -26
  87. package/dist/providers/codexDatabase.d.ts +3 -2
  88. package/dist/providers/codexDatabase.js +4 -22
  89. package/dist/providers/codexRollouts.d.ts +22 -0
  90. package/dist/providers/codexRollouts.js +24 -0
  91. package/dist/providers/openCode.js +8 -16
  92. package/dist/quota.d.ts +131 -1
  93. package/dist/quota.js +366 -24
  94. package/dist/quotaHistory.d.ts +10 -0
  95. package/dist/quotaHistory.js +34 -8
  96. package/dist/quotaPoller.d.ts +11 -3
  97. package/dist/quotaPoller.js +5 -2
  98. package/dist/quotaResolver.js +30 -5
  99. package/dist/quotaSnapshots.js +64 -9
  100. package/dist/quotaThresholds.js +7 -2
  101. package/dist/report/htmlReportGenerator.js +42 -5
  102. package/dist/report/transcriptParser.d.ts +10 -1
  103. package/dist/report/transcriptParser.js +13 -11
  104. package/dist/schemas/index.d.ts +3 -3
  105. package/dist/schemas/index.js +5 -2
  106. package/dist/schemas/providerFailure.d.ts +39 -0
  107. package/dist/schemas/providerFailure.js +2 -0
  108. package/dist/schemas/quota.d.ts +258 -0
  109. package/dist/schemas/quota.js +31 -1
  110. package/dist/schemas/quotaHistory.d.ts +2 -0
  111. package/dist/schemas/quotaHistory.js +10 -2
  112. package/dist/schemas/stateFile.d.ts +34 -0
  113. package/dist/schemas/stateFile.js +10 -1
  114. package/dist/schemas/transcript.d.ts +2 -0
  115. package/dist/schemas/transcript.js +4 -0
  116. package/dist/sessionPromptHistory.d.ts +7 -1
  117. package/dist/sessionPromptHistory.js +1 -1
  118. package/dist/stateFile.d.ts +18 -0
  119. package/dist/stateFile.js +17 -2
  120. package/dist/statusline/claudeStatuslinePayload.d.ts +4 -3
  121. package/dist/statusline/claudeStatuslinePayload.js +16 -4
  122. package/dist/statusline/formatter.d.ts +5 -0
  123. package/dist/statusline/formatter.js +56 -17
  124. package/dist/statusline/index.d.ts +4 -4
  125. package/dist/statusline/index.js +5 -1
  126. package/dist/statusline/payload.d.ts +10 -0
  127. package/dist/statusline/payload.js +14 -0
  128. package/dist/testing/credentialGuard.d.ts +14 -0
  129. package/dist/testing/credentialGuard.js +87 -5
  130. package/dist/toolCallOrigins.d.ts +70 -0
  131. package/dist/toolCallOrigins.js +346 -0
  132. package/dist/transcript.d.ts +12 -0
  133. package/dist/transcript.js +42 -3
  134. package/dist/types/opencode.d.ts +2 -0
  135. package/dist/usage/usageEvents.d.ts +54 -6
  136. package/dist/usage/usageEvents.js +146 -17
  137. package/dist/usage/usageReports.d.ts +35 -4
  138. package/dist/usage/usageReports.js +39 -6
  139. package/dist/watchers/jsonlTail.d.ts +101 -4
  140. package/dist/watchers/jsonlTail.js +380 -60
  141. package/dist/zaiQuotaApi.js +3 -1
  142. package/package.json +41 -1
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /**
3
+ * Pure parsers for the provider CLIs' read-only sign-in status commands,
4
+ * `claude auth status --json` and `codex login status`. They do no I/O. The
5
+ * caller runs the command with its own environment, home, and timeout, and
6
+ * passes the output here.
7
+ *
8
+ * A status command reports which credential the CLI would use. It does not
9
+ * prove that a request will authenticate: an expired or revoked token can
10
+ * still read as logged in.
11
+ *
12
+ * @module authStatus
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.parseClaudeAuthStatus = parseClaudeAuthStatus;
16
+ exports.parseCodexLoginStatus = parseCodexLoginStatus;
17
+ /** Larger outputs are not a status report; reading stops there. */
18
+ const MAX_STATUS_OUTPUT = 256 * 1024;
19
+ const MAX_DETAIL = 200;
20
+ function stringField(record, key) {
21
+ const value = record[key];
22
+ if (typeof value !== 'string')
23
+ return undefined;
24
+ const trimmed = value.trim();
25
+ return trimmed && trimmed.length <= 512 ? trimmed : undefined;
26
+ }
27
+ function parseJsonObject(output) {
28
+ const trimmed = output.trim();
29
+ const candidates = [trimmed];
30
+ // Tolerate warning lines printed ahead of the JSON document, which must
31
+ // then start a line and run to the end of the output.
32
+ const start = trimmed.search(/\n[ \t]*\{/);
33
+ if (start >= 0)
34
+ candidates.push(trimmed.slice(start).trim());
35
+ for (const candidate of candidates) {
36
+ if (!candidate.startsWith('{') || !candidate.endsWith('}'))
37
+ continue;
38
+ try {
39
+ const parsed = JSON.parse(candidate);
40
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
41
+ return parsed;
42
+ }
43
+ catch {
44
+ /* Try the next candidate. */
45
+ }
46
+ }
47
+ return undefined;
48
+ }
49
+ /**
50
+ * Parse the stdout of `claude auth status --json`. Claude Code exits 1 while
51
+ * printing valid signed-out JSON, so parse stdout whatever the exit status
52
+ * was. Only a JSON `"loggedIn": true` counts as logged in.
53
+ */
54
+ function parseClaudeAuthStatus(stdout) {
55
+ const raw = typeof stdout === 'string' && stdout.length <= MAX_STATUS_OUTPUT
56
+ ? parseJsonObject(stdout)
57
+ : undefined;
58
+ if (!raw)
59
+ return { loggedIn: false };
60
+ const method = stringField(raw, 'authMethod');
61
+ const email = stringField(raw, 'email');
62
+ const subscriptionType = stringField(raw, 'subscriptionType');
63
+ return {
64
+ loggedIn: raw.loggedIn === true,
65
+ ...(method ? { method } : {}),
66
+ ...(email ? { email } : {}),
67
+ ...(subscriptionType ? { subscriptionType } : {}),
68
+ raw,
69
+ };
70
+ }
71
+ /**
72
+ * Parse the output of `codex login status`. Codex prints its status line to
73
+ * stderr (`Logged in using ChatGPT`, `Logged in using an API key - sk-…`,
74
+ * `Not logged in`), so pass both streams. The exit status is not consulted:
75
+ * callers that require exit 0 check it themselves.
76
+ */
77
+ function parseCodexLoginStatus(stdout, stderr) {
78
+ const combined = `${typeof stdout === 'string' ? stdout : ''}\n${typeof stderr === 'string' ? stderr : ''}`.trim();
79
+ const firstLine = combined.split(/\r?\n/, 1)[0]?.trim() ?? '';
80
+ const detail = (/^(Logged in using .*?API key)\s+-\s.*$/i.exec(firstLine)?.[1] ?? firstLine)
81
+ .slice(0, MAX_DETAIL)
82
+ .trim();
83
+ const withDetail = detail ? { detail } : {};
84
+ if (!/^Logged in/i.test(combined))
85
+ return { loggedIn: false, ...withDetail };
86
+ if (/API key/i.test(combined))
87
+ return { loggedIn: true, method: 'api-key', ...withDetail };
88
+ if (/ChatGPT/i.test(combined))
89
+ return { loggedIn: true, method: 'chatgpt', ...withDetail };
90
+ return { loggedIn: true, ...withDetail };
91
+ }
package/dist/browser.d.ts CHANGED
@@ -24,12 +24,14 @@ export { parseMcpToolName } from './parsers/mcpToolName';
24
24
  export type { McpToolNameParts } from './parsers/mcpToolName';
25
25
  export { buildSessionContextSnapshot, calculateSessionContextPressure, createSessionContextProjector, } from './context/sessionContext';
26
26
  export { ACCOUNT_PROVIDER_IDS, MODEL_PROVIDER_IDS, QUOTA_PROVIDER_IDS, RUNTIME_QUOTA_PROVIDER_IDS, SESSION_PROVIDER_IDS, } from './providerIds';
27
- export type { AccountProviderId, ModelProviderId, QuotaProviderId, RuntimeQuotaProviderId, SessionProviderId, } from './providerIds';
27
+ export type { AccountProviderId, ModelProviderId, QuotaProviderId, RuntimeQuotaProviderId, SessionProviderId,
28
+ /** Same type as `ProviderId` from the package root and `sidekick-shared/node`. */
29
+ SessionProviderId as ProviderId, } from './providerIds';
28
30
  export type { BuildSessionContextSnapshotOptions, SessionContextCapabilities, SessionContextLayerBreakdown, SessionContextPressure, SessionContextProjector, SessionContextSnapshot, SessionContextSource, SessionContextSourceType, } from './context/sessionContext';
29
31
  export { assistantTurnEventsFromSessionEvents, extractTurnSubagents, isAssistantTurnSubagentTool, reasoningSummary, segmentAssistantTurn, } from './turns/assistantTurn';
30
32
  export type { AssistantTurnEvent, AssistantTurnEventType, AssistantTurnProcess, AssistantTurnProcessStep, AssistantTurnProjection, AssistantTurnSubagent, AssistantTurnSubagentStatus, AssistantTurnTimelineItem, AssistantTurnToolRef, ExtractTurnSubagentsOptions, ReasoningSummary, SegmentAssistantTurnOptions, } from './turns/assistantTurn';
31
- export { parseModelId, getModelPricing, resolveModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, } from './modelInfo';
32
- export type { ModelPricing, CostTokenUsage, CostSource, CostProvenanceInput, CostWithProvenance, ModelProvider, ParsedModelId, ModelInfo, ModelDisplayInfo, ModelPricingMatch, ModelPricingProvenance, ModelPricingSource, ResolvedModelPricing, } from './modelInfo';
33
+ export { parseModelId, getModelPricing, resolveModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, canonicalModelId, isMovingModelAlias, registerModelPricing, clearRegisteredModelPricing, getPricingCatalogInfo, costSourceFromPricingProvenance, mergeCostComponents, } from './modelInfo';
34
+ export type { ModelPricing, ResolveModelPricingOptions, RegisterModelPricingOptions, PricingCatalogInfo, PricingCatalogSource, CostComponent, FormatCostOptions, CostTokenUsage, CostSource, CostProvenanceInput, CostWithProvenance, ModelProvider, ParsedModelId, ModelInfo, ModelDisplayInfo, ModelPricingMatch, ModelPricingProvenance, ModelPricingSource, ResolvedModelPricing, } from './modelInfo';
33
35
  export { exportResolvedModelCatalog, importResolvedModelCatalog, registerModelAlias, resolveModelAlias, RESOLVED_MODEL_CATALOG_SCHEMA_VERSION, } from './modelCatalog';
34
36
  export type { ImportResolvedModelCatalogResult, ResolvedModelCatalogEntry, ResolvedModelCatalogSnapshot, } from './modelCatalog';
35
37
  export { BILLING_BLOCK_DURATION_MS, computeBillingBlocks, findActiveBillingBlock, } from './usage/billingBlocks';
@@ -40,17 +42,23 @@ export { applySessionSummary, isFileImported, markFileImported, removeSessionSum
40
42
  export type { ApplySessionSummaryOptions, SessionSummaryFromStatsOptions } from './historicalStore';
41
43
  export { FAILING_TOOL_TREND_ARROWS, failingToolTrend, mergeFailingToolWindows, } from './failingTools';
42
44
  export type { FailingToolTrend, FailingToolTrendRow, FailingToolWindow } from './failingTools';
43
- export { diagnoseProviderFailure, PROVIDER_AUTHENTICATION_SOURCES, PROVIDER_AUTHENTICATION_STATES, PROVIDER_CREDENTIAL_KINDS, PROVIDER_FAILURE_CODES, PROVIDER_FAILURE_EVIDENCE_SOURCES, PROVIDER_RECOVERY_ACTIONS, } from './providerFailure';
44
- export type { ProviderCredentialKind, ProviderAuthenticationEvidence, ProviderFailureInput, ProviderFailureCode, ProviderRecoveryAction, ProviderRetryAfter, ProviderFailureEvidence, ProviderFailureDiagnosis, } from './providerFailure';
45
+ export { diagnoseProviderFailure, PROVIDER_AUTHENTICATION_SOURCES, PROVIDER_AUTHENTICATION_STATES, PROVIDER_CREDENTIAL_KINDS, PROVIDER_FAILURE_CODES, PROVIDER_FAILURE_EVIDENCE_SOURCES, PROVIDER_RECOVERY_ACTIONS, CODEX_ERROR_INFO_KINDS, decodeCodexErrorInfo, } from './providerFailure';
46
+ export { parseClaudeAuthStatus, parseCodexLoginStatus } from './authStatus';
47
+ export type { ParsedClaudeAuthStatus, ParsedCodexLoginStatus } from './authStatus';
48
+ export type { CodexErrorInfoKind, CodexErrorInfoSummary, ProviderCredentialKind, ProviderAuthenticationEvidence, ProviderFailureInput, ProviderFailureCode, ProviderRecoveryAction, ProviderRetryAfter, ProviderFailureEvidence, ProviderFailureDiagnosis, } from './providerFailure';
45
49
  export type { ProviderServiceSeverity, ProviderServiceComponent, ProviderServiceIncident, ProviderServiceStatusFailureReason, ObservedProviderServiceStatus, UnavailableProviderServiceStatus, ProviderServiceStatus, FetchProviderServiceStatusOptions, } from './providerServiceStatusTypes';
46
50
  export type { SessionEvent, ClaudeSessionEvent, MessageUsage, SessionMessage, SessionEventBase, MessageSessionEvent, UserSessionEvent, AssistantSessionEvent, SummarySessionEvent, SystemSessionEvent, ToolUseSessionEvent, ToolResultSessionEvent, TokenUsage, ToolCall, ToolAnalytics, TimelineEvent, PendingToolCall, TaskStatus as SessionTaskStatus, TrackedTask, TaskState, SubagentStats, PendingUserRequest, ResponseLatency, LatencyStats, PlanStep, PlanState, SessionStats, CompactionEvent, TruncationEvent, ContextAttribution, TurnAttribution, ContextSizePoint, PermissionMode, PermissionModeChange, } from './types/sessionEvent';
47
51
  export type { CodexRolloutLine, CodexSessionMeta, CodexResponseItem, CodexMessageItem, CodexContentPart, CodexReasoningItem, CodexFunctionCallItem, CodexFunctionCallOutputItem, CodexLocalShellCallItem, CodexWebSearchCallItem, CodexCustomToolCallItem, CodexCustomToolCallOutputItem, CodexCompacted, CodexTurnContext, CodexEventMsg, CodexEvent, CodexTurnStartedEvent, CodexTurnCompleteEvent, CodexTaskStartedEvent, CodexTaskCompleteEvent, CodexTurnAbortedEvent, CodexTokenCountEvent, CodexTokenUsage, CodexRateLimits, CodexAgentMessageEvent, CodexAgentReasoningEvent, CodexUserMessageEvent, CodexExecCommandBeginEvent, CodexExecCommandEndEvent, CodexMcpToolCallBeginEvent, CodexMcpToolCallEndEvent, CodexErrorEvent, CodexContextCompactedEvent, CodexPatchAppliedEvent, CodexBackgroundEvent, CodexGenericEvent, CodexDbThread, CodexReasoningSummary, } from './types/codex';
48
52
  export { isAggregateCodexLimit } from './types/codex';
49
53
  export { CodexRolloutParser, DEFAULT_MAX_TRACKED_TOOL_CALLS, extractPatchFilePaths, normalizeCodexToolName, normalizeCodexToolInput, resolveCodexCallUsage, } from './parsers/codexParser';
50
- export type { CodexRolloutParserOptions } from './parsers/codexParser';
54
+ export type { CodexPendingStats, CodexRolloutParserOptions } from './parsers/codexParser';
55
+ export { codexRolloutOrigin, codexSpawnParentFromSource } from './parsers/codexRolloutOrigin';
56
+ export type { CodexRolloutOrigin } from './parsers/codexRolloutOrigin';
51
57
  export { HUMAN_CLAUDE_ENTRYPOINTS, HUMAN_CODEX_SOURCES, humanPromptText, isHumanPrompt, } from './humanPrompt';
52
58
  export type { HumanPromptProvider } from './humanPrompt';
53
59
  export { renderHandoffUrlTemplate } from './handoffUrl';
54
60
  export type { HandoffUrlIdentifiers } from './handoffUrl';
55
61
  export { classifyTool, TOOL_CATEGORIES } from './toolCategory';
56
62
  export type { ClassifyToolOptions, ToolCategory } from './toolCategory';
63
+ export { claudePlanLabel, extraQuotaRows, formatExtraUsage, quotaWindowApplies, quotaWindowHasData, quotaWindowLabel, quotaWindowShortLabel, } from './quota';
64
+ export type { QuotaExtraUsage, QuotaLimitRow, QuotaWindow } from './quota';
package/dist/browser.js CHANGED
@@ -10,7 +10,8 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.resolveModelPricing = exports.getModelPricing = exports.parseModelId = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.SESSION_PROVIDER_IDS = exports.RUNTIME_QUOTA_PROVIDER_IDS = exports.QUOTA_PROVIDER_IDS = exports.MODEL_PROVIDER_IDS = exports.ACCOUNT_PROVIDER_IDS = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.parseMcpToolName = exports.parseLocalDateKey = exports.formatTokenCount = exports.formatLocalDateKey = exports.formatDurationMs = exports.addLocalDays = exports.projectSessionTranscript = exports.estimateSerializedTokens = exports.estimateTextTokens = exports.ASCII_CHARS_PER_TOKEN = exports.SYNTHETIC_MODEL = exports.ClaudeUsageDeduper = exports.TOKEN_TOTAL_LABEL = exports.TOKEN_SUBAGENTS_LABEL = exports.TOKEN_SESSION_TOTAL_LABEL = exports.TOKEN_MAIN_THREAD_LABEL = exports.TOKEN_CONTEXT_LABEL = exports.sumTokenTotals = exports.summarizeTokens = exports.formatTokenBreakdown = exports.calculateNormalizedUsageCost = exports.extractNormalizedUsage = exports.normalizeProviderUsage = exports.DEFAULT_CONTEXT_WINDOW = exports.resolveModelContextWindow = exports.getModelContextWindowSize = void 0;
13
- exports.TOOL_CATEGORIES = exports.classifyTool = exports.renderHandoffUrlTemplate = exports.isHumanPrompt = exports.humanPromptText = exports.HUMAN_CODEX_SOURCES = exports.HUMAN_CLAUDE_ENTRYPOINTS = exports.resolveCodexCallUsage = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.DEFAULT_MAX_TRACKED_TOOL_CALLS = exports.CodexRolloutParser = exports.isAggregateCodexLimit = exports.PROVIDER_RECOVERY_ACTIONS = exports.PROVIDER_FAILURE_EVIDENCE_SOURCES = exports.PROVIDER_FAILURE_CODES = exports.PROVIDER_CREDENTIAL_KINDS = exports.PROVIDER_AUTHENTICATION_STATES = exports.PROVIDER_AUTHENTICATION_SOURCES = exports.diagnoseProviderFailure = exports.mergeFailingToolWindows = exports.failingToolTrend = exports.FAILING_TOOL_TREND_ARROWS = exports.sessionSummaryFromStats = exports.removeSessionSummary = exports.markFileImported = exports.isFileImported = exports.applySessionSummary = exports.weekKey = exports.usageBucketKey = exports.summarizeUsageRows = exports.bucketUsage = exports.findActiveBillingBlock = exports.computeBillingBlocks = exports.BILLING_BLOCK_DURATION_MS = exports.RESOLVED_MODEL_CATALOG_SCHEMA_VERSION = exports.resolveModelAlias = exports.registerModelAlias = exports.importResolvedModelCatalog = exports.exportResolvedModelCatalog = exports.formatCost = exports.sortModelIds = void 0;
13
+ exports.HUMAN_CLAUDE_ENTRYPOINTS = exports.codexSpawnParentFromSource = exports.codexRolloutOrigin = exports.resolveCodexCallUsage = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.DEFAULT_MAX_TRACKED_TOOL_CALLS = exports.CodexRolloutParser = exports.isAggregateCodexLimit = exports.parseCodexLoginStatus = exports.parseClaudeAuthStatus = exports.decodeCodexErrorInfo = exports.CODEX_ERROR_INFO_KINDS = exports.PROVIDER_RECOVERY_ACTIONS = exports.PROVIDER_FAILURE_EVIDENCE_SOURCES = exports.PROVIDER_FAILURE_CODES = exports.PROVIDER_CREDENTIAL_KINDS = exports.PROVIDER_AUTHENTICATION_STATES = exports.PROVIDER_AUTHENTICATION_SOURCES = exports.diagnoseProviderFailure = exports.mergeFailingToolWindows = exports.failingToolTrend = exports.FAILING_TOOL_TREND_ARROWS = exports.sessionSummaryFromStats = exports.removeSessionSummary = exports.markFileImported = exports.isFileImported = exports.applySessionSummary = exports.weekKey = exports.usageBucketKey = exports.summarizeUsageRows = exports.bucketUsage = exports.findActiveBillingBlock = exports.computeBillingBlocks = exports.BILLING_BLOCK_DURATION_MS = exports.RESOLVED_MODEL_CATALOG_SCHEMA_VERSION = exports.resolveModelAlias = exports.registerModelAlias = exports.importResolvedModelCatalog = exports.exportResolvedModelCatalog = exports.mergeCostComponents = exports.costSourceFromPricingProvenance = exports.getPricingCatalogInfo = exports.clearRegisteredModelPricing = exports.registerModelPricing = exports.isMovingModelAlias = exports.canonicalModelId = exports.formatCost = exports.sortModelIds = void 0;
14
+ exports.quotaWindowShortLabel = exports.quotaWindowLabel = exports.quotaWindowHasData = exports.quotaWindowApplies = exports.formatExtraUsage = exports.extraQuotaRows = exports.claudePlanLabel = exports.TOOL_CATEGORIES = exports.classifyTool = exports.renderHandoffUrlTemplate = exports.isHumanPrompt = exports.humanPromptText = exports.HUMAN_CODEX_SOURCES = void 0;
14
15
  var modelContext_1 = require("./modelContext");
15
16
  Object.defineProperty(exports, "getModelContextWindowSize", { enumerable: true, get: function () { return modelContext_1.getModelContextWindowSize; } });
16
17
  Object.defineProperty(exports, "resolveModelContextWindow", { enumerable: true, get: function () { return modelContext_1.resolveModelContextWindow; } });
@@ -75,6 +76,13 @@ Object.defineProperty(exports, "getModelDisplayInfo", { enumerable: true, get: f
75
76
  Object.defineProperty(exports, "compareModelIds", { enumerable: true, get: function () { return modelInfo_1.compareModelIds; } });
76
77
  Object.defineProperty(exports, "sortModelIds", { enumerable: true, get: function () { return modelInfo_1.sortModelIds; } });
77
78
  Object.defineProperty(exports, "formatCost", { enumerable: true, get: function () { return modelInfo_1.formatCost; } });
79
+ Object.defineProperty(exports, "canonicalModelId", { enumerable: true, get: function () { return modelInfo_1.canonicalModelId; } });
80
+ Object.defineProperty(exports, "isMovingModelAlias", { enumerable: true, get: function () { return modelInfo_1.isMovingModelAlias; } });
81
+ Object.defineProperty(exports, "registerModelPricing", { enumerable: true, get: function () { return modelInfo_1.registerModelPricing; } });
82
+ Object.defineProperty(exports, "clearRegisteredModelPricing", { enumerable: true, get: function () { return modelInfo_1.clearRegisteredModelPricing; } });
83
+ Object.defineProperty(exports, "getPricingCatalogInfo", { enumerable: true, get: function () { return modelInfo_1.getPricingCatalogInfo; } });
84
+ Object.defineProperty(exports, "costSourceFromPricingProvenance", { enumerable: true, get: function () { return modelInfo_1.costSourceFromPricingProvenance; } });
85
+ Object.defineProperty(exports, "mergeCostComponents", { enumerable: true, get: function () { return modelInfo_1.mergeCostComponents; } });
78
86
  var modelCatalog_1 = require("./modelCatalog");
79
87
  Object.defineProperty(exports, "exportResolvedModelCatalog", { enumerable: true, get: function () { return modelCatalog_1.exportResolvedModelCatalog; } });
80
88
  Object.defineProperty(exports, "importResolvedModelCatalog", { enumerable: true, get: function () { return modelCatalog_1.importResolvedModelCatalog; } });
@@ -108,6 +116,11 @@ Object.defineProperty(exports, "PROVIDER_CREDENTIAL_KINDS", { enumerable: true,
108
116
  Object.defineProperty(exports, "PROVIDER_FAILURE_CODES", { enumerable: true, get: function () { return providerFailure_1.PROVIDER_FAILURE_CODES; } });
109
117
  Object.defineProperty(exports, "PROVIDER_FAILURE_EVIDENCE_SOURCES", { enumerable: true, get: function () { return providerFailure_1.PROVIDER_FAILURE_EVIDENCE_SOURCES; } });
110
118
  Object.defineProperty(exports, "PROVIDER_RECOVERY_ACTIONS", { enumerable: true, get: function () { return providerFailure_1.PROVIDER_RECOVERY_ACTIONS; } });
119
+ Object.defineProperty(exports, "CODEX_ERROR_INFO_KINDS", { enumerable: true, get: function () { return providerFailure_1.CODEX_ERROR_INFO_KINDS; } });
120
+ Object.defineProperty(exports, "decodeCodexErrorInfo", { enumerable: true, get: function () { return providerFailure_1.decodeCodexErrorInfo; } });
121
+ var authStatus_1 = require("./authStatus");
122
+ Object.defineProperty(exports, "parseClaudeAuthStatus", { enumerable: true, get: function () { return authStatus_1.parseClaudeAuthStatus; } });
123
+ Object.defineProperty(exports, "parseCodexLoginStatus", { enumerable: true, get: function () { return authStatus_1.parseCodexLoginStatus; } });
111
124
  var codex_1 = require("./types/codex");
112
125
  Object.defineProperty(exports, "isAggregateCodexLimit", { enumerable: true, get: function () { return codex_1.isAggregateCodexLimit; } });
113
126
  // Codex rollout parsing (pure: no filesystem access)
@@ -118,6 +131,9 @@ Object.defineProperty(exports, "extractPatchFilePaths", { enumerable: true, get:
118
131
  Object.defineProperty(exports, "normalizeCodexToolName", { enumerable: true, get: function () { return codexParser_1.normalizeCodexToolName; } });
119
132
  Object.defineProperty(exports, "normalizeCodexToolInput", { enumerable: true, get: function () { return codexParser_1.normalizeCodexToolInput; } });
120
133
  Object.defineProperty(exports, "resolveCodexCallUsage", { enumerable: true, get: function () { return codexParser_1.resolveCodexCallUsage; } });
134
+ var codexRolloutOrigin_1 = require("./parsers/codexRolloutOrigin");
135
+ Object.defineProperty(exports, "codexRolloutOrigin", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexRolloutOrigin; } });
136
+ Object.defineProperty(exports, "codexSpawnParentFromSource", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexSpawnParentFromSource; } });
121
137
  var humanPrompt_1 = require("./humanPrompt");
122
138
  Object.defineProperty(exports, "HUMAN_CLAUDE_ENTRYPOINTS", { enumerable: true, get: function () { return humanPrompt_1.HUMAN_CLAUDE_ENTRYPOINTS; } });
123
139
  Object.defineProperty(exports, "HUMAN_CODEX_SOURCES", { enumerable: true, get: function () { return humanPrompt_1.HUMAN_CODEX_SOURCES; } });
@@ -129,3 +145,12 @@ Object.defineProperty(exports, "renderHandoffUrlTemplate", { enumerable: true, g
129
145
  var toolCategory_1 = require("./toolCategory");
130
146
  Object.defineProperty(exports, "classifyTool", { enumerable: true, get: function () { return toolCategory_1.classifyTool; } });
131
147
  Object.defineProperty(exports, "TOOL_CATEGORIES", { enumerable: true, get: function () { return toolCategory_1.TOOL_CATEGORIES; } });
148
+ // Quota window helpers (pure; safe for webviews)
149
+ var quota_1 = require("./quota");
150
+ Object.defineProperty(exports, "claudePlanLabel", { enumerable: true, get: function () { return quota_1.claudePlanLabel; } });
151
+ Object.defineProperty(exports, "extraQuotaRows", { enumerable: true, get: function () { return quota_1.extraQuotaRows; } });
152
+ Object.defineProperty(exports, "formatExtraUsage", { enumerable: true, get: function () { return quota_1.formatExtraUsage; } });
153
+ Object.defineProperty(exports, "quotaWindowApplies", { enumerable: true, get: function () { return quota_1.quotaWindowApplies; } });
154
+ Object.defineProperty(exports, "quotaWindowHasData", { enumerable: true, get: function () { return quota_1.quotaWindowHasData; } });
155
+ Object.defineProperty(exports, "quotaWindowLabel", { enumerable: true, get: function () { return quota_1.quotaWindowLabel; } });
156
+ Object.defineProperty(exports, "quotaWindowShortLabel", { enumerable: true, get: function () { return quota_1.quotaWindowShortLabel; } });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `sidekick-shared/codex` — a small Node-only entry for Codex homes, the
3
+ * child-process environment, `auth.json` helpers, credential-store mode,
4
+ * rollout lookup, and the atomic JSON writers those helpers rely on.
5
+ *
6
+ * Loads a handful of modules and no Zod, so a host that only launches or
7
+ * locates Codex does not pay for the full `sidekick-shared/node` surface.
8
+ * Everything here is also exported from the package root and from
9
+ * `sidekick-shared/node`.
10
+ *
11
+ * @module codex
12
+ */
13
+ export { getSystemCodexHome, resolveSidekickCodexHome, getCodexExecutionEnv, getCodexMonitoringHomes, getCodexSessionsDirs, getCodexCredentialStoreMode, forceFileCredentialStore, CODEX_SESSIONS_SUBDIR, CODEX_ARCHIVED_SESSIONS_SUBDIR, } from './codexPaths';
14
+ export type { CodexCredentialStoreMode, CodexSessionsDirsOptions } from './codexPaths';
15
+ export { parseAuthJson as parseCodexAuthJson, readAuthIdentityFromRaw as readCodexAuthIdentity, readLastRefresh as readCodexLastRefresh, readAccessTokenExpiry as readCodexAccessTokenExpiry, identitiesMatch as codexIdentitiesMatch, codexRefreshFingerprint, compareCodexAuthFreshness, } from './codexAuth';
16
+ export type { CodexAuthIdentity, CodexAuthJsonFile } from './codexAuth';
17
+ export { findCodexRolloutFile } from './providers/codexRollouts';
18
+ export type { FindCodexRolloutFileOptions } from './providers/codexRollouts';
19
+ export { atomicWriteJson, atomicWriteJsonSync } from './writers/atomic';
20
+ export { parseClaudeAuthStatus, parseCodexLoginStatus } from './authStatus';
21
+ export type { ParsedClaudeAuthStatus, ParsedCodexLoginStatus } from './authStatus';
22
+ export { codexRolloutOrigin, codexSpawnParentFromSource } from './parsers/codexRolloutOrigin';
23
+ export type { CodexRolloutOrigin } from './parsers/codexRolloutOrigin';
package/dist/codex.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * `sidekick-shared/codex` — a small Node-only entry for Codex homes, the
4
+ * child-process environment, `auth.json` helpers, credential-store mode,
5
+ * rollout lookup, and the atomic JSON writers those helpers rely on.
6
+ *
7
+ * Loads a handful of modules and no Zod, so a host that only launches or
8
+ * locates Codex does not pay for the full `sidekick-shared/node` surface.
9
+ * Everything here is also exported from the package root and from
10
+ * `sidekick-shared/node`.
11
+ *
12
+ * @module codex
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.codexSpawnParentFromSource = exports.codexRolloutOrigin = exports.parseCodexLoginStatus = exports.parseClaudeAuthStatus = exports.atomicWriteJsonSync = exports.atomicWriteJson = exports.findCodexRolloutFile = exports.compareCodexAuthFreshness = exports.codexRefreshFingerprint = exports.codexIdentitiesMatch = exports.readCodexAccessTokenExpiry = exports.readCodexLastRefresh = exports.readCodexAuthIdentity = exports.parseCodexAuthJson = exports.CODEX_ARCHIVED_SESSIONS_SUBDIR = exports.CODEX_SESSIONS_SUBDIR = exports.forceFileCredentialStore = exports.getCodexCredentialStoreMode = exports.getCodexSessionsDirs = exports.getCodexMonitoringHomes = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getSystemCodexHome = void 0;
16
+ var codexPaths_1 = require("./codexPaths");
17
+ Object.defineProperty(exports, "getSystemCodexHome", { enumerable: true, get: function () { return codexPaths_1.getSystemCodexHome; } });
18
+ Object.defineProperty(exports, "resolveSidekickCodexHome", { enumerable: true, get: function () { return codexPaths_1.resolveSidekickCodexHome; } });
19
+ Object.defineProperty(exports, "getCodexExecutionEnv", { enumerable: true, get: function () { return codexPaths_1.getCodexExecutionEnv; } });
20
+ Object.defineProperty(exports, "getCodexMonitoringHomes", { enumerable: true, get: function () { return codexPaths_1.getCodexMonitoringHomes; } });
21
+ Object.defineProperty(exports, "getCodexSessionsDirs", { enumerable: true, get: function () { return codexPaths_1.getCodexSessionsDirs; } });
22
+ Object.defineProperty(exports, "getCodexCredentialStoreMode", { enumerable: true, get: function () { return codexPaths_1.getCodexCredentialStoreMode; } });
23
+ Object.defineProperty(exports, "forceFileCredentialStore", { enumerable: true, get: function () { return codexPaths_1.forceFileCredentialStore; } });
24
+ Object.defineProperty(exports, "CODEX_SESSIONS_SUBDIR", { enumerable: true, get: function () { return codexPaths_1.CODEX_SESSIONS_SUBDIR; } });
25
+ Object.defineProperty(exports, "CODEX_ARCHIVED_SESSIONS_SUBDIR", { enumerable: true, get: function () { return codexPaths_1.CODEX_ARCHIVED_SESSIONS_SUBDIR; } });
26
+ var codexAuth_1 = require("./codexAuth");
27
+ Object.defineProperty(exports, "parseCodexAuthJson", { enumerable: true, get: function () { return codexAuth_1.parseAuthJson; } });
28
+ Object.defineProperty(exports, "readCodexAuthIdentity", { enumerable: true, get: function () { return codexAuth_1.readAuthIdentityFromRaw; } });
29
+ Object.defineProperty(exports, "readCodexLastRefresh", { enumerable: true, get: function () { return codexAuth_1.readLastRefresh; } });
30
+ Object.defineProperty(exports, "readCodexAccessTokenExpiry", { enumerable: true, get: function () { return codexAuth_1.readAccessTokenExpiry; } });
31
+ Object.defineProperty(exports, "codexIdentitiesMatch", { enumerable: true, get: function () { return codexAuth_1.identitiesMatch; } });
32
+ Object.defineProperty(exports, "codexRefreshFingerprint", { enumerable: true, get: function () { return codexAuth_1.codexRefreshFingerprint; } });
33
+ Object.defineProperty(exports, "compareCodexAuthFreshness", { enumerable: true, get: function () { return codexAuth_1.compareCodexAuthFreshness; } });
34
+ var codexRollouts_1 = require("./providers/codexRollouts");
35
+ Object.defineProperty(exports, "findCodexRolloutFile", { enumerable: true, get: function () { return codexRollouts_1.findCodexRolloutFile; } });
36
+ var atomic_1 = require("./writers/atomic");
37
+ Object.defineProperty(exports, "atomicWriteJson", { enumerable: true, get: function () { return atomic_1.atomicWriteJson; } });
38
+ Object.defineProperty(exports, "atomicWriteJsonSync", { enumerable: true, get: function () { return atomic_1.atomicWriteJsonSync; } });
39
+ var authStatus_1 = require("./authStatus");
40
+ Object.defineProperty(exports, "parseClaudeAuthStatus", { enumerable: true, get: function () { return authStatus_1.parseClaudeAuthStatus; } });
41
+ Object.defineProperty(exports, "parseCodexLoginStatus", { enumerable: true, get: function () { return authStatus_1.parseCodexLoginStatus; } });
42
+ var codexRolloutOrigin_1 = require("./parsers/codexRolloutOrigin");
43
+ Object.defineProperty(exports, "codexRolloutOrigin", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexRolloutOrigin; } });
44
+ Object.defineProperty(exports, "codexSpawnParentFromSource", { enumerable: true, get: function () { return codexRolloutOrigin_1.codexSpawnParentFromSource; } });
@@ -23,6 +23,7 @@ interface CodexLoginStatus {
23
23
  loggedIn: boolean;
24
24
  authMode?: 'chatgpt' | 'api-key';
25
25
  }
26
+ /** `codex login status` counts only on exit 0; the text is read by {@link parseCodexLoginStatus}. */
26
27
  export declare function parseCodexLoginStatusOutput(status: number | null, stdout: string): CodexLoginStatus;
27
28
  /**
28
29
  * Blocks the caller's event loop for up to {@link PROBE_TIMEOUT_MS}. Async code
@@ -79,6 +79,7 @@ const accountRegistry_1 = require("./accountRegistry");
79
79
  // re-serializing would drop fields added by newer codex versions, and the
80
80
  // rotated refresh token inside is only valid in its freshest form.
81
81
  const atomic_1 = require("./writers/atomic");
82
+ const authStatus_1 = require("./authStatus");
82
83
  const codexAuth_1 = require("./codexAuth");
83
84
  const codexPaths_1 = require("./codexPaths");
84
85
  const accountHealth_1 = require("./accountHealth");
@@ -285,18 +286,14 @@ function readMetadataFromLegacyCredentials(codexHome) {
285
286
  return {};
286
287
  }
287
288
  const PROBE_TIMEOUT_MS = 4000;
289
+ /** `codex login status` counts only on exit 0; the text is read by {@link parseCodexLoginStatus}. */
288
290
  function parseCodexLoginStatusOutput(status, stdout) {
289
- const trimmed = stdout.trim();
290
- if (status === 0 && /^Logged in/i.test(trimmed)) {
291
- if (/API key/i.test(trimmed)) {
292
- return { loggedIn: true, authMode: 'api-key' };
293
- }
294
- if (/ChatGPT/i.test(trimmed)) {
295
- return { loggedIn: true, authMode: 'chatgpt' };
296
- }
297
- return { loggedIn: true };
298
- }
299
- return { loggedIn: false };
291
+ if (status !== 0)
292
+ return { loggedIn: false };
293
+ const parsed = (0, authStatus_1.parseCodexLoginStatus)(stdout);
294
+ if (!parsed.loggedIn)
295
+ return { loggedIn: false };
296
+ return parsed.method ? { loggedIn: true, authMode: parsed.method } : { loggedIn: true };
300
297
  }
301
298
  /**
302
299
  * Blocks the caller's event loop for up to {@link PROBE_TIMEOUT_MS}. Async code
@@ -93,6 +93,11 @@ function isPreferredQuotaHit(candidate, current) {
93
93
  function normalizePercent(value) {
94
94
  return typeof value === 'number' && Number.isFinite(value) ? value : 0;
95
95
  }
96
+ function reportedWindowMs(value) {
97
+ return typeof value === 'number' && Number.isFinite(value) && value > 0
98
+ ? value * 60_000
99
+ : undefined;
100
+ }
96
101
  function windowMinutesToMs(value, fallbackMs) {
97
102
  return typeof value === 'number' && Number.isFinite(value) && value > 0
98
103
  ? value * 60_000
@@ -235,26 +240,34 @@ function quotaFromCodexRateLimits(rateLimits, source = 'session', capturedAt = n
235
240
  const secondary = rateLimits?.secondary;
236
241
  if (!primary && !secondary)
237
242
  return null;
243
+ const primaryMs = reportedWindowMs(primary?.window_minutes);
244
+ const secondaryMs = reportedWindowMs(secondary?.window_minutes);
238
245
  return (0, quota_1.withQuotaProjections)({
246
+ // A plan can report one window only (e.g. a weekly primary with no
247
+ // secondary); the missing one is marked `absent` rather than shown as 0%.
239
248
  fiveHour: primary
240
249
  ? {
241
250
  utilization: normalizePercent(primary.used_percent),
242
251
  resetsAt: timestampToIso(primary.resets_at),
252
+ ...(primaryMs !== undefined ? { windowMs: primaryMs } : {}),
243
253
  }
244
- : { utilization: 0, resetsAt: '' },
254
+ : { utilization: 0, resetsAt: '', absent: true },
245
255
  sevenDay: secondary
246
256
  ? {
247
257
  utilization: normalizePercent(secondary.used_percent),
248
258
  resetsAt: timestampToIso(secondary.resets_at),
259
+ ...(secondaryMs !== undefined ? { windowMs: secondaryMs } : {}),
249
260
  }
250
- : { utilization: 0, resetsAt: '' },
261
+ : { utilization: 0, resetsAt: '', absent: true },
251
262
  available: true,
252
263
  providerId: 'codex',
253
264
  source,
254
265
  capturedAt,
255
266
  stale: source === 'cache',
256
- fiveHourLabel: 'Primary',
257
- sevenDayLabel: 'Secondary',
267
+ // Name each window by its reported length (`5-Hour`, `Weekly`), falling
268
+ // back to the positional names when the length is not reported.
269
+ fiveHourLabel: (0, quota_1.quotaWindowLabel)(primaryMs, 'Primary'),
270
+ sevenDayLabel: (0, quota_1.quotaWindowLabel)(secondaryMs, 'Secondary'),
258
271
  limitId: rateLimits?.limit_id,
259
272
  limitName: rateLimits?.limit_name ?? undefined,
260
273
  credits: rateLimits?.credits,
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Pure parsers for the provider CLIs' read-only sign-in status commands,
3
+ * `claude auth status --json` and `codex login status`. They do no I/O. The
4
+ * caller runs the command with its own environment, home, and timeout, and
5
+ * passes the output here.
6
+ *
7
+ * A status command reports which credential the CLI would use. It does not
8
+ * prove that a request will authenticate: an expired or revoked token can
9
+ * still read as logged in.
10
+ *
11
+ * @module authStatus
12
+ */
13
+ /** Larger outputs are not a status report; reading stops there. */
14
+ const MAX_STATUS_OUTPUT = 256 * 1024;
15
+ const MAX_DETAIL = 200;
16
+ function stringField(record, key) {
17
+ const value = record[key];
18
+ if (typeof value !== 'string')
19
+ return undefined;
20
+ const trimmed = value.trim();
21
+ return trimmed && trimmed.length <= 512 ? trimmed : undefined;
22
+ }
23
+ function parseJsonObject(output) {
24
+ const trimmed = output.trim();
25
+ const candidates = [trimmed];
26
+ // Tolerate warning lines printed ahead of the JSON document, which must
27
+ // then start a line and run to the end of the output.
28
+ const start = trimmed.search(/\n[ \t]*\{/);
29
+ if (start >= 0)
30
+ candidates.push(trimmed.slice(start).trim());
31
+ for (const candidate of candidates) {
32
+ if (!candidate.startsWith('{') || !candidate.endsWith('}'))
33
+ continue;
34
+ try {
35
+ const parsed = JSON.parse(candidate);
36
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
37
+ return parsed;
38
+ }
39
+ catch {
40
+ /* Try the next candidate. */
41
+ }
42
+ }
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Parse the stdout of `claude auth status --json`. Claude Code exits 1 while
47
+ * printing valid signed-out JSON, so parse stdout whatever the exit status
48
+ * was. Only a JSON `"loggedIn": true` counts as logged in.
49
+ */
50
+ export function parseClaudeAuthStatus(stdout) {
51
+ const raw = typeof stdout === 'string' && stdout.length <= MAX_STATUS_OUTPUT
52
+ ? parseJsonObject(stdout)
53
+ : undefined;
54
+ if (!raw)
55
+ return { loggedIn: false };
56
+ const method = stringField(raw, 'authMethod');
57
+ const email = stringField(raw, 'email');
58
+ const subscriptionType = stringField(raw, 'subscriptionType');
59
+ return {
60
+ loggedIn: raw.loggedIn === true,
61
+ ...(method ? { method } : {}),
62
+ ...(email ? { email } : {}),
63
+ ...(subscriptionType ? { subscriptionType } : {}),
64
+ raw,
65
+ };
66
+ }
67
+ /**
68
+ * Parse the output of `codex login status`. Codex prints its status line to
69
+ * stderr (`Logged in using ChatGPT`, `Logged in using an API key - sk-…`,
70
+ * `Not logged in`), so pass both streams. The exit status is not consulted:
71
+ * callers that require exit 0 check it themselves.
72
+ */
73
+ export function parseCodexLoginStatus(stdout, stderr) {
74
+ const combined = `${typeof stdout === 'string' ? stdout : ''}\n${typeof stderr === 'string' ? stderr : ''}`.trim();
75
+ const firstLine = combined.split(/\r?\n/, 1)[0]?.trim() ?? '';
76
+ const detail = (/^(Logged in using .*?API key)\s+-\s.*$/i.exec(firstLine)?.[1] ?? firstLine)
77
+ .slice(0, MAX_DETAIL)
78
+ .trim();
79
+ const withDetail = detail ? { detail } : {};
80
+ if (!/^Logged in/i.test(combined))
81
+ return { loggedIn: false, ...withDetail };
82
+ if (/API key/i.test(combined))
83
+ return { loggedIn: true, method: 'api-key', ...withDetail };
84
+ if (/ChatGPT/i.test(combined))
85
+ return { loggedIn: true, method: 'chatgpt', ...withDetail };
86
+ return { loggedIn: true, ...withDetail };
87
+ }
@@ -18,17 +18,21 @@ export { parseMcpToolName } from './parsers/mcpToolName';
18
18
  export { buildSessionContextSnapshot, calculateSessionContextPressure, createSessionContextProjector, } from './context/sessionContext';
19
19
  export { ACCOUNT_PROVIDER_IDS, MODEL_PROVIDER_IDS, QUOTA_PROVIDER_IDS, RUNTIME_QUOTA_PROVIDER_IDS, SESSION_PROVIDER_IDS, } from './providerIds';
20
20
  export { assistantTurnEventsFromSessionEvents, extractTurnSubagents, isAssistantTurnSubagentTool, reasoningSummary, segmentAssistantTurn, } from './turns/assistantTurn';
21
- export { parseModelId, getModelPricing, resolveModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, } from './modelInfo';
21
+ export { parseModelId, getModelPricing, resolveModelPricing, getModelInfo, calculateCost, calculateCostWithPricing, calculateCostWithProvenance, mergeCostSources, shortModelName, getModelDisplayInfo, compareModelIds, sortModelIds, formatCost, canonicalModelId, isMovingModelAlias, registerModelPricing, clearRegisteredModelPricing, getPricingCatalogInfo, costSourceFromPricingProvenance, mergeCostComponents, } from './modelInfo';
22
22
  export { exportResolvedModelCatalog, importResolvedModelCatalog, registerModelAlias, resolveModelAlias, RESOLVED_MODEL_CATALOG_SCHEMA_VERSION, } from './modelCatalog';
23
23
  export { BILLING_BLOCK_DURATION_MS, computeBillingBlocks, findActiveBillingBlock, } from './usage/billingBlocks';
24
24
  export { bucketUsage, summarizeUsageRows, usageBucketKey, weekKey } from './usage/usageReports';
25
25
  export { applySessionSummary, isFileImported, markFileImported, removeSessionSummary, sessionSummaryFromStats, } from './historicalStore';
26
26
  export { FAILING_TOOL_TREND_ARROWS, failingToolTrend, mergeFailingToolWindows, } from './failingTools';
27
- export { diagnoseProviderFailure, PROVIDER_AUTHENTICATION_SOURCES, PROVIDER_AUTHENTICATION_STATES, PROVIDER_CREDENTIAL_KINDS, PROVIDER_FAILURE_CODES, PROVIDER_FAILURE_EVIDENCE_SOURCES, PROVIDER_RECOVERY_ACTIONS, } from './providerFailure';
27
+ export { diagnoseProviderFailure, PROVIDER_AUTHENTICATION_SOURCES, PROVIDER_AUTHENTICATION_STATES, PROVIDER_CREDENTIAL_KINDS, PROVIDER_FAILURE_CODES, PROVIDER_FAILURE_EVIDENCE_SOURCES, PROVIDER_RECOVERY_ACTIONS, CODEX_ERROR_INFO_KINDS, decodeCodexErrorInfo, } from './providerFailure';
28
+ export { parseClaudeAuthStatus, parseCodexLoginStatus } from './authStatus';
28
29
  export { isAggregateCodexLimit } from './types/codex';
29
30
  // Codex rollout parsing (pure: no filesystem access)
30
31
  export { CodexRolloutParser, DEFAULT_MAX_TRACKED_TOOL_CALLS, extractPatchFilePaths, normalizeCodexToolName, normalizeCodexToolInput, resolveCodexCallUsage, } from './parsers/codexParser';
32
+ export { codexRolloutOrigin, codexSpawnParentFromSource } from './parsers/codexRolloutOrigin';
31
33
  export { HUMAN_CLAUDE_ENTRYPOINTS, HUMAN_CODEX_SOURCES, humanPromptText, isHumanPrompt, } from './humanPrompt';
32
34
  export { renderHandoffUrlTemplate } from './handoffUrl';
33
35
  // Tool categories across providers
34
36
  export { classifyTool, TOOL_CATEGORIES } from './toolCategory';
37
+ // Quota window helpers (pure; safe for webviews)
38
+ export { claudePlanLabel, extraQuotaRows, formatExtraUsage, quotaWindowApplies, quotaWindowHasData, quotaWindowLabel, quotaWindowShortLabel, } from './quota';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `sidekick-shared/codex` — a small Node-only entry for Codex homes, the
3
+ * child-process environment, `auth.json` helpers, credential-store mode,
4
+ * rollout lookup, and the atomic JSON writers those helpers rely on.
5
+ *
6
+ * Loads a handful of modules and no Zod, so a host that only launches or
7
+ * locates Codex does not pay for the full `sidekick-shared/node` surface.
8
+ * Everything here is also exported from the package root and from
9
+ * `sidekick-shared/node`.
10
+ *
11
+ * @module codex
12
+ */
13
+ export { getSystemCodexHome, resolveSidekickCodexHome, getCodexExecutionEnv, getCodexMonitoringHomes, getCodexSessionsDirs, getCodexCredentialStoreMode, forceFileCredentialStore, CODEX_SESSIONS_SUBDIR, CODEX_ARCHIVED_SESSIONS_SUBDIR, } from './codexPaths';
14
+ export { parseAuthJson as parseCodexAuthJson, readAuthIdentityFromRaw as readCodexAuthIdentity, readLastRefresh as readCodexLastRefresh, readAccessTokenExpiry as readCodexAccessTokenExpiry, identitiesMatch as codexIdentitiesMatch, codexRefreshFingerprint, compareCodexAuthFreshness, } from './codexAuth';
15
+ export { findCodexRolloutFile } from './providers/codexRollouts';
16
+ export { atomicWriteJson, atomicWriteJsonSync } from './writers/atomic';
17
+ export { parseClaudeAuthStatus, parseCodexLoginStatus } from './authStatus';
18
+ export { codexRolloutOrigin, codexSpawnParentFromSource } from './parsers/codexRolloutOrigin';
@@ -7,6 +7,7 @@ import { getAccountsDir, getActiveSavedAccount, listSavedAccountProfiles, remove
7
7
  // re-serializing would drop fields added by newer codex versions, and the
8
8
  // rotated refresh token inside is only valid in its freshest form.
9
9
  import { atomicWriteFileSync as atomicWriteFile, atomicWriteJsonSync as atomicWriteJson, withFileLockSync, } from './writers/atomic';
10
+ import { parseCodexLoginStatus } from './authStatus';
10
11
  import { identitiesMatch, codexRefreshFingerprint, compareCodexAuthFreshness, readAuthIdentityFromRaw, readLastRefresh, STALE_AUTH_THRESHOLD_MS, } from './codexAuth';
11
12
  import { ensureCodexProfileDirs, forceFileCredentialStore, getCodexCredentialStoreMode, getCodexProfileDir, getCodexProfileHome, getCodexProfileStatePath, getCodexBackupPath, getCodexLoginDir, getCodexLoginsDir, getCodexLoginHome, getCodexLoginStatePath, getCodexProfilesDir, getSystemCodexHome, readFileOrNull, resolveSidekickCodexHome, } from './codexPaths';
12
13
  import { getAccountHealth, readHealthSidecar, writeCodexHealthSidecar, } from './accountHealth';
@@ -206,18 +207,14 @@ function readMetadataFromLegacyCredentials(codexHome) {
206
207
  return {};
207
208
  }
208
209
  const PROBE_TIMEOUT_MS = 4000;
210
+ /** `codex login status` counts only on exit 0; the text is read by {@link parseCodexLoginStatus}. */
209
211
  export function parseCodexLoginStatusOutput(status, stdout) {
210
- const trimmed = stdout.trim();
211
- if (status === 0 && /^Logged in/i.test(trimmed)) {
212
- if (/API key/i.test(trimmed)) {
213
- return { loggedIn: true, authMode: 'api-key' };
214
- }
215
- if (/ChatGPT/i.test(trimmed)) {
216
- return { loggedIn: true, authMode: 'chatgpt' };
217
- }
218
- return { loggedIn: true };
219
- }
220
- return { loggedIn: false };
212
+ if (status !== 0)
213
+ return { loggedIn: false };
214
+ const parsed = parseCodexLoginStatus(stdout);
215
+ if (!parsed.loggedIn)
216
+ return { loggedIn: false };
217
+ return parsed.method ? { loggedIn: true, authMode: parsed.method } : { loggedIn: true };
221
218
  }
222
219
  /**
223
220
  * Blocks the caller's event loop for up to {@link PROBE_TIMEOUT_MS}. Async code
@@ -2,7 +2,7 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { getActiveCodexAccount, getCodexMonitoringHomes, resolveSidekickCodexHome, } from './codexProfiles';
4
4
  import { readQuotaSnapshot, writeQuotaSnapshot } from './quotaSnapshots';
5
- import { FIVE_HOUR_WINDOW_MS, SEVEN_DAY_WINDOW_MS, withQuotaProjections } from './quota';
5
+ import { FIVE_HOUR_WINDOW_MS, SEVEN_DAY_WINDOW_MS, quotaWindowLabel, withQuotaProjections, } from './quota';
6
6
  import { CodexProvider } from './providers/codex';
7
7
  import { walkRolloutFiles } from './providers/rolloutWalker';
8
8
  import { isAggregateCodexLimit } from './types/codex';
@@ -51,6 +51,11 @@ function isPreferredQuotaHit(candidate, current) {
51
51
  function normalizePercent(value) {
52
52
  return typeof value === 'number' && Number.isFinite(value) ? value : 0;
53
53
  }
54
+ function reportedWindowMs(value) {
55
+ return typeof value === 'number' && Number.isFinite(value) && value > 0
56
+ ? value * 60_000
57
+ : undefined;
58
+ }
54
59
  function windowMinutesToMs(value, fallbackMs) {
55
60
  return typeof value === 'number' && Number.isFinite(value) && value > 0
56
61
  ? value * 60_000
@@ -193,26 +198,34 @@ export function quotaFromCodexRateLimits(rateLimits, source = 'session', capture
193
198
  const secondary = rateLimits?.secondary;
194
199
  if (!primary && !secondary)
195
200
  return null;
201
+ const primaryMs = reportedWindowMs(primary?.window_minutes);
202
+ const secondaryMs = reportedWindowMs(secondary?.window_minutes);
196
203
  return withQuotaProjections({
204
+ // A plan can report one window only (e.g. a weekly primary with no
205
+ // secondary); the missing one is marked `absent` rather than shown as 0%.
197
206
  fiveHour: primary
198
207
  ? {
199
208
  utilization: normalizePercent(primary.used_percent),
200
209
  resetsAt: timestampToIso(primary.resets_at),
210
+ ...(primaryMs !== undefined ? { windowMs: primaryMs } : {}),
201
211
  }
202
- : { utilization: 0, resetsAt: '' },
212
+ : { utilization: 0, resetsAt: '', absent: true },
203
213
  sevenDay: secondary
204
214
  ? {
205
215
  utilization: normalizePercent(secondary.used_percent),
206
216
  resetsAt: timestampToIso(secondary.resets_at),
217
+ ...(secondaryMs !== undefined ? { windowMs: secondaryMs } : {}),
207
218
  }
208
- : { utilization: 0, resetsAt: '' },
219
+ : { utilization: 0, resetsAt: '', absent: true },
209
220
  available: true,
210
221
  providerId: 'codex',
211
222
  source,
212
223
  capturedAt,
213
224
  stale: source === 'cache',
214
- fiveHourLabel: 'Primary',
215
- sevenDayLabel: 'Secondary',
225
+ // Name each window by its reported length (`5-Hour`, `Weekly`), falling
226
+ // back to the positional names when the length is not reported.
227
+ fiveHourLabel: quotaWindowLabel(primaryMs, 'Primary'),
228
+ sevenDayLabel: quotaWindowLabel(secondaryMs, 'Secondary'),
216
229
  limitId: rateLimits?.limit_id,
217
230
  limitName: rateLimits?.limit_name ?? undefined,
218
231
  credits: rateLimits?.credits,