vibecodingmachine-core 2025.12.6-1702 → 2025.12.24-2348
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/package.json +1 -1
- package/scripts/setup-database.js +108 -0
- package/src/compliance/compliance-manager.js +249 -0
- package/src/compliance/compliance-prompt.js +183 -0
- package/src/database/migrations.js +289 -0
- package/src/database/user-database-client.js +266 -0
- package/src/database/user-schema.js +118 -0
- package/src/ide-integration/applescript-manager.cjs +76 -129
- package/src/ide-integration/applescript-manager.js +62 -12
- package/src/ide-integration/claude-code-cli-manager.cjs +120 -1
- package/src/ide-integration/provider-manager.cjs +67 -1
- package/src/index.cjs +3 -1
- package/src/index.js +7 -0
- package/src/llm/direct-llm-manager.cjs +110 -73
- package/src/localization/index.js +148 -0
- package/src/localization/translations/en.js +675 -0
- package/src/localization/translations/es.js +676 -0
- package/src/quota-management/index.js +108 -0
- package/src/sync/sync-engine.js +32 -10
- package/src/utils/download-with-progress.js +92 -0
- package/src/utils/electron-update-checker.js +7 -0
- package/src/utils/env-helpers.js +54 -0
- package/src/utils/requirement-helpers.js +745 -49
- package/src/utils/requirements-parser.js +21 -7
- package/src/utils/update-checker.js +7 -0
- package/test-quota-system.js +67 -0
- package/test-requirement-stats.js +66 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const sharedAuth = require('../auth/shared-auth-storage');
|
|
2
|
+
const ProviderManager = require('../ide-integration/provider-manager.cjs');
|
|
3
|
+
|
|
4
|
+
// In-memory cache for quota data.
|
|
5
|
+
const quotaCache = new Map();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Represents the quota details for a specific agent.
|
|
9
|
+
*/
|
|
10
|
+
class Quota {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} agentId - The unique identifier for the agent (e.g., 'openai:gpt-4').
|
|
13
|
+
* @param {number} limit - The total quota limit.
|
|
14
|
+
* @param {number} remaining - The remaining quota.
|
|
15
|
+
* @param {Date} resetsAt - The timestamp when the quota resets.
|
|
16
|
+
* @param {string} type - The type of quota ('global', 'rate-limit', 'infinite').
|
|
17
|
+
*/
|
|
18
|
+
constructor(agentId, limit, remaining, resetsAt, type = 'rate-limit') {
|
|
19
|
+
this.agentId = agentId;
|
|
20
|
+
this.limit = limit;
|
|
21
|
+
this.remaining = remaining;
|
|
22
|
+
this.resetsAt = resetsAt;
|
|
23
|
+
this.lastUpdated = new Date();
|
|
24
|
+
this.type = type;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks if the quota has been exceeded.
|
|
29
|
+
* @returns {boolean} True if the remaining quota is zero or less.
|
|
30
|
+
*/
|
|
31
|
+
isExceeded() {
|
|
32
|
+
return this.remaining <= 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Fetches quota information for a given agent.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} agentId - The ID of the agent to fetch quota for (e.g., 'anthropic:claude-3').
|
|
40
|
+
* @returns {Promise<Quota>} A promise that resolves to a Quota object.
|
|
41
|
+
*/
|
|
42
|
+
async function fetchQuotaForAgent(agentId) {
|
|
43
|
+
// 1. Handle Global Daily Iteration Quota
|
|
44
|
+
if (agentId === 'global:iterations') {
|
|
45
|
+
const quotaInfo = await sharedAuth.canRunAutoMode();
|
|
46
|
+
const today = new Date();
|
|
47
|
+
const tonight = new Date(today);
|
|
48
|
+
tonight.setHours(24, 0, 0, 0);
|
|
49
|
+
|
|
50
|
+
const quota = new Quota(
|
|
51
|
+
'global:iterations',
|
|
52
|
+
quotaInfo.maxIterations || 10,
|
|
53
|
+
quotaInfo.todayUsage !== undefined ? Math.max(0, (quotaInfo.maxIterations || 10) - quotaInfo.todayUsage) : 10,
|
|
54
|
+
tonight,
|
|
55
|
+
'global'
|
|
56
|
+
);
|
|
57
|
+
quotaCache.set(agentId, quota);
|
|
58
|
+
return quota;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Handle Local Agent (Infinite Quota)
|
|
62
|
+
if (agentId.startsWith('local-') || agentId.includes('ollama')) {
|
|
63
|
+
const quota = new Quota(agentId, Infinity, Infinity, null, 'infinite');
|
|
64
|
+
quotaCache.set(agentId, quota);
|
|
65
|
+
return quota;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3. Handle Provider Rate Limits via ProviderManager
|
|
69
|
+
try {
|
|
70
|
+
const providerManager = new ProviderManager();
|
|
71
|
+
const parts = agentId.split(':');
|
|
72
|
+
const provider = parts[0];
|
|
73
|
+
const model = parts[1] || provider;
|
|
74
|
+
|
|
75
|
+
const isLimited = providerManager.isRateLimited(provider, model);
|
|
76
|
+
const timeUntilReset = providerManager.getTimeUntilReset(provider, model);
|
|
77
|
+
|
|
78
|
+
const quota = new Quota(
|
|
79
|
+
agentId,
|
|
80
|
+
1, // Binary limit for rate limits (1 if available, 0 if limited)
|
|
81
|
+
isLimited ? 0 : 1,
|
|
82
|
+
timeUntilReset ? new Date(Date.now() + timeUntilReset) : null,
|
|
83
|
+
'rate-limit'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
quotaCache.set(agentId, quota);
|
|
87
|
+
return quota;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(`Error fetching provider quota for ${agentId}:`, error);
|
|
90
|
+
return new Quota(agentId, 1, 1, null, 'rate-limit');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Retrieves the cached quota for a given agent.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} agentId - The ID of the agent.
|
|
98
|
+
* @returns {Quota | undefined} The cached Quota object or undefined if not found.
|
|
99
|
+
*/
|
|
100
|
+
function getCachedQuota(agentId) {
|
|
101
|
+
return quotaCache.get(agentId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
Quota,
|
|
106
|
+
fetchQuotaForAgent,
|
|
107
|
+
getCachedQuota,
|
|
108
|
+
};
|
package/src/sync/sync-engine.js
CHANGED
|
@@ -54,17 +54,27 @@ class SyncEngine extends EventEmitter {
|
|
|
54
54
|
// Initialize DynamoDB client
|
|
55
55
|
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
|
|
56
56
|
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb');
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
59
59
|
const client = new DynamoDBClient({ region });
|
|
60
60
|
this.dynamoClient = DynamoDBDocumentClient.from(client);
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
// Initialize WebSocket client for real-time updates
|
|
63
63
|
await this._initializeWebSocket();
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
this.emit('initialized');
|
|
66
66
|
return true;
|
|
67
67
|
} catch (error) {
|
|
68
|
+
// If AWS SDK is not installed in this environment (common in local dev),
|
|
69
|
+
// fall back to offline mode instead of throwing so the CLI remains usable.
|
|
70
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && /@aws-sdk\//.test(error.message)) {
|
|
71
|
+
this.options.offlineMode = true;
|
|
72
|
+
this.isOnline = false;
|
|
73
|
+
this.emit('warning', { type: 'offline-fallback', message: 'AWS SDK not available, running in offline mode', error });
|
|
74
|
+
this.emit('initialized');
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
this.emit('error', { type: 'initialization', error });
|
|
69
79
|
throw error;
|
|
70
80
|
}
|
|
@@ -177,11 +187,16 @@ class SyncEngine extends EventEmitter {
|
|
|
177
187
|
* Fetch remote changes from DynamoDB
|
|
178
188
|
*/
|
|
179
189
|
async _fetchRemoteChanges() {
|
|
190
|
+
if (!this.dynamoClient) {
|
|
191
|
+
this.emit('warning', { type: 'no-dynamo', message: 'DynamoDB client not initialized, skipping remote fetch' });
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
|
|
180
195
|
const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
|
|
181
|
-
|
|
196
|
+
|
|
182
197
|
const tableName = process.env.DYNAMODB_TABLE_NAME || 'vibecodingmachine-requirements';
|
|
183
198
|
const lastSync = this.lastSyncTime || 0;
|
|
184
|
-
|
|
199
|
+
|
|
185
200
|
try {
|
|
186
201
|
// Use Scan with filter instead of Query since we need to check all items
|
|
187
202
|
// In production, consider using DynamoDB Streams for real-time updates
|
|
@@ -195,7 +210,7 @@ class SyncEngine extends EventEmitter {
|
|
|
195
210
|
':lastSync': lastSync
|
|
196
211
|
}
|
|
197
212
|
});
|
|
198
|
-
|
|
213
|
+
|
|
199
214
|
const response = await this.dynamoClient.send(command);
|
|
200
215
|
return response.Items || [];
|
|
201
216
|
} catch (error) {
|
|
@@ -303,10 +318,17 @@ class SyncEngine extends EventEmitter {
|
|
|
303
318
|
* Push local changes to remote
|
|
304
319
|
*/
|
|
305
320
|
async _pushLocalChanges(changes) {
|
|
321
|
+
if (!this.dynamoClient) {
|
|
322
|
+
this.emit('warning', { type: 'no-dynamo', message: 'DynamoDB client not initialized, queuing local changes' });
|
|
323
|
+
// Queue all changes for later push
|
|
324
|
+
this.offlineQueue.push(...changes);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
306
328
|
const { PutCommand } = require('@aws-sdk/lib-dynamodb');
|
|
307
|
-
|
|
329
|
+
|
|
308
330
|
const tableName = process.env.DYNAMODB_TABLE_NAME || 'vibecodingmachine-requirements';
|
|
309
|
-
|
|
331
|
+
|
|
310
332
|
for (const change of changes) {
|
|
311
333
|
try {
|
|
312
334
|
const command = new PutCommand({
|
|
@@ -317,12 +339,12 @@ class SyncEngine extends EventEmitter {
|
|
|
317
339
|
...change
|
|
318
340
|
}
|
|
319
341
|
});
|
|
320
|
-
|
|
342
|
+
|
|
321
343
|
await this.dynamoClient.send(command);
|
|
322
344
|
this.emit('local-change-pushed', change);
|
|
323
345
|
} catch (error) {
|
|
324
346
|
this.emit('error', { type: 'push-local', change, error });
|
|
325
|
-
|
|
347
|
+
|
|
326
348
|
// Add to offline queue if push fails
|
|
327
349
|
if (!this.isOnline) {
|
|
328
350
|
this.offlineQueue.push(change);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { pipeline } = require('stream');
|
|
3
|
+
const { promisify } = require('util');
|
|
4
|
+
const streamPipeline = promisify(pipeline);
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
|
|
7
|
+
function progressBar(percent, width) {
|
|
8
|
+
const fill = Math.round((percent / 100) * width);
|
|
9
|
+
return '█'.repeat(fill) + '-'.repeat(Math.max(0, width - fill));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function formatEta(sec) {
|
|
13
|
+
if (!isFinite(sec) || sec === null) return '--:--';
|
|
14
|
+
const s = Math.max(0, Math.round(sec));
|
|
15
|
+
const m = Math.floor(s / 60);
|
|
16
|
+
const ss = s % 60;
|
|
17
|
+
return `${m}:${ss.toString().padStart(2, '0')}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function downloadWithProgress(url, dest, opts = {}) {
|
|
21
|
+
const fetch = require('node-fetch');
|
|
22
|
+
const spinner = opts.spinner || ora();
|
|
23
|
+
const label = opts.label || 'Downloading...';
|
|
24
|
+
const onProgress = typeof opts.onProgress === 'function' ? opts.onProgress : null;
|
|
25
|
+
|
|
26
|
+
spinner.start(label);
|
|
27
|
+
|
|
28
|
+
const res = await fetch(url);
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
spinner.fail(`Download failed: ${res.status} ${res.statusText}`);
|
|
31
|
+
throw new Error(`Failed to download ${url}: ${res.status}`);
|
|
32
|
+
}
|
|
33
|
+
// Stop the spinner so progress prints are not overwritten
|
|
34
|
+
try { spinner.stop(); } catch (e) {}
|
|
35
|
+
try { process.stdout.write('\r\x1b[2KDownloading: 0.0 MB'); } catch (e) {}
|
|
36
|
+
|
|
37
|
+
const total = Number(res.headers.get('content-length')) || 0;
|
|
38
|
+
const fileStream = fs.createWriteStream(dest);
|
|
39
|
+
|
|
40
|
+
return await new Promise((resolve, reject) => {
|
|
41
|
+
let downloaded = 0;
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
let lastPercent = -1;
|
|
44
|
+
|
|
45
|
+
res.body.on('data', (chunk) => {
|
|
46
|
+
downloaded += chunk.length;
|
|
47
|
+
if (total) {
|
|
48
|
+
const percent = Math.round((downloaded / total) * 100);
|
|
49
|
+
if (percent !== lastPercent) {
|
|
50
|
+
lastPercent = percent;
|
|
51
|
+
const mbDownloaded = (downloaded / (1024 * 1024)).toFixed(1);
|
|
52
|
+
const mbTotal = (total / (1024 * 1024)).toFixed(1);
|
|
53
|
+
const elapsed = Math.max(0.001, (Date.now() - start) / 1000);
|
|
54
|
+
const speed = downloaded / elapsed; // bytes/sec
|
|
55
|
+
const etaSec = (total - downloaded) / (speed || 1);
|
|
56
|
+
const eta = formatEta(etaSec);
|
|
57
|
+
const bar = progressBar(percent, 30);
|
|
58
|
+
// CLI/UI progress
|
|
59
|
+
if (onProgress) {
|
|
60
|
+
onProgress({ percent, downloaded, total, mbDownloaded, mbTotal, eta, bar });
|
|
61
|
+
} else {
|
|
62
|
+
process.stdout.write(`\r\x1b[2K[${bar}] ${percent}% ${mbDownloaded}MB / ${mbTotal}MB ETA: ${eta}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const mbDownloaded = (downloaded / (1024 * 1024)).toFixed(1);
|
|
67
|
+
if (onProgress) onProgress({ percent: null, downloaded, total, mbDownloaded });
|
|
68
|
+
else process.stdout.write(`\r\x1b[2K${label} ${mbDownloaded} MB`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
res.body.on('error', (err) => {
|
|
73
|
+
spinner.fail('Download error');
|
|
74
|
+
reject(err);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
fileStream.on('error', (err) => {
|
|
78
|
+
spinner.fail('File write error');
|
|
79
|
+
reject(err);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
fileStream.on('finish', () => {
|
|
83
|
+
process.stdout.write('\n');
|
|
84
|
+
spinner.succeed('Download complete');
|
|
85
|
+
resolve();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
res.body.pipe(fileStream);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { downloadWithProgress };
|
|
@@ -39,12 +39,19 @@ function formatVersionTimestamp(versionString, date) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const { shouldCheckUpdates } = require('./env-helpers');
|
|
43
|
+
|
|
42
44
|
/**
|
|
43
45
|
* Check for Electron app updates from S3 version manifest
|
|
44
46
|
* @param {string} currentVersion - Current version (e.g., '2025.11.26-0519')
|
|
45
47
|
* @returns {Promise<Object>} Update info or null if no update available
|
|
46
48
|
*/
|
|
47
49
|
async function checkForElectronUpdates(currentVersion) {
|
|
50
|
+
if (!shouldCheckUpdates()) {
|
|
51
|
+
console.log('ℹ️ [UPDATE CHECK] Skipped in development environment');
|
|
52
|
+
return { hasUpdate: false, currentVersion };
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
return new Promise((resolve, reject) => {
|
|
49
56
|
const manifestUrl = `https://d3fh7zgi8horze.cloudfront.net/downloads/version.json?t=${Date.now()}`;
|
|
50
57
|
console.log(`🔍 [UPDATE CHECK] Current version: ${currentVersion}`);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if running in development environment
|
|
6
|
+
* @returns {boolean} True if in development
|
|
7
|
+
*/
|
|
8
|
+
function isDevelopment() {
|
|
9
|
+
// Check standard environment variables
|
|
10
|
+
if (process.env.NODE_ENV === 'development' ||
|
|
11
|
+
process.env.VIBECODINGMACHINE_ENV === 'development' ||
|
|
12
|
+
process.env.ELECTRON_IS_DEV === '1') {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check for --dev flag in arguments
|
|
17
|
+
if (process.argv.includes('--dev')) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Heuristics for local development
|
|
22
|
+
// 1. Check if we are in a git repository that looks like the source code
|
|
23
|
+
// Root of the monorepo usually has 'packages' directory and 'lerna.json' or 'pnpm-workspace.yaml'
|
|
24
|
+
// But this might be too aggressive if user installs via git clone.
|
|
25
|
+
|
|
26
|
+
// 2. Check if electron is running from default app (not packaged)
|
|
27
|
+
if (process.versions && process.versions.electron) {
|
|
28
|
+
const electron = require('electron');
|
|
29
|
+
if (electron.app && !electron.app.isPackaged) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if we should perform update checks
|
|
39
|
+
* @returns {boolean} True if update checks are allowed
|
|
40
|
+
*/
|
|
41
|
+
function shouldCheckUpdates() {
|
|
42
|
+
// Don't check updates in development unless explicitly forced (not implemented yet)
|
|
43
|
+
if (isDevelopment()) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Can add more logic here (e.g. disable updates via config)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
isDevelopment,
|
|
53
|
+
shouldCheckUpdates
|
|
54
|
+
};
|