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.
- package/ERROR_REPORTING_API.md +212 -0
- package/ERROR_REPORTING_USAGE.md +380 -0
- package/__tests__/provider-manager-fallback.test.js +43 -0
- package/__tests__/provider-manager-rate-limit.test.js +61 -0
- package/__tests__/utils/git-branch-manager.test.js +61 -0
- package/package.json +1 -1
- package/src/beta-request.js +160 -0
- package/src/compliance/compliance-manager.js +5 -2
- package/src/database/migrations.js +135 -12
- package/src/database/user-database-client.js +127 -8
- package/src/database/user-schema.js +28 -0
- package/src/health-tracking/__tests__/ide-health-tracker.test.js +420 -0
- package/src/health-tracking/__tests__/interaction-recorder.test.js +392 -0
- package/src/health-tracking/errors.js +50 -0
- package/src/health-tracking/health-reporter.js +331 -0
- package/src/health-tracking/ide-health-tracker.js +446 -0
- package/src/health-tracking/interaction-recorder.js +161 -0
- package/src/health-tracking/json-storage.js +276 -0
- package/src/health-tracking/storage-interface.js +63 -0
- package/src/health-tracking/validators.js +277 -0
- package/src/ide-integration/applescript-manager.cjs +1087 -9
- package/src/ide-integration/applescript-manager.js +565 -15
- package/src/ide-integration/applescript-utils.js +26 -18
- package/src/ide-integration/provider-manager.cjs +158 -28
- package/src/ide-integration/quota-detector.cjs +339 -16
- package/src/ide-integration/quota-detector.js +6 -1
- package/src/index.cjs +36 -1
- package/src/index.js +20 -0
- package/src/localization/translations/en.js +15 -1
- package/src/localization/translations/es.js +14 -0
- package/src/requirement-numbering.js +164 -0
- package/src/sync/aws-setup.js +4 -4
- package/src/utils/admin-utils.js +33 -0
- package/src/utils/error-reporter.js +117 -0
- package/src/utils/git-branch-manager.js +278 -0
- package/src/utils/requirement-helpers.js +44 -5
- package/src/utils/requirements-parser.js +28 -3
- package/tests/health-tracking/health-reporter.test.js +329 -0
- package/tests/health-tracking/ide-health-tracker.test.js +368 -0
- 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
|
+
});
|