cursor-usage-tracker 0.0.0-development
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/LICENSE +21 -0
- package/README.md +411 -0
- package/bin/create.mjs +52 -0
- package/package.json +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ofer Shapira
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="public/logo.png" alt="Cursor Usage Tracker" width="100" height="100" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Cursor Usage Tracker</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Know who's burning through your AI budget before the invoice tells you.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://github.com/ofershap/cursor-usage-tracker/actions/workflows/ci.yml"><img src="https://github.com/ofershap/cursor-usage-tracker/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
13
|
+
<a href="https://github.com/ofershap/cursor-usage-tracker/actions/workflows/codeql.yml"><img src="https://github.com/ofershap/cursor-usage-tracker/actions/workflows/codeql.yml/badge.svg" alt="CodeQL" /></a>
|
|
14
|
+
<a href="https://scorecard.dev/viewer/?uri=github.com/ofershap/cursor-usage-tracker"><img src="https://api.scorecard.dev/projects/github.com/ofershap/cursor-usage-tracker/badge" alt="OpenSSF Scorecard" /></a>
|
|
15
|
+
<a href="https://www.bestpractices.dev/projects/11968"><img src="https://www.bestpractices.dev/projects/11968/badge" alt="OpenSSF Best Practices" /></a>
|
|
16
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
|
|
17
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-strict-blue" alt="TypeScript" /></a>
|
|
18
|
+
<a href="https://www.docker.com/"><img src="https://img.shields.io/badge/Docker-ready-2496ED" alt="Docker" /></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## AI is the new cloud bill
|
|
24
|
+
|
|
25
|
+
Engineering costs used to be two things: headcount and cloud infrastructure. You had tools for both. Then AI coding assistants showed up, and suddenly there's a third cost center that nobody has good tooling for.
|
|
26
|
+
|
|
27
|
+
A single developer on Cursor can burn through hundreds of dollars a day just by switching to an expensive model or letting an agent loop run wild. Now multiply that by 50, 100, 500 developers. The bill gets big fast, and there's nothing like Datadog or CloudHealth for this category yet.
|
|
28
|
+
|
|
29
|
+
Cursor's admin dashboard shows you the raw numbers, but it won't tell you when something is off. No anomaly detection. No alerts. No incident tracking. You find out about cost spikes when the invoice lands, weeks after the damage is done.
|
|
30
|
+
|
|
31
|
+
I built cursor-usage-tracker to fix that. It sits on top of Cursor's Enterprise APIs and gives engineering managers, finance, and platform teams actual visibility into AI spend before it becomes a surprise.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## What It Does
|
|
36
|
+
|
|
37
|
+
Your company has 50+ developers on Cursor. Do you know who's spending $200/day on Claude Opus while everyone else uses Sonnet?
|
|
38
|
+
|
|
39
|
+
You're about to find out.
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
<sub>Demo animation created with <a href="https://github.com/ofershap/remotion-readme-kit">remotion-readme-kit</a></sub>
|
|
44
|
+
|
|
45
|
+
It connects to Cursor's Enterprise APIs, collects usage data, and automatically detects anomalies across three layers. When something looks off, you get a Slack message or email within the hour, not next month.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Developer uses Cursor → API collects data hourly → Engine detects anomaly → You get a Slack alert
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### How It Works
|
|
52
|
+
|
|
53
|
+
| What happens | Example |
|
|
54
|
+
| ------------------------------------------ | ----------------------------------------------------------------------------- |
|
|
55
|
+
| A developer exceeds the spend limit | `Bob spent $82 this cycle (limit: $50)` → Slack alert |
|
|
56
|
+
| Someone's daily spend spikes | `Alice: daily spend spiked to $214 (4.2x her 7-day avg of $51)` → Slack alert |
|
|
57
|
+
| A user's cycle spend is far above the team | `Bob: cycle spend $957 is 5.1x the team median ($188)` → Slack alert |
|
|
58
|
+
| A user is statistically far from the team | `Bob: daily spend $214 is 3.2σ above team mean ($42)` → Slack alert |
|
|
59
|
+
|
|
60
|
+
Every alert includes who, what model, how much, and a link to their dashboard page so you can investigate immediately.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
### Three-Layer Anomaly Detection
|
|
67
|
+
|
|
68
|
+
| Layer | Method | What it catches |
|
|
69
|
+
| -------------- | ------------- | ----------------------------------------------------------------------------- |
|
|
70
|
+
| **Thresholds** | Static limits | Optional hard caps on spend, requests, or tokens (disabled by default) |
|
|
71
|
+
| **Z-Score** | Statistical | User daily spend 2.5+ standard deviations above team mean (active users only) |
|
|
72
|
+
| **Trends** | Spend-based | Daily spend spikes vs personal average, cycle spend outliers vs team median |
|
|
73
|
+
|
|
74
|
+
### Incident Lifecycle (MTTD / MTTI / MTTR)
|
|
75
|
+
|
|
76
|
+
Every anomaly becomes a tracked incident with full lifecycle metrics:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Anomaly Detected ──→ Alert Sent ──→ Acknowledged ──→ Resolved
|
|
80
|
+
│ │ │ │
|
|
81
|
+
└──── MTTD ────────┘ │ │
|
|
82
|
+
└── MTTI ──────┘
|
|
83
|
+
└────────────────── MTTR ─────────────────────────┘
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **MTTD** (Mean Time to Detect): how fast the system catches it
|
|
87
|
+
- **MTTI** (Mean Time to Identify): how fast a human acknowledges it
|
|
88
|
+
- **MTTR** (Mean Time to Resolve): how fast it gets fixed
|
|
89
|
+
|
|
90
|
+
### Rich Alerting
|
|
91
|
+
|
|
92
|
+
- **Slack**: Block Kit messages via bot token (`chat.postMessage`) with severity, user, model, value vs threshold, and dashboard links. Batches alerts automatically (individual messages for 1-3 anomalies, single summary for 4+).
|
|
93
|
+
- **Email**: HTML-formatted alerts via [Resend](https://resend.com) (one API key, no SMTP config)
|
|
94
|
+
|
|
95
|
+
### Web Dashboard
|
|
96
|
+
|
|
97
|
+
| Page | What you see |
|
|
98
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| **Team Overview** | Stat cards, spend by user, daily spend trend, spend breakdown, members table with search/sort, **group filter dropdown**, billing cycle progress, time range picker |
|
|
100
|
+
| **Insights** | DAU chart, model adoption trends, model efficiency rankings (cost/precision), MCP tool usage, file extensions, client versions |
|
|
101
|
+
| **User Drilldown** | Per-user token timeline, model breakdown, feature usage, activity profile, anomaly history |
|
|
102
|
+
| **Anomalies** | Open incidents, MTTD/MTTI/MTTR metrics, full anomaly timeline |
|
|
103
|
+
| **Settings** | Detection thresholds, **billing group management** (rename, assign, create), **HiBob CSV import** with change preview |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Quick Start
|
|
108
|
+
|
|
109
|
+
### Prerequisites
|
|
110
|
+
|
|
111
|
+
| What | Where to get it |
|
|
112
|
+
| ---------------------- | ------------------------------------------------------- |
|
|
113
|
+
| Cursor Enterprise plan | Required for API access |
|
|
114
|
+
| Admin API key | Cursor dashboard → Settings → Advanced → Admin API Keys |
|
|
115
|
+
| Node.js 18+ | [nodejs.org](https://nodejs.org) |
|
|
116
|
+
|
|
117
|
+
### 1. Set up
|
|
118
|
+
|
|
119
|
+
**Option A: One command**
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx cursor-usage-tracker my-tracker
|
|
123
|
+
cd my-tracker
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Option B: Manual clone**
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
git clone https://github.com/ofershap/cursor-usage-tracker.git
|
|
130
|
+
cd cursor-usage-tracker
|
|
131
|
+
npm install
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 2. Configure
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
cp .env.example .env
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Edit `.env`:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Required
|
|
144
|
+
CURSOR_ADMIN_API_KEY=your_admin_api_key
|
|
145
|
+
|
|
146
|
+
# Alerting — Slack (at least one alerting channel recommended)
|
|
147
|
+
SLACK_BOT_TOKEN=xoxb-your-bot-token # bot token with chat:write scope
|
|
148
|
+
SLACK_CHANNEL_ID=C0123456789 # channel to post alerts to
|
|
149
|
+
|
|
150
|
+
# Dashboard URL (used in alert links)
|
|
151
|
+
DASHBOARD_URL=http://localhost:3000
|
|
152
|
+
|
|
153
|
+
# Optional
|
|
154
|
+
CRON_SECRET=your_secret_here # protects the cron endpoint
|
|
155
|
+
DASHBOARD_PASSWORD=your_password # optional basic auth for the dashboard
|
|
156
|
+
|
|
157
|
+
# Email alerts via Resend (optional)
|
|
158
|
+
RESEND_API_KEY=re_xxxxxxxxxxxx
|
|
159
|
+
ALERT_EMAIL_TO=team-lead@company.com
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 3. Start the dashboard
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm run dev
|
|
166
|
+
# Open http://localhost:3000
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 4. Collect your first data
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm run collect
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
You should see:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
[collect] Done in 4.2s
|
|
179
|
+
Members: 87
|
|
180
|
+
Daily usage: 30
|
|
181
|
+
Spending: 87
|
|
182
|
+
Usage events: 12,847
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 5. Run anomaly detection
|
|
186
|
+
|
|
187
|
+
After collecting data, run detection separately:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm run detect
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This runs the stored data through all three detection layers and sends alerts for anything it finds.
|
|
194
|
+
|
|
195
|
+
> `npm run collect` only fetches data. `npm run detect` only runs detection. The cron endpoint (`POST /api/cron`) does both in one call.
|
|
196
|
+
|
|
197
|
+
### 6. Set up recurring collection
|
|
198
|
+
|
|
199
|
+
Trigger the cron endpoint hourly (via crontab, GitHub Actions, or any scheduler):
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
curl -X POST http://localhost:3000/api/cron -H "x-cron-secret: YOUR_SECRET"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
This collects data, runs anomaly detection, and sends alerts in one call.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Docker
|
|
210
|
+
|
|
211
|
+
For production deployment:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
cp .env.example .env # configure your keys
|
|
215
|
+
docker compose up -d
|
|
216
|
+
# Dashboard at http://localhost:3000
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The Docker image uses multi-stage builds for a minimal production image. Data persists in a Docker volume.
|
|
220
|
+
|
|
221
|
+
<details>
|
|
222
|
+
<summary>Docker Compose details</summary>
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
services:
|
|
226
|
+
tracker:
|
|
227
|
+
build: .
|
|
228
|
+
ports:
|
|
229
|
+
- "3000:3000"
|
|
230
|
+
env_file: .env
|
|
231
|
+
volumes:
|
|
232
|
+
- tracker-data:/app/data
|
|
233
|
+
volumes:
|
|
234
|
+
tracker-data:
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
</details>
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Architecture
|
|
242
|
+
|
|
243
|
+
```mermaid
|
|
244
|
+
flowchart TB
|
|
245
|
+
APIs["Cursor Enterprise APIs\n/teams/members · /teams/spend · /teams/daily-usage-data\n/teams/filtered-usage-events · /teams/groups · /analytics/team/*"]
|
|
246
|
+
C["Collector (hourly)"]
|
|
247
|
+
DB[("SQLite (local)")]
|
|
248
|
+
D["Detection Engine, 3 layers"]
|
|
249
|
+
AL["Alerts: Slack / Email"]
|
|
250
|
+
DA["Dashboard: Next.js"]
|
|
251
|
+
|
|
252
|
+
APIs --> C --> DB --> D
|
|
253
|
+
DB --> DA
|
|
254
|
+
D --> AL
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Zero external dependencies.** SQLite stores everything locally. No Postgres, no Redis, no cloud database. Clone, configure, run.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Configuration
|
|
262
|
+
|
|
263
|
+
All detection thresholds are configurable via the Settings page or the API:
|
|
264
|
+
|
|
265
|
+
| Setting | Default | What it does |
|
|
266
|
+
| ------------------------ | ------- | -------------------------------------------------------------- |
|
|
267
|
+
| Max spend per cycle | 0 (off) | Alert when a user exceeds this in a billing cycle |
|
|
268
|
+
| Max requests per day | 0 (off) | Alert on excessive daily request count |
|
|
269
|
+
| Max tokens per day | 0 (off) | Alert on excessive daily token consumption |
|
|
270
|
+
| Z-score multiplier | 2.5 | How many standard deviations above mean to flag (spend + reqs) |
|
|
271
|
+
| Z-score window | 7 days | Historical window for statistical comparison |
|
|
272
|
+
| Spend spike multiplier | 5.0x | Alert when today's spend > N× user's personal daily average |
|
|
273
|
+
| Spend spike lookback | 7 days | How many days of history to compare against |
|
|
274
|
+
| Cycle outlier multiplier | 10.0x | Alert when cycle spend > N× team median (active users only) |
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
<details>
|
|
279
|
+
<summary><strong>Billing Groups: organize teams by department, group, or custom structure</strong></summary>
|
|
280
|
+
|
|
281
|
+
Billing groups let you organize team members by department, team, or any structure that fits your org.
|
|
282
|
+
|
|
283
|
+
**Dashboard Filtering**
|
|
284
|
+
|
|
285
|
+
The Team Overview page includes a group filter dropdown next to the search bar. Select a group to instantly filter all stats, charts, and the members table to that subset. Groups are displayed in a hierarchical `Parent > Team` format.
|
|
286
|
+
|
|
287
|
+
**Settings Page**
|
|
288
|
+
|
|
289
|
+
From the Settings page you can:
|
|
290
|
+
|
|
291
|
+
- **View** all groups with member counts and per-group spend
|
|
292
|
+
- **Rename** groups to match your org structure
|
|
293
|
+
- **Reassign** members between groups
|
|
294
|
+
- **Create** new groups manually
|
|
295
|
+
- **Search** across all members to find who's in which group
|
|
296
|
+
|
|
297
|
+
</details>
|
|
298
|
+
|
|
299
|
+
<details>
|
|
300
|
+
<summary><strong>HiBob Import: sync your org structure from HiBob's People Directory</strong></summary>
|
|
301
|
+
|
|
302
|
+
For teams using [HiBob](https://www.hibob.com/) as their HR platform, the Settings page includes an **Import from HiBob** feature:
|
|
303
|
+
|
|
304
|
+
1. Download a CSV export from HiBob's People Directory
|
|
305
|
+
2. Upload it to the import modal in Settings
|
|
306
|
+
3. Review the preview: see which members will be moved, which groups will be created, and which members weren't matched
|
|
307
|
+
4. Selectively approve or reject individual changes before applying
|
|
308
|
+
|
|
309
|
+
The import uses HiBob's `Group` and `Team` columns (falling back to `Department`) to build a `Group > Team` hierarchy. Small teams (fewer than 3 members) are automatically consolidated into broader groups to avoid excessive granularity.
|
|
310
|
+
|
|
311
|
+
> The HiBob import updates your local billing groups only. It does not push changes back to HiBob or to Cursor's billing API.
|
|
312
|
+
|
|
313
|
+
</details>
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## API Endpoints
|
|
318
|
+
|
|
319
|
+
| Endpoint | Method | Description |
|
|
320
|
+
| --------------------- | ------- | --------------------------------------------------- |
|
|
321
|
+
| `/api/cron` | POST | Collect + detect + alert (use with scheduler) |
|
|
322
|
+
| `/api/stats` | GET | Dashboard statistics (`?days=7`) |
|
|
323
|
+
| `/api/analytics` | GET | Analytics data: DAU, models, MCP, etc. (`?days=30`) |
|
|
324
|
+
| `/api/team-spend` | GET | Daily team spend breakdown |
|
|
325
|
+
| `/api/model-costs` | GET | Model cost breakdown by users and spend |
|
|
326
|
+
| `/api/groups` | GET | Billing groups with members and spend |
|
|
327
|
+
| `/api/groups` | PATCH | Rename group, assign member, or create group |
|
|
328
|
+
| `/api/groups/import` | POST | HiBob CSV import (preview + apply) |
|
|
329
|
+
| `/api/anomalies` | GET | Anomaly timeline (`?days=30`) |
|
|
330
|
+
| `/api/users/[email]` | GET | Per-user statistics (`?days=30`) |
|
|
331
|
+
| `/api/incidents/[id]` | PATCH | Acknowledge or resolve incident |
|
|
332
|
+
| `/api/settings` | GET/PUT | Detection configuration |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Tech Stack
|
|
337
|
+
|
|
338
|
+
| Component | Technology |
|
|
339
|
+
| ---------- | ------------------------------------ |
|
|
340
|
+
| Framework | Next.js (App Router) |
|
|
341
|
+
| Language | TypeScript (strict mode) |
|
|
342
|
+
| Database | SQLite (better-sqlite3), zero config |
|
|
343
|
+
| Charts | Recharts |
|
|
344
|
+
| Styling | Tailwind CSS |
|
|
345
|
+
| Testing | Vitest |
|
|
346
|
+
| Deployment | Docker (multi-stage) |
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Development
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
npm run dev # Start dev server
|
|
354
|
+
npm run collect # Manual data collection
|
|
355
|
+
npm run detect # Manual anomaly detection + alerting
|
|
356
|
+
npm run typecheck # Type checking
|
|
357
|
+
npm test # Run tests
|
|
358
|
+
npm run lint # Lint + format check
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Cursor API Requirements
|
|
364
|
+
|
|
365
|
+
Requires a **Cursor Enterprise** plan. The tool uses these endpoints:
|
|
366
|
+
|
|
367
|
+
| Endpoint | Auth | What it provides |
|
|
368
|
+
| ----------------------------------- | ----------------- | -------------------------------------------- |
|
|
369
|
+
| `GET /teams/members` | Admin API key | Team member list |
|
|
370
|
+
| `POST /teams/spend` | Admin API key | Per-user spending data |
|
|
371
|
+
| `POST /teams/daily-usage-data` | Admin API key | Daily usage metrics |
|
|
372
|
+
| `POST /teams/filtered-usage-events` | Admin API key | Detailed usage events with model/token info |
|
|
373
|
+
| `POST /teams/groups` | Admin API key | Billing groups + cycle dates |
|
|
374
|
+
| `GET /analytics/team/*` | Analytics API key | DAU, model usage, MCP, tabs, etc. (optional) |
|
|
375
|
+
|
|
376
|
+
Rate limit: 20 requests/minute (Admin API), 100 requests/minute (Analytics API). The collector handles rate limiting with automatic retry.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Security
|
|
381
|
+
|
|
382
|
+
This project handles sensitive usage and spending data, so security matters here more than most.
|
|
383
|
+
|
|
384
|
+
- **Vulnerability reporting**: See [SECURITY.md](SECURITY.md) for the disclosure policy. Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/ofershap/cursor-usage-tracker/security/advisories/new), not public issues.
|
|
385
|
+
- **Automated scanning**: Every push and PR goes through [CodeQL](https://codeql.github.com/) (SQL injection, XSS, CSRF, etc.) and [Dependabot](https://docs.github.com/en/code-security/dependabot) for dependency vulnerabilities.
|
|
386
|
+
- **OpenSSF Scorecard**: Continuously evaluated against [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/ofershap/cursor-usage-tracker) security benchmarks.
|
|
387
|
+
- **OpenSSF Best Practices**: [Passing badge](https://www.bestpractices.dev/projects/11968) earned.
|
|
388
|
+
- **Data stays local**: Everything is stored in a local SQLite file. Nothing leaves your infrastructure. No external databases, no cloud services, no telemetry.
|
|
389
|
+
- **Small dependency tree**: Fewer dependencies = smaller attack surface.
|
|
390
|
+
- **Signed releases**: Automated via semantic-release with GitHub-verified provenance.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Contributing
|
|
395
|
+
|
|
396
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines. Bug reports, feature requests, docs improvements, and code are all welcome. Use [conventional commits](https://www.conventionalcommits.org/) and make sure CI is green before opening a PR.
|
|
397
|
+
|
|
398
|
+
## Code of Conduct
|
|
399
|
+
|
|
400
|
+
This project uses the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).
|
|
401
|
+
|
|
402
|
+
## Author
|
|
403
|
+
|
|
404
|
+
**Ofer Shapira**
|
|
405
|
+
|
|
406
|
+
[](https://linkedin.com/in/ofershap)
|
|
407
|
+
[](https://github.com/ofershap)
|
|
408
|
+
|
|
409
|
+
## License
|
|
410
|
+
|
|
411
|
+
[MIT](LICENSE) © Ofer Shapira
|
package/bin/create.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { existsSync, copyFileSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
const REPO = "https://github.com/ofershap/cursor-usage-tracker.git";
|
|
8
|
+
const dirName = process.argv[2] || "cursor-usage-tracker";
|
|
9
|
+
const targetDir = resolve(process.cwd(), dirName);
|
|
10
|
+
|
|
11
|
+
function run(cmd, opts = {}) {
|
|
12
|
+
execSync(cmd, { stdio: "inherit", ...opts });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function log(msg) {
|
|
16
|
+
console.log(`\n\x1b[36m${msg}\x1b[0m`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (existsSync(targetDir)) {
|
|
20
|
+
console.error(`\x1b[31mDirectory "${dirName}" already exists.\x1b[0m`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
log(`Cloning cursor-usage-tracker into ${dirName}...`);
|
|
25
|
+
run(`git clone --depth 1 ${REPO} "${targetDir}"`);
|
|
26
|
+
|
|
27
|
+
log("Installing dependencies...");
|
|
28
|
+
run("npm install", { cwd: targetDir });
|
|
29
|
+
|
|
30
|
+
const envExample = resolve(targetDir, ".env.example");
|
|
31
|
+
const envFile = resolve(targetDir, ".env");
|
|
32
|
+
if (existsSync(envExample) && !existsSync(envFile)) {
|
|
33
|
+
copyFileSync(envExample, envFile);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
log("Done! Next steps:");
|
|
37
|
+
console.log(`
|
|
38
|
+
cd ${dirName}
|
|
39
|
+
|
|
40
|
+
1. Edit .env and add your CURSOR_ADMIN_API_KEY
|
|
41
|
+
Get it from: Cursor dashboard → Settings → Advanced → Admin API Keys
|
|
42
|
+
|
|
43
|
+
2. Start the dashboard:
|
|
44
|
+
npm run dev
|
|
45
|
+
|
|
46
|
+
3. Collect your first data:
|
|
47
|
+
npm run collect
|
|
48
|
+
|
|
49
|
+
4. Open http://localhost:3000
|
|
50
|
+
|
|
51
|
+
Docs: https://github.com/ofershap/cursor-usage-tracker
|
|
52
|
+
`);
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cursor-usage-tracker",
|
|
3
|
+
"version": "0.0.0-development",
|
|
4
|
+
"description": "Open-source Cursor IDE usage monitoring, anomaly detection, and alerting for enterprise teams",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "next dev",
|
|
8
|
+
"build": "next build",
|
|
9
|
+
"start": "next start",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"test:coverage": "vitest run --coverage",
|
|
14
|
+
"lint": "eslint . && prettier --check .",
|
|
15
|
+
"format": "prettier --write .",
|
|
16
|
+
"prepare": "husky",
|
|
17
|
+
"collect": "tsx src/lib/cli/collect.ts",
|
|
18
|
+
"detect": "tsx src/lib/cli/detect.ts",
|
|
19
|
+
"generate:mock": "tsx scripts/generate-mock-db.ts",
|
|
20
|
+
"dev:mock": "DATABASE_PATH=data/mock.db next dev"
|
|
21
|
+
},
|
|
22
|
+
"lint-staged": {
|
|
23
|
+
"*.{ts,tsx,js,jsx}": [
|
|
24
|
+
"eslint --fix",
|
|
25
|
+
"prettier --write"
|
|
26
|
+
],
|
|
27
|
+
"*.{json,md,yml,yaml,css}": [
|
|
28
|
+
"prettier --write"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"release": {
|
|
32
|
+
"branches": [
|
|
33
|
+
"main"
|
|
34
|
+
],
|
|
35
|
+
"plugins": [
|
|
36
|
+
"@semantic-release/commit-analyzer",
|
|
37
|
+
"@semantic-release/release-notes-generator",
|
|
38
|
+
"@semantic-release/npm",
|
|
39
|
+
"@semantic-release/github"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"bin": {
|
|
43
|
+
"cursor-usage-tracker": "bin/create.mjs"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"bin/"
|
|
47
|
+
],
|
|
48
|
+
"keywords": [
|
|
49
|
+
"cursor",
|
|
50
|
+
"cursor-ide",
|
|
51
|
+
"usage-monitoring",
|
|
52
|
+
"anomaly-detection",
|
|
53
|
+
"cost-tracking",
|
|
54
|
+
"enterprise",
|
|
55
|
+
"developer-tools",
|
|
56
|
+
"ai-spending"
|
|
57
|
+
],
|
|
58
|
+
"author": "Ofer Shapira",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/ofershap/cursor-usage-tracker.git"
|
|
63
|
+
},
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/ofershap/cursor-usage-tracker/issues"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://github.com/ofershap/cursor-usage-tracker#readme",
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
70
|
+
"better-sqlite3": "^12.6.2",
|
|
71
|
+
"next": "^16.1.6",
|
|
72
|
+
"react": "^19.2.4",
|
|
73
|
+
"react-dom": "^19.2.4",
|
|
74
|
+
"recharts": "^3.7.0",
|
|
75
|
+
"resend": "^6.9.2",
|
|
76
|
+
"tailwindcss": "^4.1.18"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^10.0.1",
|
|
80
|
+
"@next/eslint-plugin-next": "^16.1.6",
|
|
81
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
82
|
+
"@semantic-release/git": "^10.0.1",
|
|
83
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
84
|
+
"@types/node": "^25.2.3",
|
|
85
|
+
"@types/react": "^19.2.14",
|
|
86
|
+
"@types/react-dom": "^19.2.3",
|
|
87
|
+
"eslint": "^10.0.0",
|
|
88
|
+
"husky": "^9.1.7",
|
|
89
|
+
"lint-staged": "^16.2.7",
|
|
90
|
+
"prettier": "^3.8.1",
|
|
91
|
+
"semantic-release": "^25.0.3",
|
|
92
|
+
"tsx": "^4.21.0",
|
|
93
|
+
"typescript": "^5.9.3",
|
|
94
|
+
"typescript-eslint": "^8.56.0",
|
|
95
|
+
"vitest": "^4.0.18"
|
|
96
|
+
}
|
|
97
|
+
}
|