sidekick-shared 0.13.6 → 0.13.8
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 +13 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/quota.d.ts +6 -0
- package/dist/quota.js +38 -5
- package/dist/quotaPresentation.d.ts +10 -0
- package/dist/quotaPresentation.js +88 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,10 +77,22 @@ import { fetchQuota, readClaudeMaxCredentials } from 'sidekick-shared';
|
|
|
77
77
|
const creds = readClaudeMaxCredentials();
|
|
78
78
|
if (creds) {
|
|
79
79
|
const quota = await fetchQuota(creds.accessToken);
|
|
80
|
-
|
|
80
|
+
if (quota.available) {
|
|
81
|
+
console.log(`5-hour utilization: ${quota.fiveHour.utilization}%`);
|
|
82
|
+
} else {
|
|
83
|
+
console.log(quota.failureKind, quota.httpStatus, quota.retryAfterMs);
|
|
84
|
+
}
|
|
81
85
|
}
|
|
82
86
|
```
|
|
83
87
|
|
|
88
|
+
Unavailable quota responses remain non-throwing and may include:
|
|
89
|
+
|
|
90
|
+
- `failureKind`: `auth | network | rate_limit | server | unknown`
|
|
91
|
+
- `httpStatus`: HTTP response status when available
|
|
92
|
+
- `retryAfterMs`: retry delay in milliseconds for `429` responses when the API provides `Retry-After`
|
|
93
|
+
|
|
94
|
+
For first-party style messaging, `describeQuotaFailure()` maps unavailable quota states to stable alert keys plus display-ready severity/title/message/detail fields for CLI and VS Code consumers.
|
|
95
|
+
|
|
84
96
|
## Building
|
|
85
97
|
|
|
86
98
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -83,5 +83,7 @@ export { readClaudeMaxCredentials, readClaudeMaxAccessTokenSync } from './creden
|
|
|
83
83
|
export type { ClaudeMaxCredentials } from './credentials';
|
|
84
84
|
export { fetchQuota } from './quota';
|
|
85
85
|
export type { QuotaWindow, QuotaState } from './quota';
|
|
86
|
+
export { describeQuotaFailure } from './quotaPresentation';
|
|
87
|
+
export type { QuotaFailureDescriptor } from './quotaPresentation';
|
|
86
88
|
export { fetchProviderStatus } from './providerStatus';
|
|
87
89
|
export type { ProviderStatusState } from './providerStatus';
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.findActiveClaudeSession = exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
|
|
7
7
|
exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = void 0;
|
|
8
|
-
exports.fetchProviderStatus = exports.fetchQuota = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
|
|
8
|
+
exports.fetchProviderStatus = exports.describeQuotaFailure = exports.fetchQuota = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
|
|
9
9
|
var taskPersistence_1 = require("./types/taskPersistence");
|
|
10
10
|
Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
|
|
11
11
|
Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
|
|
@@ -181,6 +181,8 @@ Object.defineProperty(exports, "readClaudeMaxAccessTokenSync", { enumerable: tru
|
|
|
181
181
|
// Quota
|
|
182
182
|
var quota_1 = require("./quota");
|
|
183
183
|
Object.defineProperty(exports, "fetchQuota", { enumerable: true, get: function () { return quota_1.fetchQuota; } });
|
|
184
|
+
var quotaPresentation_1 = require("./quotaPresentation");
|
|
185
|
+
Object.defineProperty(exports, "describeQuotaFailure", { enumerable: true, get: function () { return quotaPresentation_1.describeQuotaFailure; } });
|
|
184
186
|
// Provider Status
|
|
185
187
|
var providerStatus_1 = require("./providerStatus");
|
|
186
188
|
Object.defineProperty(exports, "fetchProviderStatus", { enumerable: true, get: function () { return providerStatus_1.fetchProviderStatus; } });
|
package/dist/quota.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export interface QuotaState {
|
|
|
17
17
|
available: boolean;
|
|
18
18
|
/** Human-readable error if not available */
|
|
19
19
|
error?: string;
|
|
20
|
+
/** Machine-readable failure classification for unavailable states */
|
|
21
|
+
failureKind?: 'auth' | 'network' | 'rate_limit' | 'server' | 'unknown';
|
|
22
|
+
/** HTTP status code for unavailable API responses */
|
|
23
|
+
httpStatus?: number;
|
|
24
|
+
/** Retry delay in milliseconds when rate-limited */
|
|
25
|
+
retryAfterMs?: number;
|
|
20
26
|
/** Projected 5-hour utilization at reset (percentage, capped at 200) */
|
|
21
27
|
projectedFiveHour?: number;
|
|
22
28
|
/** Projected 7-day utilization at reset (percentage, capped at 200) */
|
package/dist/quota.js
CHANGED
|
@@ -29,14 +29,27 @@ function projectFromElapsed(utilization, resetsAt, windowMs) {
|
|
|
29
29
|
/**
|
|
30
30
|
* Helper to build an unavailable QuotaState.
|
|
31
31
|
*/
|
|
32
|
-
function unavailableState(error) {
|
|
32
|
+
function unavailableState(error, meta = {}) {
|
|
33
33
|
return {
|
|
34
34
|
fiveHour: { utilization: 0, resetsAt: '' },
|
|
35
35
|
sevenDay: { utilization: 0, resetsAt: '' },
|
|
36
36
|
available: false,
|
|
37
37
|
error,
|
|
38
|
+
...meta,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
41
|
+
function parseRetryAfterMs(retryAfter) {
|
|
42
|
+
if (!retryAfter)
|
|
43
|
+
return undefined;
|
|
44
|
+
const seconds = Number(retryAfter);
|
|
45
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
46
|
+
return Math.round(seconds * 1000);
|
|
47
|
+
}
|
|
48
|
+
const retryAt = Date.parse(retryAfter);
|
|
49
|
+
if (Number.isNaN(retryAt))
|
|
50
|
+
return undefined;
|
|
51
|
+
return Math.max(retryAt - Date.now(), 0);
|
|
52
|
+
}
|
|
40
53
|
/**
|
|
41
54
|
* Fetch current quota utilization from the Anthropic OAuth usage API.
|
|
42
55
|
*
|
|
@@ -57,9 +70,29 @@ async function fetchQuota(accessToken) {
|
|
|
57
70
|
},
|
|
58
71
|
});
|
|
59
72
|
if (!res.ok) {
|
|
60
|
-
if (res.status === 401)
|
|
61
|
-
return unavailableState('Sign in to Claude Code to view quota'
|
|
62
|
-
|
|
73
|
+
if (res.status === 401) {
|
|
74
|
+
return unavailableState('Sign in to Claude Code to view quota', {
|
|
75
|
+
failureKind: 'auth',
|
|
76
|
+
httpStatus: res.status,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (res.status === 429) {
|
|
80
|
+
return unavailableState(`API error: ${res.status}`, {
|
|
81
|
+
failureKind: 'rate_limit',
|
|
82
|
+
httpStatus: res.status,
|
|
83
|
+
retryAfterMs: parseRetryAfterMs(res.headers.get('retry-after')),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (res.status >= 500 && res.status <= 599) {
|
|
87
|
+
return unavailableState(`API error: ${res.status}`, {
|
|
88
|
+
failureKind: 'server',
|
|
89
|
+
httpStatus: res.status,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return unavailableState(`API error: ${res.status}`, {
|
|
93
|
+
failureKind: 'unknown',
|
|
94
|
+
httpStatus: res.status,
|
|
95
|
+
});
|
|
63
96
|
}
|
|
64
97
|
const data = await res.json();
|
|
65
98
|
const fiveUtil = data.five_hour?.utilization ?? 0;
|
|
@@ -75,6 +108,6 @@ async function fetchQuota(accessToken) {
|
|
|
75
108
|
};
|
|
76
109
|
}
|
|
77
110
|
catch {
|
|
78
|
-
return unavailableState('Network error');
|
|
111
|
+
return unavailableState('Network error', { failureKind: 'network' });
|
|
79
112
|
}
|
|
80
113
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { QuotaState } from './quota';
|
|
2
|
+
export interface QuotaFailureDescriptor {
|
|
3
|
+
severity: 'info' | 'warning' | 'error';
|
|
4
|
+
title: string;
|
|
5
|
+
message: string;
|
|
6
|
+
detail?: string;
|
|
7
|
+
alertKey: string;
|
|
8
|
+
isRetryable: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function describeQuotaFailure(quota: QuotaState | null | undefined): QuotaFailureDescriptor | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeQuotaFailure = describeQuotaFailure;
|
|
4
|
+
function formatRetryAfter(ms) {
|
|
5
|
+
if (ms <= 0)
|
|
6
|
+
return 'now';
|
|
7
|
+
const totalSeconds = Math.ceil(ms / 1000);
|
|
8
|
+
const days = Math.floor(totalSeconds / 86_400);
|
|
9
|
+
const hours = Math.floor((totalSeconds % 86_400) / 3_600);
|
|
10
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
11
|
+
const seconds = totalSeconds % 60;
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (days > 0)
|
|
14
|
+
parts.push(`${days}d`);
|
|
15
|
+
if (hours > 0)
|
|
16
|
+
parts.push(`${hours}h`);
|
|
17
|
+
if (minutes > 0 && days === 0)
|
|
18
|
+
parts.push(`${minutes}m`);
|
|
19
|
+
if (parts.length === 0)
|
|
20
|
+
parts.push(`${seconds}s`);
|
|
21
|
+
return parts.join(' ');
|
|
22
|
+
}
|
|
23
|
+
function describeQuotaFailure(quota) {
|
|
24
|
+
if (!quota || quota.available || !quota.failureKind)
|
|
25
|
+
return null;
|
|
26
|
+
switch (quota.failureKind) {
|
|
27
|
+
case 'auth':
|
|
28
|
+
if (quota.error === 'No OAuth token available') {
|
|
29
|
+
return {
|
|
30
|
+
severity: 'error',
|
|
31
|
+
title: 'Sign in required',
|
|
32
|
+
message: 'No Claude Code credentials are available in this environment.',
|
|
33
|
+
detail: 'Run `claude` to sign in, then retry quota refresh.',
|
|
34
|
+
alertKey: 'auth:no-credentials',
|
|
35
|
+
isRetryable: false,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
severity: 'error',
|
|
40
|
+
title: 'Claude Code sign-in expired',
|
|
41
|
+
message: 'The current Claude Code OAuth session was rejected.',
|
|
42
|
+
detail: 'Sign in again to refresh subscription quota.',
|
|
43
|
+
alertKey: `auth:${quota.httpStatus ?? 'unknown'}`,
|
|
44
|
+
isRetryable: false,
|
|
45
|
+
};
|
|
46
|
+
case 'network':
|
|
47
|
+
return {
|
|
48
|
+
severity: 'warning',
|
|
49
|
+
title: 'Quota API unreachable',
|
|
50
|
+
message: 'Could not reach Anthropic from the current environment.',
|
|
51
|
+
detail: 'Check connectivity, proxy, or firewall settings, then retry.',
|
|
52
|
+
alertKey: 'network',
|
|
53
|
+
isRetryable: true,
|
|
54
|
+
};
|
|
55
|
+
case 'rate_limit':
|
|
56
|
+
return {
|
|
57
|
+
severity: 'warning',
|
|
58
|
+
title: 'Quota API rate limited',
|
|
59
|
+
message: quota.retryAfterMs != null
|
|
60
|
+
? `Retry in ${formatRetryAfter(quota.retryAfterMs)}.`
|
|
61
|
+
: 'Retry shortly.',
|
|
62
|
+
detail: quota.httpStatus != null ? `Anthropic returned HTTP ${quota.httpStatus}.` : undefined,
|
|
63
|
+
alertKey: `rate_limit:${quota.httpStatus ?? 429}`,
|
|
64
|
+
isRetryable: true,
|
|
65
|
+
};
|
|
66
|
+
case 'server':
|
|
67
|
+
return {
|
|
68
|
+
severity: 'warning',
|
|
69
|
+
title: 'Quota API unavailable',
|
|
70
|
+
message: quota.httpStatus != null
|
|
71
|
+
? `Anthropic returned HTTP ${quota.httpStatus}. Try again shortly.`
|
|
72
|
+
: 'Anthropic quota data is temporarily unavailable.',
|
|
73
|
+
alertKey: `server:${quota.httpStatus ?? 'unknown'}`,
|
|
74
|
+
isRetryable: true,
|
|
75
|
+
};
|
|
76
|
+
case 'unknown':
|
|
77
|
+
return {
|
|
78
|
+
severity: 'error',
|
|
79
|
+
title: 'Unexpected quota response',
|
|
80
|
+
message: quota.httpStatus != null
|
|
81
|
+
? `Anthropic returned HTTP ${quota.httpStatus}.`
|
|
82
|
+
: (quota.error ?? 'Quota data could not be retrieved.'),
|
|
83
|
+
detail: 'This failure is not classified as retryable.',
|
|
84
|
+
alertKey: `unknown:${quota.httpStatus ?? 'none'}`,
|
|
85
|
+
isRetryable: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
package/package.json
CHANGED