cursor-usage 0.1.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.
- package/README.md +209 -0
- package/dist/_consts.d.ts +13 -0
- package/dist/_consts.js +16 -0
- package/dist/_types.d.ts +60 -0
- package/dist/_types.js +4 -0
- package/dist/args-parser.d.ts +32 -0
- package/dist/args-parser.js +104 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +162 -0
- package/dist/commands.d.ts +42 -0
- package/dist/commands.js +273 -0
- package/dist/data-loader.d.ts +13 -0
- package/dist/data-loader.js +134 -0
- package/dist/event-loader.d.ts +82 -0
- package/dist/event-loader.js +350 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +11 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +34 -0
- package/dist/table-formatter.d.ts +16 -0
- package/dist/table-formatter.js +40 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Cursor Usage Analyzer
|
|
2
|
+
|
|
3
|
+
A CLI tool for analyzing Cursor API usage and membership information, inspired by [ccusage](https://github.com/ryoppippi/ccusage).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ⨠Extract Cursor credentials from local database
|
|
8
|
+
- š Fetch usage data from Cursor API
|
|
9
|
+
- š³ Display membership and plan usage information
|
|
10
|
+
- š Show usage breakdown (included vs bonus)
|
|
11
|
+
- šÆ Track percentage of included usage consumed
|
|
12
|
+
- šØ Colorful terminal output
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Show current billing summary (default)
|
|
26
|
+
npm run dev
|
|
27
|
+
|
|
28
|
+
# Show daily usage for last 7 days (default)
|
|
29
|
+
npm run dev -- daily
|
|
30
|
+
|
|
31
|
+
# Show daily usage for last N days
|
|
32
|
+
npm run dev -- daily 30
|
|
33
|
+
npm run dev -- d 7 # Alias
|
|
34
|
+
|
|
35
|
+
# Show monthly usage for last 3 months (default)
|
|
36
|
+
npm run dev -- monthly
|
|
37
|
+
|
|
38
|
+
# Show monthly usage for last N months
|
|
39
|
+
npm run dev -- monthly 6
|
|
40
|
+
npm run dev -- m 12 # Alias
|
|
41
|
+
|
|
42
|
+
# Show detailed usage for today
|
|
43
|
+
npm run dev -- today
|
|
44
|
+
|
|
45
|
+
# Show help
|
|
46
|
+
npm run dev -- help
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Development
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm run dev # Run with tsx (default summary)
|
|
53
|
+
npm start # Run with tsx (default summary)
|
|
54
|
+
npm run build # Compile TypeScript
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Example Output
|
|
58
|
+
|
|
59
|
+
### Summary View
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
š ACCOUNT INFORMATION:
|
|
63
|
+
Membership: pro
|
|
64
|
+
Billing Cycle: <your-billing-cycle>
|
|
65
|
+
|
|
66
|
+
š PLAN USAGE:
|
|
67
|
+
Used: 150 / 2000 (7.50%)
|
|
68
|
+
Remaining: 1850
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Daily Report View
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
ā Found 3 usage events
|
|
75
|
+
|
|
76
|
+
====================================================================================================
|
|
77
|
+
DAILY USAGE REPORT (Last 7 days)
|
|
78
|
+
====================================================================================================
|
|
79
|
+
|
|
80
|
+
+------------+--------+--------------+-------+--------+-------+----------------------------------+
|
|
81
|
+
| Date | Events | Total Tokens | Input | Output | Cost | Models |
|
|
82
|
+
+------------+--------+--------------+-------+--------+-------+----------------------------------+
|
|
83
|
+
| 2026-01-11 | 3 | 539,301 | 381 | 7,081 | $0.82 | claude-4.5-opus-high-thinking(3) |
|
|
84
|
+
+------------+--------+--------------+-------+--------+-------+----------------------------------+
|
|
85
|
+
|
|
86
|
+
====================================================================================================
|
|
87
|
+
SUMMARY
|
|
88
|
+
====================================================================================================
|
|
89
|
+
Total Events: 3
|
|
90
|
+
Total Tokens: 539,301
|
|
91
|
+
Total Cost: $0.82
|
|
92
|
+
====================================================================================================
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Monthly Report View
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
====================================================================================================
|
|
99
|
+
MONTHLY USAGE REPORT (Last 3 months)
|
|
100
|
+
====================================================================================================
|
|
101
|
+
|
|
102
|
+
+----------+--------+---------------+-------+-------+-------+------------------------------+
|
|
103
|
+
| Month | Events | Total Tokens | Input | Output| Cost | Models |
|
|
104
|
+
+----------+--------+---------------+-------+-------+-------+------------------------------+
|
|
105
|
+
| Month 1 | 50 | 5,000,000 | 50000 | 30000 | $4.50 | claude-sonnet(50) |
|
|
106
|
+
| Month 2 | 75 | 8,500,000 | 75000 | 45000 | $6.80 | claude-opus(75) |
|
|
107
|
+
| Month 3 | 25 | 2,500,000 | 20000 | 10000 | $2.00 | claude-haiku(25) |
|
|
108
|
+
+----------+--------+---------------+-------+-------+-------+------------------------------+
|
|
109
|
+
|
|
110
|
+
====================================================================================================
|
|
111
|
+
SUMMARY
|
|
112
|
+
====================================================================================================
|
|
113
|
+
Total Events: 150
|
|
114
|
+
Total Tokens: 16,000,000
|
|
115
|
+
Total Cost: $13.30
|
|
116
|
+
Average per month: 5,333,333 tokens, $4.43
|
|
117
|
+
====================================================================================================
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Detailed Event View
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
========================================================================================================================
|
|
124
|
+
USAGE EVENTS FOR Today
|
|
125
|
+
========================================================================================================================
|
|
126
|
+
|
|
127
|
+
+----------+---------------+-------+--------------+---------------+--------------+---------+
|
|
128
|
+
| Time | Model | Type | Input Tokens | Output Tokens | Total Tokens | Cost |
|
|
129
|
+
+----------+---------------+-------+--------------+---------------+--------------+---------+
|
|
130
|
+
| 2:15 pm | claude-opus | usage | 500 | 1,200 | 1,700 | $0.0850 |
|
|
131
|
+
| 1:45 pm | claude-sonnet | usage | 300 | 800 | 1,100 | $0.0440 |
|
|
132
|
+
| 1:20 pm | claude-haiku | usage | 100 | 250 | 350 | $0.0070 |
|
|
133
|
+
+----------+---------------+-------+--------------+---------------+--------------+---------+
|
|
134
|
+
|
|
135
|
+
========================================================================================================================
|
|
136
|
+
Total Events: 3
|
|
137
|
+
Total Tokens: 3,150
|
|
138
|
+
Total Cost: $0.14
|
|
139
|
+
========================================================================================================================
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## How It Works
|
|
143
|
+
|
|
144
|
+
1. **Extracts credentials** from `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb`
|
|
145
|
+
- User ID from `workbench.experiments.statsigBootstrap`
|
|
146
|
+
- Access Token from `cursorAuth/accessToken`
|
|
147
|
+
- Email and membership (optional)
|
|
148
|
+
|
|
149
|
+
2. **Authenticates** with Cursor API using `WorkosCursorSessionToken` cookie
|
|
150
|
+
|
|
151
|
+
3. **Fetches usage data** from `https://cursor.com/api/usage-summary`
|
|
152
|
+
|
|
153
|
+
4. **Parses and displays** membership, plan usage, and billing information
|
|
154
|
+
|
|
155
|
+
## Environment Variables
|
|
156
|
+
|
|
157
|
+
- `CURSOR_DATA_DIR` - Custom path to Cursor data directory (optional)
|
|
158
|
+
- `DEBUG` - Enable debug logging
|
|
159
|
+
|
|
160
|
+
## Project Structure
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
src/
|
|
164
|
+
āāā index.ts # CLI entry point & display logic
|
|
165
|
+
āāā data-loader.ts # Database extraction & API fetching
|
|
166
|
+
āāā logger.ts # Colored logging utilities
|
|
167
|
+
āāā _types.ts # TypeScript type definitions
|
|
168
|
+
āāā _consts.ts # Constants & configuration
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API Response Structure
|
|
172
|
+
|
|
173
|
+
The tool fetches data from Cursor's `/api/usage-summary` endpoint which returns:
|
|
174
|
+
- Billing cycle information
|
|
175
|
+
- Membership type (pro, hobby, etc.)
|
|
176
|
+
- Plan usage (used/limit/remaining)
|
|
177
|
+
- On-demand usage status
|
|
178
|
+
- Display messages about usage percentage
|
|
179
|
+
|
|
180
|
+
## Features Implemented
|
|
181
|
+
|
|
182
|
+
- ā
Daily usage reports with table formatting
|
|
183
|
+
- ā
Detailed event breakdown by time
|
|
184
|
+
- ā
Token count tracking (input, output, cache)
|
|
185
|
+
- ā
Cost calculation and display
|
|
186
|
+
- ā
Model usage breakdown
|
|
187
|
+
- ā
Flexible date range querying
|
|
188
|
+
- ā
Colorized CLI output
|
|
189
|
+
|
|
190
|
+
## Future Enhancements
|
|
191
|
+
|
|
192
|
+
- [ ] JSON output format (`--json`)
|
|
193
|
+
- [ ] Date range queries (`--from 2026-01-01 --to 2026-01-31`)
|
|
194
|
+
- [ ] Weekly/monthly aggregations
|
|
195
|
+
- [ ] Historical data caching
|
|
196
|
+
- [ ] Cost projections
|
|
197
|
+
- [ ] Team usage analysis
|
|
198
|
+
- [ ] Export to CSV/JSON
|
|
199
|
+
- [ ] Web dashboard
|
|
200
|
+
- [ ] Webhook notifications
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
205
|
+
|
|
206
|
+
## Related Projects
|
|
207
|
+
|
|
208
|
+
- [ccusage](https://github.com/ryoppippi/ccusage) - Claude Code usage analyzer
|
|
209
|
+
- [@ccusage/amp](https://www.npmjs.com/package/@ccusage/amp) - Amp usage analyzer
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Cursor usage analyzer
|
|
3
|
+
*/
|
|
4
|
+
export declare const CURSOR_DB_PATH: string;
|
|
5
|
+
export declare const CURSOR_API_URL = "https://cursor.com/api/usage-summary";
|
|
6
|
+
export declare const CURSOR_API_URL_EVENTS = "https://cursor.com/api/dashboard/get-filtered-usage-events";
|
|
7
|
+
export declare const CURSOR_DATA_DIR_ENV = "CURSOR_DATA_DIR";
|
|
8
|
+
export declare const DB_KEY_STATSIG_BOOTSTRAP = "workbench.experiments.statsigBootstrap";
|
|
9
|
+
export declare const DB_KEY_ACCESS_TOKEN = "cursorAuth/accessToken";
|
|
10
|
+
export declare const DB_KEY_EMAIL = "cursorAuth/cachedEmail";
|
|
11
|
+
export declare const DB_KEY_MEMBERSHIP = "cursorAuth/stripeMembershipType";
|
|
12
|
+
export declare const DEFAULT_TIMEOUT = 10000;
|
|
13
|
+
export declare const USER_AGENT = "Mozilla/5.0 (compatible; cursor-usage/1.0)";
|
package/dist/_consts.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Cursor usage analyzer
|
|
3
|
+
*/
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
export const CURSOR_DB_PATH = join(homedir(), 'Library/Application Support/Cursor/User/globalStorage/state.vscdb');
|
|
7
|
+
export const CURSOR_API_URL = 'https://cursor.com/api/usage-summary';
|
|
8
|
+
export const CURSOR_API_URL_EVENTS = 'https://cursor.com/api/dashboard/get-filtered-usage-events';
|
|
9
|
+
export const CURSOR_DATA_DIR_ENV = 'CURSOR_DATA_DIR';
|
|
10
|
+
// Database keys
|
|
11
|
+
export const DB_KEY_STATSIG_BOOTSTRAP = 'workbench.experiments.statsigBootstrap';
|
|
12
|
+
export const DB_KEY_ACCESS_TOKEN = 'cursorAuth/accessToken';
|
|
13
|
+
export const DB_KEY_EMAIL = 'cursorAuth/cachedEmail';
|
|
14
|
+
export const DB_KEY_MEMBERSHIP = 'cursorAuth/stripeMembershipType';
|
|
15
|
+
export const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
|
16
|
+
export const USER_AGENT = 'Mozilla/5.0 (compatible; cursor-usage/1.0)';
|
package/dist/_types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Cursor usage data
|
|
3
|
+
*/
|
|
4
|
+
export interface UsageBreakdown {
|
|
5
|
+
included: number;
|
|
6
|
+
bonus: number;
|
|
7
|
+
total: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PlanUsage {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
used: number;
|
|
12
|
+
limit: number;
|
|
13
|
+
remaining: number;
|
|
14
|
+
breakdown: UsageBreakdown;
|
|
15
|
+
autoPercentUsed: number;
|
|
16
|
+
apiPercentUsed: number;
|
|
17
|
+
totalPercentUsed: number;
|
|
18
|
+
}
|
|
19
|
+
export interface OnDemandUsage {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
used: number;
|
|
22
|
+
limit: number | null;
|
|
23
|
+
remaining: number | null;
|
|
24
|
+
}
|
|
25
|
+
export interface IndividualUsage {
|
|
26
|
+
plan: PlanUsage;
|
|
27
|
+
onDemand: OnDemandUsage;
|
|
28
|
+
}
|
|
29
|
+
export interface UsageSummary {
|
|
30
|
+
billingCycleStart: string;
|
|
31
|
+
billingCycleEnd: string;
|
|
32
|
+
membershipType: string;
|
|
33
|
+
limitType: string;
|
|
34
|
+
isUnlimited: boolean;
|
|
35
|
+
autoModelSelectedDisplayMessage: string;
|
|
36
|
+
namedModelSelectedDisplayMessage: string;
|
|
37
|
+
individualUsage: IndividualUsage;
|
|
38
|
+
teamUsage: Record<string, any>;
|
|
39
|
+
}
|
|
40
|
+
export interface CursorCredentials {
|
|
41
|
+
userId: string;
|
|
42
|
+
accessToken: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
membership?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface FetchOptions {
|
|
47
|
+
userAgent?: string;
|
|
48
|
+
timeout?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface MonthlyStats {
|
|
51
|
+
year: number;
|
|
52
|
+
month: number;
|
|
53
|
+
monthName: string;
|
|
54
|
+
eventCount: number;
|
|
55
|
+
totalTokens: number;
|
|
56
|
+
inputTokens: number;
|
|
57
|
+
outputTokens: number;
|
|
58
|
+
totalCost: number;
|
|
59
|
+
models: Map<string, number>;
|
|
60
|
+
}
|
package/dist/_types.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-line argument parser
|
|
3
|
+
*/
|
|
4
|
+
export interface ParsedArgs {
|
|
5
|
+
command: string;
|
|
6
|
+
params: string[];
|
|
7
|
+
flags: Record<string, string | boolean>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse command-line arguments
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
13
|
+
/**
|
|
14
|
+
* Validate and parse date string (YYYY-MM-DD)
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseDate(dateStr: string): Date | null;
|
|
17
|
+
/**
|
|
18
|
+
* Get date from flag or parameter
|
|
19
|
+
*/
|
|
20
|
+
export declare function getDateFlag(flags: Record<string, string | boolean>, key: string): Date | null;
|
|
21
|
+
/**
|
|
22
|
+
* Get number from flag or parameter
|
|
23
|
+
*/
|
|
24
|
+
export declare function getNumberFlag(flags: Record<string, string | boolean>, key: string, defaultValue?: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* Get boolean flag
|
|
27
|
+
*/
|
|
28
|
+
export declare function getBoolFlag(flags: Record<string, string | boolean>, key: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Get string flag
|
|
31
|
+
*/
|
|
32
|
+
export declare function getStringFlag(flags: Record<string, string | boolean>, key: string, defaultValue?: string): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-line argument parser
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parse command-line arguments
|
|
6
|
+
*/
|
|
7
|
+
export function parseArgs(argv) {
|
|
8
|
+
const command = argv[0] || 'summary';
|
|
9
|
+
const params = [];
|
|
10
|
+
const flags = {};
|
|
11
|
+
let i = 1;
|
|
12
|
+
while (i < argv.length) {
|
|
13
|
+
const arg = argv[i];
|
|
14
|
+
if (arg.startsWith('--')) {
|
|
15
|
+
// Long flag: --flag or --flag=value
|
|
16
|
+
const eqIndex = arg.indexOf('=');
|
|
17
|
+
if (eqIndex > -1) {
|
|
18
|
+
const key = arg.substring(2, eqIndex);
|
|
19
|
+
const value = arg.substring(eqIndex + 1);
|
|
20
|
+
flags[key] = value;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const key = arg.substring(2);
|
|
24
|
+
// Check if next arg is value (not a flag)
|
|
25
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
|
|
26
|
+
flags[key] = argv[i + 1];
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
flags[key] = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (arg.startsWith('-') && arg.length === 2) {
|
|
35
|
+
// Short flag: -h
|
|
36
|
+
const key = arg.substring(1);
|
|
37
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
|
|
38
|
+
flags[key] = argv[i + 1];
|
|
39
|
+
i++;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
flags[key] = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Positional parameter
|
|
47
|
+
params.push(arg);
|
|
48
|
+
}
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
return { command, params, flags };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate and parse date string (YYYY-MM-DD)
|
|
55
|
+
*/
|
|
56
|
+
export function parseDate(dateStr) {
|
|
57
|
+
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
58
|
+
if (!match) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const [, year, month, day] = match;
|
|
62
|
+
const date = new Date(`${year}-${month}-${day}T00:00:00Z`);
|
|
63
|
+
if (isNaN(date.getTime())) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return date;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get date from flag or parameter
|
|
70
|
+
*/
|
|
71
|
+
export function getDateFlag(flags, key) {
|
|
72
|
+
const value = flags[key];
|
|
73
|
+
if (!value || typeof value !== 'string') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return parseDate(value);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get number from flag or parameter
|
|
80
|
+
*/
|
|
81
|
+
export function getNumberFlag(flags, key, defaultValue = 0) {
|
|
82
|
+
const value = flags[key];
|
|
83
|
+
if (value === undefined || value === true) {
|
|
84
|
+
return defaultValue;
|
|
85
|
+
}
|
|
86
|
+
const num = Number(value);
|
|
87
|
+
return isNaN(num) ? defaultValue : num;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get boolean flag
|
|
91
|
+
*/
|
|
92
|
+
export function getBoolFlag(flags, key) {
|
|
93
|
+
return flags[key] === true;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get string flag
|
|
97
|
+
*/
|
|
98
|
+
export function getStringFlag(flags, key, defaultValue = '') {
|
|
99
|
+
const value = flags[key];
|
|
100
|
+
if (typeof value === 'string') {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
return defaultValue;
|
|
104
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command routing and argument handling
|
|
3
|
+
*/
|
|
4
|
+
import { getCursorCredentials, fetchUsageData } from './data-loader';
|
|
5
|
+
import { showDailyReport, showMonthlyReport, showWeeklyReport, showDateReport, } from './commands';
|
|
6
|
+
import { logger } from './logger';
|
|
7
|
+
import { parseArgs, getNumberFlag, getDateFlag, getBoolFlag, } from './args-parser';
|
|
8
|
+
export async function runCLI(argv) {
|
|
9
|
+
const parsed = parseArgs(argv);
|
|
10
|
+
const { command, params, flags } = parsed;
|
|
11
|
+
// Load credentials
|
|
12
|
+
const credentials = await getCursorCredentials();
|
|
13
|
+
if (!credentials) {
|
|
14
|
+
logger.error('Failed to load credentials from local database');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
// Get common flags
|
|
18
|
+
const breakdown = getBoolFlag(flags, 'breakdown');
|
|
19
|
+
const json = getBoolFlag(flags, 'json');
|
|
20
|
+
const sinceDate = getDateFlag(flags, 'since');
|
|
21
|
+
const untilDate = getDateFlag(flags, 'until');
|
|
22
|
+
// Route commands
|
|
23
|
+
if (command === 'daily' || command === 'd') {
|
|
24
|
+
const days = getNumberFlag(flags, 'days') || getNumberFlag({ [params[0] || '']: params[0] }, '', 7);
|
|
25
|
+
const numDays = params.length > 0 && !isNaN(Number(params[0])) ? Number(params[0]) : days;
|
|
26
|
+
const options = {
|
|
27
|
+
breakdown,
|
|
28
|
+
startDate: sinceDate || undefined,
|
|
29
|
+
endDate: untilDate || undefined,
|
|
30
|
+
compact: getBoolFlag(flags, 'compact')
|
|
31
|
+
};
|
|
32
|
+
await showDailyReport(credentials, numDays, options, json);
|
|
33
|
+
}
|
|
34
|
+
else if (command === 'monthly' || command === 'm') {
|
|
35
|
+
const months = params.length > 0 && !isNaN(Number(params[0])) ? Number(params[0]) : 3;
|
|
36
|
+
const options = {
|
|
37
|
+
breakdown,
|
|
38
|
+
startDate: sinceDate || undefined,
|
|
39
|
+
endDate: untilDate || undefined,
|
|
40
|
+
compact: getBoolFlag(flags, 'compact')
|
|
41
|
+
};
|
|
42
|
+
await showMonthlyReport(credentials, months, options, json);
|
|
43
|
+
}
|
|
44
|
+
else if (command === 'weekly' || command === 'w') {
|
|
45
|
+
const weeks = params.length > 0 && !isNaN(Number(params[0])) ? Number(params[0]) : 4;
|
|
46
|
+
const options = {
|
|
47
|
+
breakdown,
|
|
48
|
+
startDate: sinceDate || undefined,
|
|
49
|
+
endDate: untilDate || undefined,
|
|
50
|
+
compact: getBoolFlag(flags, 'compact')
|
|
51
|
+
};
|
|
52
|
+
await showWeeklyReport(credentials, weeks, options, json);
|
|
53
|
+
}
|
|
54
|
+
else if (command === 'today') {
|
|
55
|
+
const options = { breakdown, compact: getBoolFlag(flags, 'compact') };
|
|
56
|
+
await showDateReport(credentials, new Date(), options, json);
|
|
57
|
+
}
|
|
58
|
+
else if (command === 'help' || command === '-h' || command === '--help') {
|
|
59
|
+
showHelp();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Default: show summary
|
|
63
|
+
const usageData = await fetchUsageData(credentials);
|
|
64
|
+
if (!usageData) {
|
|
65
|
+
logger.error('Failed to fetch usage data');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
logger.log('');
|
|
69
|
+
displayResults(usageData);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function displayResults(data) {
|
|
73
|
+
logger.log('='.repeat(70));
|
|
74
|
+
logger.log('CURSOR USAGE SUMMARY');
|
|
75
|
+
logger.log('='.repeat(70));
|
|
76
|
+
// Membership and plan info
|
|
77
|
+
logger.log('\nš ACCOUNT INFORMATION:');
|
|
78
|
+
logger.log(` Membership: ${data.membershipType || 'N/A'}`);
|
|
79
|
+
logger.log(` Billing Cycle: ${formatDate(data.billingCycleStart)} to ${formatDate(data.billingCycleEnd)}`);
|
|
80
|
+
// Plan usage
|
|
81
|
+
const plan = data.individualUsage?.plan;
|
|
82
|
+
if (plan) {
|
|
83
|
+
logger.log('\nš PLAN USAGE:');
|
|
84
|
+
logger.log(` Used: ${plan.used} / ${plan.limit} (${(plan.totalPercentUsed * 100).toFixed(2)}%)`);
|
|
85
|
+
logger.log(` Remaining: ${plan.remaining}`);
|
|
86
|
+
logger.log(` Breakdown:`);
|
|
87
|
+
logger.log(` - Included: ${plan.breakdown?.included || 0}`);
|
|
88
|
+
logger.log(` - Bonus: ${plan.breakdown?.bonus || 0}`);
|
|
89
|
+
}
|
|
90
|
+
// On-demand usage
|
|
91
|
+
const onDemand = data.individualUsage?.onDemand;
|
|
92
|
+
if (onDemand && onDemand.enabled) {
|
|
93
|
+
logger.log('\nš° ON-DEMAND USAGE:');
|
|
94
|
+
logger.log(` Used: ${onDemand.used}`);
|
|
95
|
+
logger.log(` Status: Enabled`);
|
|
96
|
+
}
|
|
97
|
+
// Display messages
|
|
98
|
+
if (data.autoModelSelectedDisplayMessage) {
|
|
99
|
+
logger.log('\nš¢ AUTO MODEL MESSAGE:');
|
|
100
|
+
logger.log(` ${data.autoModelSelectedDisplayMessage}`);
|
|
101
|
+
}
|
|
102
|
+
if (data.namedModelSelectedDisplayMessage) {
|
|
103
|
+
logger.log('\nš¬ NAMED MODEL MESSAGE:');
|
|
104
|
+
logger.log(` ${data.namedModelSelectedDisplayMessage}`);
|
|
105
|
+
}
|
|
106
|
+
logger.log('\n' + '='.repeat(70) + '\n');
|
|
107
|
+
}
|
|
108
|
+
function formatDate(dateString) {
|
|
109
|
+
try {
|
|
110
|
+
const date = new Date(dateString);
|
|
111
|
+
return date.toLocaleDateString('en-US', {
|
|
112
|
+
year: 'numeric',
|
|
113
|
+
month: 'short',
|
|
114
|
+
day: 'numeric',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return dateString;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function showHelp() {
|
|
122
|
+
logger.log(`
|
|
123
|
+
CURSOR USAGE ANALYZER - CLI COMMANDS
|
|
124
|
+
|
|
125
|
+
Usage: cursor-usage [COMMAND] [OPTIONS]
|
|
126
|
+
|
|
127
|
+
Commands:
|
|
128
|
+
(none) Show current billing summary (default)
|
|
129
|
+
daily [N] Show daily usage for last N days (default: 7)
|
|
130
|
+
d [N] Alias for 'daily'
|
|
131
|
+
weekly [N] Show weekly usage for last N weeks (default: 4)
|
|
132
|
+
w [N] Alias for 'weekly'
|
|
133
|
+
monthly [N] Show monthly usage for last N months (default: 3)
|
|
134
|
+
m [N] Alias for 'monthly'
|
|
135
|
+
today Show detailed usage for today
|
|
136
|
+
help Show this help message
|
|
137
|
+
-h, --help Show this help message
|
|
138
|
+
|
|
139
|
+
Flags:
|
|
140
|
+
--since DATE Start date (YYYY-MM-DD)
|
|
141
|
+
--until DATE End date (YYYY-MM-DD)
|
|
142
|
+
--breakdown Show per-model breakdown
|
|
143
|
+
--json Output as JSON (in development)
|
|
144
|
+
--compact Compact table format
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
cursor-usage # Show summary
|
|
148
|
+
cursor-usage daily # Show last 7 days
|
|
149
|
+
cursor-usage daily 30 # Show last 30 days
|
|
150
|
+
cursor-usage daily --since 2026-01-01 --until 2026-01-15
|
|
151
|
+
cursor-usage weekly # Show last 4 weeks
|
|
152
|
+
cursor-usage weekly 8 # Show last 8 weeks
|
|
153
|
+
cursor-usage monthly # Show last 3 months
|
|
154
|
+
cursor-usage monthly 6 # Show last 6 months
|
|
155
|
+
cursor-usage daily --breakdown # With model breakdown
|
|
156
|
+
cursor-usage weekly --breakdown --json # JSON output
|
|
157
|
+
cursor-usage today # Today's details
|
|
158
|
+
|
|
159
|
+
Environment Variables:
|
|
160
|
+
DEBUG Enable debug logging
|
|
161
|
+
`);
|
|
162
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command handlers
|
|
3
|
+
*/
|
|
4
|
+
import type { CursorCredentials } from './_types';
|
|
5
|
+
/**
|
|
6
|
+
* Convert stats to JSON
|
|
7
|
+
*/
|
|
8
|
+
export declare function statsToJSON(command: string, stats: any[], events: any[], options?: any): string;
|
|
9
|
+
/**
|
|
10
|
+
* Show daily usage report for a date range
|
|
11
|
+
*/
|
|
12
|
+
export declare function showDailyReport(credentials: CursorCredentials, days?: number, options?: {
|
|
13
|
+
breakdown?: boolean;
|
|
14
|
+
startDate?: Date;
|
|
15
|
+
endDate?: Date;
|
|
16
|
+
compact?: boolean;
|
|
17
|
+
}, outputJson?: boolean): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Show monthly usage report
|
|
20
|
+
*/
|
|
21
|
+
export declare function showMonthlyReport(credentials: CursorCredentials, months?: number, options?: {
|
|
22
|
+
breakdown?: boolean;
|
|
23
|
+
startDate?: Date;
|
|
24
|
+
endDate?: Date;
|
|
25
|
+
compact?: boolean;
|
|
26
|
+
}, outputJson?: boolean): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Show weekly usage report
|
|
29
|
+
*/
|
|
30
|
+
export declare function showWeeklyReport(credentials: CursorCredentials, weeks?: number, options?: {
|
|
31
|
+
breakdown?: boolean;
|
|
32
|
+
startDate?: Date;
|
|
33
|
+
endDate?: Date;
|
|
34
|
+
compact?: boolean;
|
|
35
|
+
}, outputJson?: boolean): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Show usage for a specific date
|
|
38
|
+
*/
|
|
39
|
+
export declare function showDateReport(credentials: CursorCredentials, date: Date, options?: {
|
|
40
|
+
breakdown?: boolean;
|
|
41
|
+
compact?: boolean;
|
|
42
|
+
}, outputJson?: boolean): Promise<void>;
|