when-does-my-quota-refresh 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 when-does-my-quota-refresh contributors
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,704 @@
1
+ # when-does-my-quota-refresh
2
+
3
+ **Cross-platform CLI + SDK for tracking Antigravity AI model quotas across all your accounts.**
4
+
5
+ ```
6
+ ╔══════════════════════════════════════════════════════════════╗
7
+ ║ when-does-my-quota-refresh — Full Dashboard 14:32:07 ║
8
+ ╠══════════════════════════════════════════════════════════════╣
9
+ ║ 📧 user@gmail.com (Personal — Ultra) ● Active ║
10
+ ║ ┌─ Gemini Models ───────────────────────────────────────┐ ║
11
+ ║ │ 5-Hour [██████████████░░░░░░░░░░░░░░░░] 72% 2h31m │ ║
12
+ ║ │ Weekly [████████████████████████░░░░░░░] 85% 3d12h │ ║
13
+ ║ └────────────────────────────────────────────────────────┘ ║
14
+ ║ ┌─ Claude + GPT Models ──────────────────────────────────┐ ║
15
+ ║ │ 5-Hour [████████████████░░░░░░░░░░░░░░] 56% 2h31m │ ║
16
+ ║ │ Weekly [████████████████████████████░░░] 93% 3d12h │ ║
17
+ ║ └────────────────────────────────────────────────────────┘ ║
18
+ ║ 📈 Recent ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁▂▃▄▅▆▇█▇▆▅▄▃ ║
19
+ ╚══════════════════════════════════════════════════════════════╝
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Table of Contents
25
+
26
+ - [Why when-does-my-quota-refresh?](#why-when-does-my-quota-refresh)
27
+ - [Quick Start](#quick-start)
28
+ - [Installation](#installation)
29
+ - [Commands Reference](#commands-reference)
30
+ - [SDK Usage](#sdk-usage)
31
+ - [Data Sources](#data-sources)
32
+ - [Configuration](#configuration)
33
+ - [Plugin System](#plugin-system)
34
+ - [Platform Support](#platform-support)
35
+ - [Development](#development)
36
+ - [Testing](#testing)
37
+ - [Contributing](#contributing)
38
+ - [License](#license)
39
+
40
+ ---
41
+
42
+ ## Why when-does-my-quota-refresh?
43
+
44
+ **antigravity-usage** (the original tool) has significant shortcomings:
45
+
46
+ | | antigravity-usage | when-does-my-quota-refresh |
47
+ |---|---|---|
48
+ | Windows support | ❌ Broken | ✅ Full support |
49
+ | Data completeness | ❌ 5-hour session only | ✅ 5-hour + weekly limits |
50
+ | SDK / library use | ❌ CLI only | ✅ Import as library |
51
+ | Plugin system | ❌ None | ✅ Extensible |
52
+ | Usage history | ❌ None | ✅ SQLite-backed trends |
53
+ | Development | ❌ Stalled (6+ months) | ✅ Active |
54
+
55
+ **when-does-my-quota-refresh** provides:
56
+
57
+ - ✅ **Cross-platform** — Windows, macOS, and Linux
58
+ - ✅ **Complete quota data** — both 5-hour and weekly limits for all model groups
59
+ - ✅ **Full dashboard** — `aq --full` for a complete overview
60
+ - ✅ **SDK-first** — import as a library in your own tools
61
+ - ✅ **Plugin system** — notifications, webhooks, and community plugins
62
+ - ✅ **Usage history** — track and visualize quota trends over time
63
+ - ✅ **Smart auto-wakeup** — maximize daily quota usage via system scheduler
64
+
65
+ ---
66
+
67
+ ## Quick Start
68
+
69
+ ```bash
70
+ # Install globally
71
+ npm install -g when-does-my-quota-refresh
72
+
73
+ # Check quota (uses local IDE connection — no login needed)
74
+ aq
75
+
76
+ # Full dashboard with all accounts
77
+ aq --full
78
+ ```
79
+
80
+ > **No login required** for the default mode. when-does-my-quota-refresh connects directly to Antigravity's language server running in your IDE.
81
+
82
+ ---
83
+
84
+ ## Installation
85
+
86
+ ### npm (recommended)
87
+
88
+ ```bash
89
+ npm install -g when-does-my-quota-refresh
90
+ ```
91
+
92
+ ### From source
93
+
94
+ ```bash
95
+ git clone https://github.com/your-username/when-does-my-quota-refresh.git
96
+ cd when-does-my-quota-refresh
97
+ npm install
98
+ npm run build
99
+ npm link
100
+ ```
101
+
102
+ ### Verify installation
103
+
104
+ ```bash
105
+ aq --version
106
+ aq doctor
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Commands Reference
112
+
113
+ ### Default Command (no subcommand)
114
+
115
+ | Command | Description |
116
+ |---------|-------------|
117
+ | `aq` | Show compact quota for the active account |
118
+ | `aq --full` | Show full dashboard with all accounts, groups, and sparklines |
119
+ | `aq --full --watch` | Live-refreshing full dashboard (30s interval) |
120
+ | `aq --json` | Output default command as JSON |
121
+ | `aq --verbose` | Enable debug logging |
122
+
123
+ ---
124
+
125
+ ### `aq quota` — Display Current Quota
126
+
127
+ ```bash
128
+ aq quota # Compact view for active account
129
+ aq quota --all # Side-by-side comparison of all accounts
130
+ aq quota --refresh # Force fresh fetch (bypass cache)
131
+ aq quota --method local # Force a specific data source (local, cli, oauth)
132
+ aq quota --json # Machine-readable JSON output
133
+ ```
134
+
135
+ **Options:**
136
+
137
+ | Flag | Description |
138
+ |------|-------------|
139
+ | `-a, --all` | Fetch and display all accounts side-by-side |
140
+ | `-r, --refresh` | Skip cache, fetch fresh data |
141
+ | `-m, --method <method>` | Force a specific data source |
142
+ | `--json` | Output as JSON |
143
+
144
+ ---
145
+
146
+ ### `aq accounts` — Manage Accounts
147
+
148
+ ```bash
149
+ aq accounts list # List all configured accounts
150
+ aq accounts add # Login a new Google account (browser flow)
151
+ aq accounts add --manual # Manual login (headless/SSH — shows URL)
152
+ aq accounts switch <email> # Set the active account
153
+ aq accounts remove <email> # Remove an account (with confirmation)
154
+ ```
155
+
156
+ **Subcommands:**
157
+
158
+ | Subcommand | Description |
159
+ |------------|-------------|
160
+ | `list` | List all managed accounts with status |
161
+ | `add` | Start OAuth login flow for a new Google account |
162
+ | `add --manual` | Get a URL for manual login (for headless environments) |
163
+ | `switch <email>` | Set a different account as active |
164
+ | `remove <email>` | Remove an account (prompts for confirmation) |
165
+
166
+ ---
167
+
168
+ ### `aq history` — Usage History & Analytics
169
+
170
+ ```bash
171
+ aq history # Last 7 days for active account
172
+ aq history --days 30 # Last 30 days
173
+ aq history --email user@gmail.com # Specific account
174
+ aq history --group gemini # Filter: gemini, claude, or all
175
+ aq history --compare # Compare all accounts
176
+ aq history --json # Machine-readable output
177
+ ```
178
+
179
+ **Options:**
180
+
181
+ | Flag | Description |
182
+ |------|-------------|
183
+ | `-e, --email <email>` | Account to show (default: active account) |
184
+ | `-d, --days <days>` | Number of days (default: 7) |
185
+ | `--group <group>` | Filter: `gemini`, `claude`, or `all` (default: `all`) |
186
+ | `--compare` | Show side-by-side comparison of all accounts |
187
+ | `--json` | Output as JSON |
188
+
189
+ ---
190
+
191
+ ### `aq wakeup` — Auto-Trigger Quota Refresh
192
+
193
+ Automatically trigger quota refreshes to maximize your daily usage limits. Uses your system's native scheduler (cron on macOS/Linux, Task Scheduler on Windows).
194
+
195
+ ```bash
196
+ aq wakeup config # Interactive setup
197
+ aq wakeup install # Install to system scheduler
198
+ aq wakeup uninstall # Remove from system scheduler
199
+ aq wakeup status # Check if wakeup is installed
200
+ aq wakeup test # Manually test the wakeup trigger
201
+ ```
202
+
203
+ **Subcommands:**
204
+
205
+ | Subcommand | Description |
206
+ |------------|-------------|
207
+ | `config` | Interactive wizard to configure trigger mode, schedule, and accounts |
208
+ | `install` | Register the wakeup job with the system scheduler |
209
+ | `uninstall` | Remove the wakeup job from the system scheduler |
210
+ | `status` | Show current wakeup configuration and scheduler status |
211
+ | `test` | Manually trigger a quota refresh to verify it works |
212
+
213
+ **Trigger Modes:**
214
+
215
+ | Mode | Description |
216
+ |------|-------------|
217
+ | `smart` | Trigger when quota resets are detected (Recommended) |
218
+ | `interval` | Trigger every N hours |
219
+ | `daily` | Trigger at specific times of day |
220
+
221
+ ---
222
+
223
+ ### `aq watch` — Real-Time Monitoring
224
+
225
+ ```bash
226
+ aq watch start # Start monitoring (foreground)
227
+ aq watch start --interval 30 # Custom poll interval (seconds)
228
+ aq watch status # Check daemon status
229
+ ```
230
+
231
+ ---
232
+
233
+ ### `aq doctor` — Diagnose Setup Issues
234
+
235
+ ```bash
236
+ aq doctor
237
+ ```
238
+
239
+ Runs a comprehensive health check:
240
+
241
+ - Platform and Node.js version
242
+ - Config directory and file
243
+ - Account configuration and token validity
244
+ - Antigravity process detection
245
+ - Live quota fetch test
246
+
247
+ ---
248
+
249
+ ### `aq config` — Manage Settings
250
+
251
+ ```bash
252
+ aq config show # Display all configuration
253
+ aq config set <key> <value> # Update a setting
254
+ aq config get <key> # Read a single setting
255
+ aq config path # Show config directory path
256
+ ```
257
+
258
+ **Common settings:**
259
+
260
+ ```bash
261
+ aq config set defaultMode oauth # Change default data source
262
+ aq config set cacheTtlMs 300000 # Cache TTL: 5 minutes
263
+ aq config set notifications false # Disable desktop notifications
264
+ aq config set daemon.pollIntervalMs 30000 # Watch interval: 30 seconds
265
+ ```
266
+
267
+ **Supported config keys:**
268
+
269
+ | Key | Type | Default | Description |
270
+ |-----|------|---------|-------------|
271
+ | `defaultMode` | `string` | `local` | Default data source (`local`, `cli`, `oauth`) |
272
+ | `cacheTtlMs` | `number` | `300000` | Cache time-to-live in milliseconds |
273
+ | `refreshIntervalMs` | `number` | `300000` | Auto-refresh interval |
274
+ | `notifications` | `boolean` | `true` | Desktop notifications |
275
+ | `plugins` | `string[]` | `[]` | Installed plugin names |
276
+ | `wakeupModels` | `string[]` | `[...]` | Models to track in wakeup |
277
+ | `wakeupAccounts` | `string[]` | `[]` | Accounts for wakeup (empty = all) |
278
+ | `daemon.pollIntervalMs` | `number` | `60000` | Watch mode poll interval |
279
+ | `daemon.enabled` | `boolean` | `false` | Enable background daemon |
280
+
281
+ ---
282
+
283
+ ### `aq plugin` — Plugin Management
284
+
285
+ ```bash
286
+ aq plugin list # List installed plugins
287
+ aq plugin install <name> # Install a plugin
288
+ aq plugin remove <name> # Remove a plugin
289
+ ```
290
+
291
+ ---
292
+
293
+ ### Global Flags
294
+
295
+ | Flag | Description |
296
+ |------|-------------|
297
+ | `-V, --version` | Show version number |
298
+ | `-h, --help` | Show help |
299
+ | `--verbose` | Enable debug output |
300
+ | `--json` | Output as JSON (where applicable) |
301
+ | `--full` | Show full dashboard |
302
+ | `--no-history` | Skip history sparkline in full dashboard |
303
+ | `--watch` | Auto-refresh mode (with `--full`) |
304
+
305
+ ---
306
+
307
+ ## SDK Usage
308
+
309
+ when-does-my-quota-refresh ships as an importable SDK for building your own tools and extensions.
310
+
311
+ ### Installation
312
+
313
+ ```bash
314
+ npm install when-does-my-quota-refresh
315
+ ```
316
+
317
+ ### Basic Usage
318
+
319
+ ```typescript
320
+ import { createClient } from 'when-does-my-quota-refresh'
321
+
322
+ const client = createClient()
323
+
324
+ // Fetch quota for active account
325
+ const snapshot = await client.fetchQuota()
326
+ console.log(snapshot.groups)
327
+ // => [{ displayName: 'Gemini Models', buckets: [...] }, ...]
328
+
329
+ // Fetch all accounts
330
+ const snapshots = await client.fetchQuota({ allAccounts: true })
331
+ for (const s of snapshots) {
332
+ console.log(`${s.email}: ${s.groups.length} groups`)
333
+ }
334
+
335
+ // Force refresh (bypass cache)
336
+ const fresh = await client.fetchQuota({ refresh: true })
337
+
338
+ // Force a specific data source
339
+ const fromLocal = await client.fetchQuota({ source: 'local' })
340
+ ```
341
+
342
+ ### Full Dashboard
343
+
344
+ ```typescript
345
+ const { snapshots, history, dailySummary } = await client.getFullDashboard()
346
+
347
+ // snapshots: QuotaSnapshot[] — current state for each account
348
+ // history: Record<string, UsageRecord[]> — recent readings per account
349
+ // dailySummary: Record<string, DailySummary[]> — aggregated daily stats
350
+ ```
351
+
352
+ ### Usage History
353
+
354
+ ```typescript
355
+ // Last 30 days for a specific account
356
+ const history = client.getHistory('user@gmail.com', 30)
357
+
358
+ // Daily summary
359
+ const summary = client.getDailySummary('user@gmail.com', 7)
360
+ ```
361
+
362
+ ### Types
363
+
364
+ All types are exported for TypeScript consumers:
365
+
366
+ ```typescript
367
+ import type {
368
+ QuotaSnapshot,
369
+ QuotaGroup,
370
+ QuotaBucket,
371
+ PromptCreditsInfo,
372
+ DataSource,
373
+ ModelQuotaInfo,
374
+ AccountInfo,
375
+ Plugin,
376
+ UsageRecord,
377
+ ClientOptions,
378
+ } from 'when-does-my-quota-refresh'
379
+ ```
380
+
381
+ ### Error Handling
382
+
383
+ ```typescript
384
+ import {
385
+ SourceUnavailableError,
386
+ AuthError,
387
+ QuotaFetchError,
388
+ } from 'when-does-my-quota-refresh'
389
+
390
+ try {
391
+ const snapshot = await client.fetchQuota()
392
+ } catch (err) {
393
+ if (err instanceof SourceUnavailableError) {
394
+ console.log(`Source ${err.message} is not available`)
395
+ } else if (err instanceof AuthError) {
396
+ console.log('Authentication failed — run `aq accounts add`')
397
+ } else if (err instanceof QuotaFetchError) {
398
+ console.log(`Fetch failed: ${err.message}`)
399
+ }
400
+ }
401
+ ```
402
+
403
+ ### Configuration
404
+
405
+ ```typescript
406
+ import { loadConfig, saveConfig, getConfigDirPath } from 'when-does-my-quota-refresh'
407
+
408
+ // Read current config
409
+ const config = loadConfig()
410
+
411
+ // Update and save
412
+ config.cacheTtlMs = 10 * 60 * 1000 // 10 minutes
413
+ saveConfig(config)
414
+
415
+ // Find where config is stored
416
+ console.log(getConfigDirPath())
417
+ ```
418
+
419
+ ---
420
+
421
+ ## Data Sources
422
+
423
+ when-does-my-quota-refresh uses a smart fallback chain to fetch quota data:
424
+
425
+ ### 1. Local (highest priority)
426
+
427
+ Connects directly to Antigravity's language server running in your IDE (VS Code, Cursor, etc.). This is the **default and recommended** source — no login required.
428
+
429
+ **How it works:**
430
+ 1. Detects running Antigravity processes on your system
431
+ 2. Discovers the language server's local port
432
+ 3. Sends an HTTP request to fetch quota data
433
+
434
+ ### 2. CLI Bridge
435
+
436
+ Uses the `agy` CLI's embedded HTTPS server. Useful when the local connection isn't available.
437
+
438
+ ### 3. OAuth (Google Cloud Code API)
439
+
440
+ Fetches quota data via Google's Cloud Code API using OAuth2 tokens. Requires account login via `aq accounts add`.
441
+
442
+ **When to use:**
443
+ - Running on a remote server without IDE access
444
+ - Need historical data from the cloud
445
+ - Local/CLI sources are unavailable
446
+
447
+ ### Source Selection
448
+
449
+ ```bash
450
+ # Auto-select best source (default)
451
+ aq quota
452
+
453
+ # Force specific source
454
+ aq quota --method local
455
+ aq quota --method oauth
456
+
457
+ # Bypass cache
458
+ aq quota --refresh
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Configuration
464
+
465
+ Configuration is stored in your platform's standard location:
466
+
467
+ | Platform | Path |
468
+ |----------|------|
469
+ | **macOS** | `~/Library/Application Support/when-does-my-quota-refresh/` |
470
+ | **Linux** | `~/.config/when-does-my-quota-refresh/` |
471
+ | **Windows** | `%APPDATA%/when-does-my-quota-refresh/` |
472
+
473
+ ### Config File
474
+
475
+ Located at `<config-dir>/config.json`:
476
+
477
+ ```json
478
+ {
479
+ "defaultMode": "local",
480
+ "cacheTtlMs": 300000,
481
+ "refreshIntervalMs": 300000,
482
+ "wakeupModels": ["claude-sonnet-4-5", "gemini-3-flash", "gemini-3-pro-low"],
483
+ "wakeupAccounts": [],
484
+ "notifications": true,
485
+ "plugins": [],
486
+ "daemon": {
487
+ "pollIntervalMs": 60000,
488
+ "enabled": false
489
+ }
490
+ }
491
+ ```
492
+
493
+ ### Data Directory
494
+
495
+ Usage history and snapshots are stored in `<config-dir>/data/` using SQLite (`better-sqlite3`).
496
+
497
+ ---
498
+
499
+ ## Plugin System
500
+
501
+ when-does-my-quota-refresh has a built-in plugin system for extending functionality.
502
+
503
+ ### Built-in Plugins
504
+
505
+ | Plugin | Description |
506
+ |--------|-------------|
507
+ | `notifications` | Desktop notifications when quota drops below threshold |
508
+ | `webhook` | Send quota updates to a webhook URL |
509
+
510
+ ### Creating a Plugin
511
+
512
+ ```typescript
513
+ import type { Plugin } from 'when-does-my-quota-refresh'
514
+
515
+ const myPlugin: Plugin = {
516
+ name: 'my-plugin',
517
+ version: '1.0.0',
518
+
519
+ // Called when plugin loads
520
+ onInit: async (ctx) => {
521
+ ctx.log('Plugin initialized')
522
+ },
523
+
524
+ // Called on every quota update
525
+ onQuotaUpdate: async (snapshot, ctx) => {
526
+ ctx.log(`Quota updated for ${snapshot.email}`)
527
+ ctx.notify('Quota Update', `${snapshot.groups.length} groups`)
528
+ },
529
+
530
+ // Called when any bucket drops below threshold
531
+ onQuotaLow: async (group, bucket, remaining, ctx) => {
532
+ ctx.notify('Low Quota!', `${group} ${bucket}: ${remaining * 100}% remaining`)
533
+ },
534
+
535
+ // Called when a bucket resets
536
+ onReset: async (group, nextReset, ctx) => {
537
+ ctx.log(`${group} resets at ${nextReset.toISOString()}`)
538
+ },
539
+
540
+ // Called when plugin unloads
541
+ onDestroy: async () => {
542
+ // Cleanup
543
+ },
544
+ }
545
+
546
+ export default myPlugin
547
+ ```
548
+
549
+ ### Plugin Hooks
550
+
551
+ | Hook | When | Parameters |
552
+ |------|------|------------|
553
+ | `onInit` | Plugin loaded | `ctx: PluginContext` |
554
+ | `onQuotaUpdate` | Quota data fetched | `snapshot, ctx` |
555
+ | `onQuotaLow` | Bucket below threshold | `group, bucket, remaining, ctx` |
556
+ | `onReset` | Bucket resets | `group, nextReset, ctx` |
557
+ | `onDestroy` | Plugin unloaded | — |
558
+
559
+ ### Plugin Context
560
+
561
+ ```typescript
562
+ interface PluginContext {
563
+ /** Show a desktop notification */
564
+ notify(title: string, message: string): void
565
+ /** Log a debug message */
566
+ log(message: string): void
567
+ }
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Platform Support
573
+
574
+ ### Windows
575
+
576
+ when-does-my-quota-refresh provides full Windows support, unlike antigravity-usage:
577
+
578
+ - **Process detection** — `wmic` + PowerShell + `netstat`
579
+ - **Port discovery** — `Get-NetTCPConnection` + command-line flag extraction
580
+ - **System scheduler** — Windows Task Scheduler (via `schtasks`)
581
+ - **All commands** — full dashboard, watch, wakeup, history, etc.
582
+
583
+ ### macOS & Linux
584
+
585
+ - **Process detection** — `ps` + `/proc` filesystem
586
+ - **Port discovery** — `lsof` / `/proc/net/tcp`
587
+ - **System scheduler** — `crontab`
588
+ - **Full feature support**
589
+
590
+ ---
591
+
592
+ ## Development
593
+
594
+ ### Prerequisites
595
+
596
+ - Node.js >= 18
597
+ - npm >= 9
598
+
599
+ ### Getting Started
600
+
601
+ ```bash
602
+ git clone https://github.com/your-username/when-does-my-quota-refresh.git
603
+ cd when-does-my-quota-refresh
604
+ npm install
605
+ npm run dev -- quota # Run CLI in development mode
606
+ ```
607
+
608
+ ### Scripts
609
+
610
+ | Command | Description |
611
+ |---------|-------------|
612
+ | `npm run build` | Build SDK + CLI with tsup |
613
+ | `npm run dev` | Run CLI via tsx (no build step) |
614
+ | `npm test` | Run test suite |
615
+ | `npm run test:watch` | Run tests in watch mode |
616
+ | `npm run typecheck` | Type-check without emitting |
617
+
618
+ ### Build Output
619
+
620
+ The build produces:
621
+
622
+ ```
623
+ dist/
624
+ ├── cli/
625
+ │ ├── index.js # CLI entry point (with shebang)
626
+ │ └── index.js.map
627
+ ├── sdk/
628
+ │ ├── index.js # SDK entry point (importable)
629
+ │ ├── index.d.ts # TypeScript declarations
630
+ │ └── index.js.map
631
+ ├── auth-*.js # OAuth chunk
632
+ ├── auth-*.js.map
633
+ ├── chunk-*.js # Shared chunks
634
+ └── chunk-*.js.map
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Testing
640
+
641
+ ### Running Tests
642
+
643
+ ```bash
644
+ # Run all tests
645
+ npm test
646
+
647
+ # Watch mode (re-runs on file changes)
648
+ npm run test:watch
649
+
650
+ # Run a specific test file
651
+ npx vitest run tests/sdk/cache.test.ts
652
+ ```
653
+
654
+ ### Test Structure
655
+
656
+ ```
657
+ tests/
658
+ ├── sdk/
659
+ │ ├── cache.test.ts # Cache behavior tests
660
+ │ └── types.test.ts # Type validation tests
661
+ └── sources/
662
+ └── local/
663
+ ├── parser.test.ts # Response parser tests
664
+ └── process-detector.test.ts # Process detection tests
665
+ ```
666
+
667
+ ### Writing Tests
668
+
669
+ Tests use [Vitest](https://vitest.dev/) with globals enabled:
670
+
671
+ ```typescript
672
+ import { describe, it, expect } from 'vitest'
673
+
674
+ describe('MyFeature', () => {
675
+ it('should do something', () => {
676
+ expect(result).toBe(expected)
677
+ })
678
+ })
679
+ ```
680
+
681
+ ### Testing Before Publishing
682
+
683
+ See [docs/PUBLISHING.md](docs/PUBLISHING.md) for a complete guide on testing before npm publish.
684
+
685
+ ---
686
+
687
+ ## Contributing
688
+
689
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
690
+
691
+ **Quick version:**
692
+
693
+ 1. Fork and clone the repo
694
+ 2. Install dependencies: `npm install`
695
+ 3. Create a feature branch: `git checkout -b feat/my-feature`
696
+ 4. Make changes + add tests
697
+ 5. Verify: `npm run typecheck && npm test && npm run build`
698
+ 6. Commit and open a PR
699
+
700
+ ---
701
+
702
+ ## License
703
+
704
+ [MIT](LICENSE) — see the LICENSE file for details.