vibecodingmachine-core 2025.12.25-25 → 2026.1.22-1441

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/ERROR_REPORTING_API.md +212 -0
  2. package/ERROR_REPORTING_USAGE.md +380 -0
  3. package/__tests__/provider-manager-fallback.test.js +43 -0
  4. package/__tests__/provider-manager-rate-limit.test.js +61 -0
  5. package/__tests__/utils/git-branch-manager.test.js +61 -0
  6. package/package.json +1 -1
  7. package/src/beta-request.js +160 -0
  8. package/src/compliance/compliance-manager.js +5 -2
  9. package/src/database/migrations.js +135 -12
  10. package/src/database/user-database-client.js +127 -8
  11. package/src/database/user-schema.js +28 -0
  12. package/src/health-tracking/__tests__/ide-health-tracker.test.js +420 -0
  13. package/src/health-tracking/__tests__/interaction-recorder.test.js +392 -0
  14. package/src/health-tracking/errors.js +50 -0
  15. package/src/health-tracking/health-reporter.js +331 -0
  16. package/src/health-tracking/ide-health-tracker.js +446 -0
  17. package/src/health-tracking/interaction-recorder.js +161 -0
  18. package/src/health-tracking/json-storage.js +276 -0
  19. package/src/health-tracking/storage-interface.js +63 -0
  20. package/src/health-tracking/validators.js +277 -0
  21. package/src/ide-integration/applescript-manager.cjs +1087 -9
  22. package/src/ide-integration/applescript-manager.js +565 -15
  23. package/src/ide-integration/applescript-utils.js +26 -18
  24. package/src/ide-integration/provider-manager.cjs +158 -28
  25. package/src/ide-integration/quota-detector.cjs +339 -16
  26. package/src/ide-integration/quota-detector.js +6 -1
  27. package/src/index.cjs +36 -1
  28. package/src/index.js +20 -0
  29. package/src/localization/translations/en.js +15 -1
  30. package/src/localization/translations/es.js +14 -0
  31. package/src/requirement-numbering.js +164 -0
  32. package/src/sync/aws-setup.js +4 -4
  33. package/src/utils/admin-utils.js +33 -0
  34. package/src/utils/error-reporter.js +117 -0
  35. package/src/utils/git-branch-manager.js +278 -0
  36. package/src/utils/requirement-helpers.js +44 -5
  37. package/src/utils/requirements-parser.js +28 -3
  38. package/tests/health-tracking/health-reporter.test.js +329 -0
  39. package/tests/health-tracking/ide-health-tracker.test.js +368 -0
  40. package/tests/health-tracking/interaction-recorder.test.js +309 -0
@@ -0,0 +1,212 @@
1
+ # Error Reporting API Documentation
2
+
3
+ ## Overview
4
+
5
+ The error reporting system automatically sends error information to the admin database when the CLI or GUI encounters errors. This allows the admin to track issues and use VibeCodingMachine to fix itself.
6
+
7
+ ## Client Implementation
8
+
9
+ The client-side error reporting is implemented in:
10
+ - `packages/core/src/utils/error-reporter.js` - Error reporter utility
11
+ - `packages/core/src/database/user-database-client.js` - API client with `reportError()` method
12
+ - `packages/cli/bin/vibecodingmachine.js` - Global error handlers
13
+
14
+ ## API Endpoint Required
15
+
16
+ ### POST /api/errors/report
17
+
18
+ Reports an error to the admin database for tracking and fixing.
19
+
20
+ **Authentication**: Required (Bearer token)
21
+
22
+ **Request Body**:
23
+ ```json
24
+ {
25
+ "message": "The \"path\" argument must be of type string. Received null",
26
+ "stack": "TypeError: The \"path\" argument must be of type string...",
27
+ "name": "TypeError",
28
+ "code": "ERR_INVALID_ARG_TYPE",
29
+ "timestamp": 1704326400000,
30
+ "hostname": "Jesses-MacBook-Pro.local",
31
+ "platform": "darwin",
32
+ "arch": "arm64",
33
+ "nodeVersion": "v18.17.0",
34
+ "context": {
35
+ "command": "addRemoteRequirement",
36
+ "computerId": "OLSEN2018PC",
37
+ "requirementText": "Fix the login bug",
38
+ "cwd": "/Users/jesseolsen/repos/mediawink/allnightai",
39
+ "argv": ["computer:add-requirement", "OLSEN2018PC", "Fix the login bug"]
40
+ }
41
+ }
42
+ ```
43
+
44
+ **Response**:
45
+ ```json
46
+ {
47
+ "success": true,
48
+ "errorId": "err_abc123xyz"
49
+ }
50
+ ```
51
+
52
+ **Error Response**:
53
+ ```json
54
+ {
55
+ "error": "Failed to report error",
56
+ "details": "Database connection failed"
57
+ }
58
+ ```
59
+
60
+ ## Database Schema
61
+
62
+ The error reports should be stored in a DynamoDB table with the following structure:
63
+
64
+ **Table Name**: `vibecodingmachine-errors`
65
+
66
+ **Primary Key**:
67
+ - `errorId` (String) - Unique error identifier (generated server-side)
68
+
69
+ **Attributes**:
70
+ - `userId` (String) - User ID from auth token
71
+ - `email` (String) - User email
72
+ - `timestamp` (Number) - Unix timestamp when error occurred
73
+ - `message` (String) - Error message
74
+ - `stack` (String) - Error stack trace
75
+ - `name` (String) - Error name/type
76
+ - `code` (String) - Error code (if available)
77
+ - `hostname` (String) - Computer hostname
78
+ - `platform` (String) - OS platform (darwin, win32, linux)
79
+ - `arch` (String) - CPU architecture
80
+ - `nodeVersion` (String) - Node.js version
81
+ - `context` (Map) - Additional context about the error
82
+ - `status` (String) - Error status: "NEW", "INVESTIGATING", "FIXED", "IGNORED"
83
+ - `reportedAt` (Number) - Server timestamp when error was reported
84
+
85
+ **GSI (Global Secondary Index)**:
86
+ - `userId-timestamp-index` - For querying errors by user and time
87
+ - `status-timestamp-index` - For querying errors by status
88
+
89
+ ## Server Implementation Required
90
+
91
+ The API server needs to implement the `/api/errors/report` endpoint:
92
+
93
+ ```javascript
94
+ // Example implementation (pseudo-code)
95
+ app.post('/api/errors/report', authenticateToken, async (req, res) => {
96
+ try {
97
+ const userId = req.user.userId;
98
+ const email = req.user.email;
99
+
100
+ const errorId = generateErrorId(); // e.g., 'err_' + crypto.randomBytes(8).toString('hex')
101
+
102
+ const errorReport = {
103
+ errorId,
104
+ userId,
105
+ email,
106
+ ...req.body,
107
+ status: 'NEW',
108
+ reportedAt: Date.now()
109
+ };
110
+
111
+ // Save to DynamoDB
112
+ await dynamodb.put({
113
+ TableName: 'vibecodingmachine-errors',
114
+ Item: errorReport
115
+ }).promise();
116
+
117
+ res.json({ success: true, errorId });
118
+ } catch (error) {
119
+ res.status(500).json({
120
+ error: 'Failed to report error',
121
+ details: error.message
122
+ });
123
+ }
124
+ });
125
+ ```
126
+
127
+ ## Usage Examples
128
+
129
+ ### Automatic Error Reporting (Global Handlers)
130
+
131
+ ```javascript
132
+ // In CLI entry point
133
+ const { errorReporter } = require('vibecodingmachine-core');
134
+
135
+ // Initialize with auth token
136
+ const token = await auth.getAuthToken();
137
+ errorReporter.setAuthToken(token);
138
+
139
+ // Global handlers automatically report errors
140
+ process.on('uncaughtException', async (error) => {
141
+ await errorReporter.reportError(error, { type: 'uncaughtException' });
142
+ });
143
+ ```
144
+
145
+ ### Manual Error Reporting
146
+
147
+ ```javascript
148
+ const { errorReporter } = require('vibecodingmachine-core');
149
+
150
+ try {
151
+ // Some operation that might fail
152
+ await addRemoteRequirement(computerId, requirementText);
153
+ } catch (error) {
154
+ // Report error with context
155
+ await errorReporter.reportError(error, {
156
+ command: 'addRemoteRequirement',
157
+ computerId,
158
+ requirementText
159
+ });
160
+ throw error; // Re-throw to show user
161
+ }
162
+ ```
163
+
164
+ ### Wrapping Functions
165
+
166
+ ```javascript
167
+ const { errorReporter } = require('vibecodingmachine-core');
168
+
169
+ // Wrap a function to automatically report errors
170
+ const safeFunction = errorReporter.wrapFunction(
171
+ async (arg1, arg2) => {
172
+ // Function implementation
173
+ },
174
+ { functionName: 'myFunction' }
175
+ );
176
+ ```
177
+
178
+ ## Admin Dashboard Integration
179
+
180
+ The admin dashboard should display error reports with:
181
+ - List of all errors (filterable by status, user, date)
182
+ - Error details view (message, stack, context)
183
+ - Ability to change error status
184
+ - Statistics (errors per day, most common errors)
185
+ - Link to create a requirement/issue from an error
186
+
187
+ ## Privacy Considerations
188
+
189
+ The error reporter automatically filters sensitive information:
190
+ - Passwords, tokens, and secrets are removed from arguments
191
+ - Only non-sensitive context is included
192
+ - Stack traces may contain file paths but not file contents
193
+
194
+ ## Testing
195
+
196
+ To test error reporting:
197
+
198
+ ```bash
199
+ # Trigger an error in the CLI
200
+ vcm computer:add-requirement TESTPC null
201
+
202
+ # Check that error was reported
203
+ # (Admin dashboard should show the error)
204
+ ```
205
+
206
+ ## Future Enhancements
207
+
208
+ - Automatic error grouping (similar errors grouped together)
209
+ - Error frequency tracking
210
+ - Automatic issue creation in GitHub
211
+ - Email notifications for critical errors
212
+ - Error trend analysis
@@ -0,0 +1,380 @@
1
+ # Error Reporting Usage Guide
2
+
3
+ ## Quick Start
4
+
5
+ ### 1. Import the Error Reporter
6
+
7
+ ```javascript
8
+ const { errorReporter } = require('vibecodingmachine-core');
9
+ ```
10
+
11
+ ### 2. Initialize with Auth Token
12
+
13
+ ```javascript
14
+ const auth = require('./utils/auth');
15
+ const token = await auth.getAuthToken();
16
+ if (token) {
17
+ errorReporter.setAuthToken(token);
18
+ }
19
+ ```
20
+
21
+ ### 3. Report Errors
22
+
23
+ ```javascript
24
+ try {
25
+ // Your code here
26
+ await someOperation();
27
+ } catch (error) {
28
+ // Report error with context
29
+ await errorReporter.reportError(error, {
30
+ command: 'someOperation',
31
+ additionalContext: 'value'
32
+ });
33
+ throw error; // Re-throw to show user
34
+ }
35
+ ```
36
+
37
+ ## Usage Patterns
38
+
39
+ ### Pattern 1: Try-Catch with Error Reporting
40
+
41
+ ```javascript
42
+ async function addRemoteRequirement(computerId, requirementText) {
43
+ try {
44
+ // Operation that might fail
45
+ const result = await performOperation(computerId, requirementText);
46
+ return result;
47
+ } catch (error) {
48
+ console.error(chalk.red('✗ Operation failed:'), error.message);
49
+
50
+ // Report error with context
51
+ await errorReporter.reportError(error, {
52
+ command: 'addRemoteRequirement',
53
+ computerId,
54
+ requirementText
55
+ });
56
+
57
+ throw error; // Re-throw to maintain error flow
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### Pattern 2: Global Error Handlers
63
+
64
+ ```javascript
65
+ // In your main entry point
66
+ const { errorReporter } = require('vibecodingmachine-core');
67
+
68
+ // Initialize with auth token
69
+ const token = await auth.getAuthToken();
70
+ if (token) {
71
+ errorReporter.setAuthToken(token);
72
+ }
73
+
74
+ // Setup global handlers
75
+ process.on('uncaughtException', async (error) => {
76
+ console.error('Uncaught Exception:', error);
77
+ await errorReporter.reportError(error, {
78
+ type: 'uncaughtException'
79
+ });
80
+ process.exit(1);
81
+ });
82
+
83
+ process.on('unhandledRejection', async (error) => {
84
+ console.error('Unhandled Rejection:', error);
85
+ await errorReporter.reportError(error, {
86
+ type: 'unhandledRejection'
87
+ });
88
+ process.exit(1);
89
+ });
90
+ ```
91
+
92
+ ### Pattern 3: Function Wrapping
93
+
94
+ ```javascript
95
+ const { errorReporter } = require('vibecodingmachine-core');
96
+
97
+ // Wrap a function to automatically report errors
98
+ const safeOperation = errorReporter.wrapFunction(
99
+ async (arg1, arg2) => {
100
+ // Your function implementation
101
+ return await performOperation(arg1, arg2);
102
+ },
103
+ {
104
+ functionName: 'performOperation',
105
+ module: 'operations'
106
+ }
107
+ );
108
+
109
+ // Use the wrapped function
110
+ try {
111
+ await safeOperation('value1', 'value2');
112
+ } catch (error) {
113
+ // Error was already reported automatically
114
+ console.error('Operation failed:', error.message);
115
+ }
116
+ ```
117
+
118
+ ### Pattern 4: Conditional Error Reporting
119
+
120
+ ```javascript
121
+ const { errorReporter } = require('vibecodingmachine-core');
122
+
123
+ // Enable/disable error reporting
124
+ errorReporter.setEnabled(true); // Enable
125
+ errorReporter.setEnabled(false); // Disable
126
+
127
+ // Example: Disable in development
128
+ if (process.env.NODE_ENV === 'development') {
129
+ errorReporter.setEnabled(false);
130
+ }
131
+ ```
132
+
133
+ ## Context Best Practices
134
+
135
+ ### Good Context Examples
136
+
137
+ ```javascript
138
+ // ✅ GOOD - Includes relevant context
139
+ await errorReporter.reportError(error, {
140
+ command: 'addRemoteRequirement',
141
+ computerId: 'OLSEN2018PC',
142
+ requirementText: 'Fix the login bug',
143
+ operation: 'file_write'
144
+ });
145
+
146
+ // ✅ GOOD - Includes user action context
147
+ await errorReporter.reportError(error, {
148
+ action: 'sync',
149
+ syncDirection: 'upload',
150
+ fileCount: 5,
151
+ retryAttempt: 2
152
+ });
153
+ ```
154
+
155
+ ### Bad Context Examples
156
+
157
+ ```javascript
158
+ // ❌ BAD - Includes sensitive information
159
+ await errorReporter.reportError(error, {
160
+ password: 'secret123', // Don't include passwords!
161
+ apiKey: 'abc123xyz' // Don't include API keys!
162
+ });
163
+
164
+ // ❌ BAD - Too much unnecessary data
165
+ await errorReporter.reportError(error, {
166
+ entireFileContents: fs.readFileSync('large-file.txt'),
167
+ allEnvironmentVariables: process.env
168
+ });
169
+
170
+ // ❌ BAD - No context at all
171
+ await errorReporter.reportError(error);
172
+ ```
173
+
174
+ ## What Gets Reported Automatically
175
+
176
+ The error reporter automatically includes:
177
+
178
+ - **Error Information**:
179
+ - `message` - Error message
180
+ - `stack` - Stack trace
181
+ - `name` - Error type (TypeError, ReferenceError, etc.)
182
+ - `code` - Error code (if available)
183
+
184
+ - **System Information**:
185
+ - `timestamp` - When error occurred
186
+ - `hostname` - Computer hostname
187
+ - `platform` - OS platform (darwin, win32, linux)
188
+ - `arch` - CPU architecture (x64, arm64)
189
+ - `nodeVersion` - Node.js version
190
+
191
+ - **Context Information**:
192
+ - `cwd` - Current working directory
193
+ - `argv` - Command line arguments (filtered for sensitive data)
194
+ - Any custom context you provide
195
+
196
+ ## Privacy & Security
197
+
198
+ The error reporter automatically filters sensitive information:
199
+
200
+ - Passwords
201
+ - Tokens
202
+ - Secrets
203
+ - API keys
204
+ - Authentication credentials
205
+
206
+ **Example**:
207
+ ```javascript
208
+ // These will be filtered out automatically
209
+ const args = ['--password=secret', '--token=abc123'];
210
+ await errorReporter.reportError(error, { args });
211
+ // Reported args will be filtered
212
+ ```
213
+
214
+ ## Testing Error Reporting
215
+
216
+ ### Test 1: Manual Error
217
+
218
+ ```javascript
219
+ const { errorReporter } = require('vibecodingmachine-core');
220
+
221
+ // Initialize
222
+ const token = await auth.getAuthToken();
223
+ errorReporter.setAuthToken(token);
224
+
225
+ // Create and report a test error
226
+ const testError = new Error('Test error for reporting');
227
+ testError.code = 'TEST_ERROR';
228
+
229
+ await errorReporter.reportError(testError, {
230
+ test: true,
231
+ purpose: 'testing error reporting system'
232
+ });
233
+
234
+ console.log('Test error reported successfully');
235
+ ```
236
+
237
+ ### Test 2: Trigger Real Error
238
+
239
+ ```bash
240
+ # Trigger an error in the CLI
241
+ vcm computer:add-requirement TESTPC null
242
+
243
+ # Check console output for error reporting
244
+ # Check admin dashboard for error report
245
+ ```
246
+
247
+ ## Troubleshooting
248
+
249
+ ### Error Not Being Reported
250
+
251
+ 1. **Check Authentication**:
252
+ ```javascript
253
+ const token = await auth.getAuthToken();
254
+ console.log('Auth token:', token ? 'Present' : 'Missing');
255
+ ```
256
+
257
+ 2. **Check Error Reporter Status**:
258
+ ```javascript
259
+ console.log('Error reporter enabled:', errorReporter.enabled);
260
+ ```
261
+
262
+ 3. **Check Network**:
263
+ - Verify API server is running
264
+ - Check API endpoint URL
265
+ - Verify authentication token is valid
266
+
267
+ ### Silent Failures
268
+
269
+ Error reporting is designed to fail silently to avoid disrupting the user experience. Check console warnings:
270
+
271
+ ```javascript
272
+ // Look for warnings like:
273
+ // "Failed to report error: Network error"
274
+ // "UserDatabase: reportError failed: ..."
275
+ ```
276
+
277
+ ## Integration with Other Systems
278
+
279
+ ### Electron App
280
+
281
+ ```javascript
282
+ // In main process
283
+ const { errorReporter } = require('vibecodingmachine-core');
284
+
285
+ // Initialize with user's auth token
286
+ const token = await getUserAuthToken();
287
+ errorReporter.setAuthToken(token);
288
+
289
+ // Report renderer process errors
290
+ ipcMain.on('renderer-error', async (event, error) => {
291
+ await errorReporter.reportError(error, {
292
+ source: 'renderer',
293
+ window: event.sender.id
294
+ });
295
+ });
296
+ ```
297
+
298
+ ### Web Application
299
+
300
+ ```javascript
301
+ // In your React/Vue app
302
+ import { errorReporter } from 'vibecodingmachine-core';
303
+
304
+ // Initialize with user's auth token
305
+ const token = await getAuthToken();
306
+ errorReporter.setAuthToken(token);
307
+
308
+ // Report errors in error boundary
309
+ class ErrorBoundary extends React.Component {
310
+ componentDidCatch(error, errorInfo) {
311
+ errorReporter.reportError(error, {
312
+ source: 'react',
313
+ componentStack: errorInfo.componentStack
314
+ });
315
+ }
316
+ }
317
+ ```
318
+
319
+ ## API Reference
320
+
321
+ ### `errorReporter.setAuthToken(token)`
322
+
323
+ Set the authentication token for error reporting.
324
+
325
+ **Parameters**:
326
+ - `token` (string) - JWT authentication token
327
+
328
+ **Example**:
329
+ ```javascript
330
+ const token = await auth.getAuthToken();
331
+ errorReporter.setAuthToken(token);
332
+ ```
333
+
334
+ ### `errorReporter.setEnabled(enabled)`
335
+
336
+ Enable or disable error reporting.
337
+
338
+ **Parameters**:
339
+ - `enabled` (boolean) - True to enable, false to disable
340
+
341
+ **Example**:
342
+ ```javascript
343
+ errorReporter.setEnabled(false); // Disable in development
344
+ ```
345
+
346
+ ### `errorReporter.reportError(error, context)`
347
+
348
+ Report an error to the admin database.
349
+
350
+ **Parameters**:
351
+ - `error` (Error) - Error object to report
352
+ - `context` (Object) - Additional context about the error
353
+
354
+ **Returns**: Promise<boolean> - True if reported successfully
355
+
356
+ **Example**:
357
+ ```javascript
358
+ await errorReporter.reportError(error, {
359
+ command: 'sync',
360
+ operation: 'upload'
361
+ });
362
+ ```
363
+
364
+ ### `errorReporter.wrapFunction(fn, context)`
365
+
366
+ Wrap a function to automatically report errors.
367
+
368
+ **Parameters**:
369
+ - `fn` (Function) - Function to wrap
370
+ - `context` (Object) - Context to include in error reports
371
+
372
+ **Returns**: Function - Wrapped function
373
+
374
+ **Example**:
375
+ ```javascript
376
+ const safeFunction = errorReporter.wrapFunction(
377
+ async (arg) => { /* ... */ },
378
+ { functionName: 'myFunction' }
379
+ );
380
+ ```
@@ -0,0 +1,43 @@
1
+ const ProviderManager = require('../src/ide-integration/provider-manager.cjs');
2
+
3
+ describe('ProviderManager fallback behavior', () => {
4
+ let pm;
5
+
6
+ beforeEach(() => {
7
+ pm = new ProviderManager();
8
+ pm.clearAllRateLimits();
9
+ });
10
+
11
+ afterEach(() => {
12
+ pm.clearAllRateLimits();
13
+ });
14
+
15
+ test('isRateLimited should return true for provider when only legacy/model-agnostic key exists', () => {
16
+ // Simulate legacy stored key with model undefined (string 'undefined')
17
+ const resetTime = Date.now() + 10 * 60 * 1000; // 10 minutes from now
18
+ pm.rateLimits['antigravity:undefined'] = {
19
+ provider: 'antigravity',
20
+ model: undefined,
21
+ resetTime,
22
+ resetDate: new Date(resetTime).toISOString(),
23
+ reason: 'Quota limit reached',
24
+ markedAt: new Date().toISOString()
25
+ };
26
+
27
+ expect(pm.isRateLimited('antigravity', 'antigravity')).toBe(true);
28
+ const t = pm.getTimeUntilReset('antigravity', 'antigravity');
29
+ expect(t).toBeGreaterThan(0);
30
+ });
31
+
32
+ test('getRateLimitInfo should return earliest reset for provider when model-specific not present', () => {
33
+ const resetA = Date.now() + 60 * 60 * 1000; // 60m
34
+ const resetB = Date.now() + 5 * 60 * 1000; // 5m
35
+
36
+ pm.rateLimits['antigravity:legacy'] = { provider: 'antigravity', model: 'legacy', resetTime: resetA, reason: 'legacy', markedAt: new Date().toISOString() };
37
+ pm.rateLimits['antigravity:other'] = { provider: 'antigravity', model: 'other', resetTime: resetB, reason: 'other', markedAt: new Date().toISOString() };
38
+
39
+ const info = pm.getRateLimitInfo('antigravity', 'antigravity');
40
+ expect(info.isRateLimited).toBe(true);
41
+ expect(info.resetTime).toBe(resetB);
42
+ });
43
+ });
@@ -0,0 +1,61 @@
1
+ const ProviderManager = require('../src/ide-integration/provider-manager.cjs');
2
+
3
+ describe('ProviderManager rate limit handling', () => {
4
+ let pm;
5
+
6
+ beforeEach(() => {
7
+ pm = new ProviderManager();
8
+ pm.clearAllRateLimits();
9
+ });
10
+
11
+ afterEach(() => {
12
+ pm.clearAllRateLimits();
13
+ });
14
+
15
+ test('markRateLimited with undefined model normalizes model to provider', () => {
16
+ // Call with model undefined to simulate IDE provider case
17
+ pm.markRateLimited('antigravity', undefined, 'Quota limit reached');
18
+
19
+ // The provider should be considered rate-limited when queried using provider as model
20
+ expect(pm.isRateLimited('antigravity', 'antigravity')).toBe(true);
21
+
22
+ const info = pm.getRateLimitInfo('antigravity', 'antigravity');
23
+ expect(info.isRateLimited).toBe(true);
24
+ expect(info.resetTime).toBeGreaterThan(Date.now());
25
+
26
+ const ms = pm.getTimeUntilReset('antigravity', 'antigravity');
27
+ expect(ms).toBeGreaterThan(0);
28
+ });
29
+
30
+
31
+ test('parses "resets Month Day at Time" format correctly', () => {
32
+ // Mock Date.now to Jan 13, 2026 10:00 AM
33
+ const mockNow = new Date('2026-01-13T10:00:00').getTime();
34
+ const originalDateNow = Date.now;
35
+ Date.now = jest.fn(() => mockNow);
36
+
37
+ try {
38
+ const errorMessage = 'Spending cap reached resets Jan 17 at 12pm';
39
+
40
+ // Expected: Jan 17 12:00 PM = 12:00 + (17-13)*24 hours
41
+ // Jan 13 10am to Jan 17 12pm
42
+ // Diff: 4 days + 2 hours = 98 hours
43
+ const expectedDuration = (4 * 24 * 60 * 60 * 1000) + (2 * 60 * 60 * 1000);
44
+
45
+ // Verify regex in test env
46
+ const regex = /resets\s+([a-zA-Z]+)\s+(\d{1,2})\s+at\s+(\d{1,2}(?::\d{2})?)\s*(am|pm)?/i;
47
+ const match = errorMessage.match(regex);
48
+ expect(match).not.toBeNull();
49
+
50
+ const duration = pm.parseRateLimitDuration(errorMessage);
51
+
52
+ // Allow for some minor variance if calculation logic differs slightly, but it should be close
53
+ expect(duration).toBeDefined();
54
+ expect(duration).not.toBeNull();
55
+ expect(Math.abs(duration - expectedDuration)).toBeLessThan(1000);
56
+
57
+ } finally {
58
+ Date.now = originalDateNow;
59
+ }
60
+ });
61
+ });