vibecodingmachine-core 2025.12.1-534 → 2025.12.22-2230
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 +4 -1
- package/scripts/setup-database.js +108 -0
- package/src/auth/shared-auth-storage.js +43 -6
- 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 +199 -145
- package/src/ide-integration/applescript-manager.js +234 -96
- package/src/ide-integration/claude-code-cli-manager.cjs +120 -1
- package/src/ide-integration/provider-manager.cjs +67 -1
- package/src/index.cjs +2 -0
- package/src/index.js +6 -0
- package/src/llm/direct-llm-manager.cjs +110 -73
- package/src/quota-management/index.js +108 -0
- package/src/sync/aws-setup.js +445 -0
- package/src/sync/sync-engine.js +410 -0
- 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 +865 -100
- package/src/utils/requirements-parser.js +324 -0
- package/src/utils/update-checker.js +7 -0
- package/test-quota-system.js +67 -0
- package/test-requirement-stats.js +66 -0
|
@@ -72,7 +72,7 @@ class AppleScriptManager {
|
|
|
72
72
|
} catch (rerr) {
|
|
73
73
|
this.logger.log('⚠️ Deterministic: failed to read history before send:', rerr.message || rerr);
|
|
74
74
|
historyOut = '';
|
|
75
|
-
} finally { try { unlinkSync(tmpRead); } catch (e) {} }
|
|
75
|
+
} finally { try { unlinkSync(tmpRead); } catch (e) { } }
|
|
76
76
|
|
|
77
77
|
this.logger.log('🔎 Deterministic: recent history snippet length:', historyOut.length);
|
|
78
78
|
|
|
@@ -128,7 +128,7 @@ class AppleScriptManager {
|
|
|
128
128
|
const tempFile = join(tmpdir(), `claude_direct_${Date.now()}.scpt`);
|
|
129
129
|
writeFileSync(tempFile, directSendScript, 'utf8');
|
|
130
130
|
const out = execSync(`osascript "${tempFile}"`, { encoding: 'utf8', timeout: 30000, stdio: 'pipe' }).trim();
|
|
131
|
-
try { unlinkSync(tempFile); } catch (e) {}
|
|
131
|
+
try { unlinkSync(tempFile); } catch (e) { }
|
|
132
132
|
|
|
133
133
|
this.logger.log('🔁 Deterministic direct send output:', out);
|
|
134
134
|
const verified = out.indexOf('|HIST:') !== -1 && out.split('|HIST:')[1].includes(text);
|
|
@@ -439,24 +439,24 @@ class AppleScriptManager {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
442
|
+
this.logger.log(`Executing command: ${command}`);
|
|
443
|
+
|
|
444
|
+
// For Windsurf, we don't wait for the command to complete since it runs in background
|
|
445
|
+
if (windsurfPath.includes('windsurf')) {
|
|
446
|
+
// Use spawn to run in background
|
|
447
|
+
const { spawn } = require('child_process');
|
|
448
|
+
const windsurfProcess = spawn(windsurfPath, ['--reuse-window', repoPath], {
|
|
449
|
+
stdio: 'pipe',
|
|
450
|
+
detached: true
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Don't wait for completion, just give it a moment to start
|
|
454
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
455
|
+
} else {
|
|
456
|
+
execSync(command, { stdio: 'pipe' });
|
|
457
|
+
// Wait a moment for Windsurf to start
|
|
458
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
459
|
+
}
|
|
460
460
|
|
|
461
461
|
this.logger.log('Windsurf opened successfully');
|
|
462
462
|
return {
|
|
@@ -516,6 +516,52 @@ class AppleScriptManager {
|
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Open AWS Kiro IDE with optional repository path
|
|
521
|
+
* @param {string} repoPath - Optional repository path to open
|
|
522
|
+
* @returns {Promise<Object>} Result object with success status and details
|
|
523
|
+
*/
|
|
524
|
+
async openKiro(repoPath = null) {
|
|
525
|
+
try {
|
|
526
|
+
this.logger.log('Opening AWS Kiro...');
|
|
527
|
+
|
|
528
|
+
let command = 'open -a "AWS Kiro"';
|
|
529
|
+
if (repoPath) {
|
|
530
|
+
command += ` "${repoPath}"`;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
this.logger.log(`Executing command: ${command}`);
|
|
535
|
+
execSync(command, { stdio: 'pipe' });
|
|
536
|
+
} catch (e) {
|
|
537
|
+
// Fallback to just "Kiro"
|
|
538
|
+
this.logger.log('Failed to open "AWS Kiro", trying "Kiro"...');
|
|
539
|
+
command = 'open -a "Kiro"';
|
|
540
|
+
if (repoPath) {
|
|
541
|
+
command += ` "${repoPath}"`;
|
|
542
|
+
}
|
|
543
|
+
execSync(command, { stdio: 'pipe' });
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Wait a moment for Kiro to start
|
|
547
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
548
|
+
|
|
549
|
+
this.logger.log('AWS Kiro opened successfully');
|
|
550
|
+
return {
|
|
551
|
+
success: true,
|
|
552
|
+
message: repoPath ? `AWS Kiro opened with repository: ${repoPath}` : 'AWS Kiro opened successfully',
|
|
553
|
+
method: 'applescript'
|
|
554
|
+
};
|
|
555
|
+
} catch (error) {
|
|
556
|
+
this.logger.log('Error opening AWS Kiro:', error.message);
|
|
557
|
+
return {
|
|
558
|
+
success: false,
|
|
559
|
+
error: error.message,
|
|
560
|
+
method: 'applescript'
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
519
565
|
/**
|
|
520
566
|
* Handle Antigravity quota limit by automatically switching models
|
|
521
567
|
* @returns {Promise<{success: boolean, model?: string, error?: string}>}
|
|
@@ -530,11 +576,43 @@ class AppleScriptManager {
|
|
|
530
576
|
set frontmost to true
|
|
531
577
|
delay 0.8
|
|
532
578
|
|
|
533
|
-
-- Try to find
|
|
579
|
+
-- Try to find model selection button with multiple possible texts
|
|
534
580
|
try
|
|
535
|
-
set
|
|
536
|
-
|
|
537
|
-
|
|
581
|
+
set buttonTexts to {"Select another model", "Switch model", "Change model", "Select model", "Try another model", "Choose model"}
|
|
582
|
+
set foundButton to missing value
|
|
583
|
+
|
|
584
|
+
-- Search for button in window 1
|
|
585
|
+
repeat with buttonText in buttonTexts
|
|
586
|
+
try
|
|
587
|
+
set matchingButtons to buttons of window 1 whose name contains buttonText
|
|
588
|
+
if (count of matchingButtons) > 0 then
|
|
589
|
+
set foundButton to item 1 of matchingButtons
|
|
590
|
+
exit repeat
|
|
591
|
+
end if
|
|
592
|
+
end try
|
|
593
|
+
end repeat
|
|
594
|
+
|
|
595
|
+
-- Also search in groups within window 1
|
|
596
|
+
if foundButton is missing value then
|
|
597
|
+
repeat with buttonText in buttonTexts
|
|
598
|
+
try
|
|
599
|
+
set allGroups to groups of window 1
|
|
600
|
+
repeat with grp in allGroups
|
|
601
|
+
try
|
|
602
|
+
set matchingButtons to buttons of grp whose name contains buttonText
|
|
603
|
+
if (count of matchingButtons) > 0 then
|
|
604
|
+
set foundButton to item 1 of matchingButtons
|
|
605
|
+
exit repeat
|
|
606
|
+
end if
|
|
607
|
+
end try
|
|
608
|
+
end repeat
|
|
609
|
+
if foundButton is not missing value then exit repeat
|
|
610
|
+
end try
|
|
611
|
+
end repeat
|
|
612
|
+
end if
|
|
613
|
+
|
|
614
|
+
if foundButton is not missing value then
|
|
615
|
+
click foundButton
|
|
538
616
|
delay 1.5
|
|
539
617
|
|
|
540
618
|
-- Look for model dropdown/menu items
|
|
@@ -543,20 +621,38 @@ class AppleScriptManager {
|
|
|
543
621
|
|
|
544
622
|
repeat with modelName in modelNames
|
|
545
623
|
try
|
|
546
|
-
-- Try to find and click this model option
|
|
547
|
-
set modelItems to
|
|
624
|
+
-- Try to find and click this model option in various locations
|
|
625
|
+
set modelItems to {}
|
|
626
|
+
|
|
627
|
+
-- Try menu items
|
|
628
|
+
try
|
|
629
|
+
set modelItems to menu items of menu 1 of window 1 whose name is modelName
|
|
630
|
+
end try
|
|
631
|
+
|
|
632
|
+
-- Try buttons if no menu items found
|
|
633
|
+
if (count of modelItems) = 0 then
|
|
634
|
+
try
|
|
635
|
+
set modelItems to buttons of window 1 whose name contains modelName
|
|
636
|
+
end try
|
|
637
|
+
end if
|
|
638
|
+
|
|
548
639
|
if (count of modelItems) > 0 then
|
|
549
640
|
click item 1 of modelItems
|
|
550
641
|
delay 0.8
|
|
551
642
|
|
|
552
643
|
-- Click "Accept all" or similar confirmation button
|
|
553
644
|
try
|
|
554
|
-
set
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
645
|
+
set acceptTexts to {"Accept", "OK", "Confirm", "Continue"}
|
|
646
|
+
repeat with acceptText in acceptTexts
|
|
647
|
+
try
|
|
648
|
+
set acceptButtons to buttons of window 1 whose name contains acceptText
|
|
649
|
+
if (count of acceptButtons) > 0 then
|
|
650
|
+
click item 1 of acceptButtons
|
|
651
|
+
delay 0.5
|
|
652
|
+
return "success:" & modelName
|
|
653
|
+
end if
|
|
654
|
+
end try
|
|
655
|
+
end repeat
|
|
560
656
|
end try
|
|
561
657
|
|
|
562
658
|
return "success:" & modelName
|
|
@@ -615,6 +711,8 @@ class AppleScriptManager {
|
|
|
615
711
|
return await this.openAntigravity(repoPath);
|
|
616
712
|
case 'vscode':
|
|
617
713
|
return await this.openVSCode(repoPath);
|
|
714
|
+
case 'kiro':
|
|
715
|
+
return await this.openKiro(repoPath);
|
|
618
716
|
case 'claude':
|
|
619
717
|
return await this.openClaude(repoPath);
|
|
620
718
|
case 'gemini':
|
|
@@ -781,7 +879,7 @@ class AppleScriptManager {
|
|
|
781
879
|
};
|
|
782
880
|
}
|
|
783
881
|
|
|
784
|
-
const ideName = ide === 'windsurf' ? 'Windsurf' : ide === 'cursor' ? 'Cursor' : ide === 'antigravity' ? 'Antigravity' : ide === 'claude' ? 'Claude' : ide === 'vscode' ? 'VS Code' : 'Unknown IDE';
|
|
882
|
+
const ideName = ide === 'windsurf' ? 'Windsurf' : ide === 'cursor' ? 'Cursor' : ide === 'antigravity' ? 'Antigravity' : ide === 'claude' ? 'Claude' : ide === 'vscode' ? 'VS Code' : ide === 'kiro' ? 'AWS Kiro' : 'Unknown IDE';
|
|
785
883
|
|
|
786
884
|
this.logger.log(`🚀 [${ideName}] Starting text send on ${this.platform} platform`);
|
|
787
885
|
this.logger.log(`🚀 [${ideName}] Text to send: "${text}"`);
|
|
@@ -998,15 +1096,13 @@ class AppleScriptManager {
|
|
|
998
1096
|
success: false,
|
|
999
1097
|
method: 'applescript',
|
|
1000
1098
|
error: `Invalid text parameter for Claude: received ${typeof text} instead of string`,
|
|
1001
|
-
|
|
1099
|
+
note: 'Text validation failed'
|
|
1002
1100
|
};
|
|
1003
1101
|
}
|
|
1004
1102
|
|
|
1005
|
-
const
|
|
1006
|
-
const findAndSendScript = `
|
|
1103
|
+
const findAndSendToClaudeScript = `
|
|
1007
1104
|
tell application "Terminal"
|
|
1008
1105
|
activate
|
|
1009
|
-
|
|
1010
1106
|
set claudeWindow to null
|
|
1011
1107
|
set claudeTab to null
|
|
1012
1108
|
set windowCount to count of windows
|
|
@@ -1070,58 +1166,58 @@ class AppleScriptManager {
|
|
|
1070
1166
|
const tempFile = join(tmpdir(), `claude_script_${Date.now()}.scpt`);
|
|
1071
1167
|
writeFileSync(tempFile, findAndSendScript, 'utf8');
|
|
1072
1168
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1169
|
+
let result;
|
|
1170
|
+
try {
|
|
1171
|
+
result = execSync(`osascript "${tempFile}"`, {
|
|
1172
|
+
encoding: 'utf8',
|
|
1173
|
+
timeout: 30000, // 30 seconds to allow for window searching and delays
|
|
1174
|
+
stdio: 'pipe'
|
|
1175
|
+
}).toString().trim();
|
|
1176
|
+
} finally {
|
|
1177
|
+
// Clean up temp file
|
|
1178
|
+
try { unlinkSync(tempFile); } catch (cleanupError) { this.logger.log(`⚠️ [Claude] Failed to cleanup temp file: ${cleanupError.message}`); }
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
this.logger.log(`✅ [Claude] AppleScript execution output:`, result);
|
|
1084
1182
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
note: result
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
this.logger.log('⚠️ [Claude] Sent text NOT found in terminal history snippet; will attempt deterministic retry');
|
|
1100
|
-
} catch (verifyErr) {
|
|
1101
|
-
this.logger.log('⚠️ [Claude] Verification parse error:', verifyErr.message || verifyErr);
|
|
1183
|
+
// Verify that the sent text appears in the returned history snippet
|
|
1184
|
+
try {
|
|
1185
|
+
const sentVerified = result && result.indexOf('|HIST:') !== -1 && result.split('|HIST:')[1].includes(`${text}`);
|
|
1186
|
+
if (sentVerified) {
|
|
1187
|
+
this.logger.log('✅ [Claude] Verified sent text present in terminal history snippet');
|
|
1188
|
+
return {
|
|
1189
|
+
success: true,
|
|
1190
|
+
method: 'applescript',
|
|
1191
|
+
message: `Text sent and verified in Claude terminal: ${text}`,
|
|
1192
|
+
note: result
|
|
1193
|
+
};
|
|
1102
1194
|
}
|
|
1195
|
+
this.logger.log('⚠️ [Claude] Sent text NOT found in terminal history snippet; will attempt deterministic retry');
|
|
1196
|
+
} catch (verifyErr) {
|
|
1197
|
+
this.logger.log('⚠️ [Claude] Verification parse error:', verifyErr.message || verifyErr);
|
|
1198
|
+
}
|
|
1103
1199
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
}
|
|
1111
|
-
this.logger.log('⚠️ [Claude] Deterministic retry did not verify:', det && det.note ? det.note : det && det.error ? det.error : det);
|
|
1112
|
-
} catch (detErr) {
|
|
1113
|
-
this.logger.log('❌ [Claude] Deterministic retry failed:', detErr.message || detErr);
|
|
1200
|
+
// If verification failed, attempt a deterministic direct send to window 1 tab 1
|
|
1201
|
+
try {
|
|
1202
|
+
const det = await this.sendDeterministicToTerminalWindow1(text);
|
|
1203
|
+
if (det && det.success) {
|
|
1204
|
+
this.logger.log('✅ [Claude] Deterministic retry verified and succeeded');
|
|
1205
|
+
return { success: true, method: 'applescript', message: `Text sent to newly opened Claude terminal: ${text}`, note: det.note };
|
|
1114
1206
|
}
|
|
1207
|
+
this.logger.log('⚠️ [Claude] Deterministic retry did not verify:', det && det.note ? det.note : det && det.error ? det.error : det);
|
|
1208
|
+
} catch (detErr) {
|
|
1209
|
+
this.logger.log('❌ [Claude] Deterministic retry failed:', detErr.message || detErr);
|
|
1210
|
+
}
|
|
1115
1211
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1212
|
+
// CRITICAL FIX: If we reach here, text was sent but not verified
|
|
1213
|
+
// Return success anyway since the AppleScript execution succeeded
|
|
1214
|
+
this.logger.log('⚠️ [Claude] Text sent but verification inconclusive, returning success');
|
|
1215
|
+
return {
|
|
1216
|
+
success: true,
|
|
1217
|
+
method: 'applescript',
|
|
1218
|
+
message: `Text sent to Claude terminal (verification inconclusive): ${text}`,
|
|
1219
|
+
note: 'Text sent successfully but verification did not confirm'
|
|
1220
|
+
};
|
|
1125
1221
|
} catch (error) {
|
|
1126
1222
|
this.logger.log('❌ [Claude] AppleScript failed:', error.message);
|
|
1127
1223
|
return {
|
|
@@ -1168,11 +1264,53 @@ class AppleScriptManager {
|
|
|
1168
1264
|
end tell
|
|
1169
1265
|
end tell
|
|
1170
1266
|
`;
|
|
1267
|
+
} else if (ide === 'kiro') {
|
|
1268
|
+
// AWS Kiro support
|
|
1269
|
+
this.logger.log('🚀 [AWS Kiro] Sending text via AppleScript accessibility (with fallback check)...');
|
|
1270
|
+
|
|
1271
|
+
appleScript = `
|
|
1272
|
+
tell application "System Events"
|
|
1273
|
+
set processName to "AWS Kiro"
|
|
1274
|
+
try
|
|
1275
|
+
if not (exists process "AWS Kiro") then
|
|
1276
|
+
set processName to "Kiro"
|
|
1277
|
+
end if
|
|
1278
|
+
on error
|
|
1279
|
+
set processName to "Kiro"
|
|
1280
|
+
end try
|
|
1281
|
+
|
|
1282
|
+
tell process processName
|
|
1283
|
+
set frontmost to true
|
|
1284
|
+
delay 1.0
|
|
1285
|
+
|
|
1286
|
+
-- Attempt to focus chat via standard AI IDE shortcuts
|
|
1287
|
+
-- Try Cmd+L (Cursor/Windsurf standard)
|
|
1288
|
+
try
|
|
1289
|
+
key code 37 using {command down}
|
|
1290
|
+
delay 0.5
|
|
1291
|
+
on error
|
|
1292
|
+
-- Ignore if fails
|
|
1293
|
+
end try
|
|
1294
|
+
|
|
1295
|
+
-- Use clipboard for more reliable text entry than keystrokes
|
|
1296
|
+
set the clipboard to "${text.replace(/"/g, '\\"')}"
|
|
1297
|
+
delay 0.3
|
|
1298
|
+
|
|
1299
|
+
-- Paste
|
|
1300
|
+
key code 9 using {command down}
|
|
1301
|
+
delay 0.5
|
|
1302
|
+
|
|
1303
|
+
-- Send Enter
|
|
1304
|
+
key code 36
|
|
1305
|
+
delay 0.5
|
|
1306
|
+
end tell
|
|
1307
|
+
end tell
|
|
1308
|
+
`;
|
|
1171
1309
|
} else {
|
|
1172
1310
|
return {
|
|
1173
1311
|
success: false,
|
|
1174
1312
|
error: `Unsupported IDE for AppleScript: ${ide}`,
|
|
1175
|
-
note: 'AppleScript is only supported for Cursor, Windsurf, Antigravity, and
|
|
1313
|
+
note: 'AppleScript is only supported for Cursor, Windsurf, Antigravity, VS Code, and AWS Kiro'
|
|
1176
1314
|
};
|
|
1177
1315
|
}
|
|
1178
1316
|
|
|
@@ -1820,14 +1958,14 @@ class AppleScriptManager {
|
|
|
1820
1958
|
|
|
1821
1959
|
// Check if the result is just diagnostic information
|
|
1822
1960
|
const isDiagnosticResult = result.includes('DEBUG:') ||
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1961
|
+
result.includes('diagnostic') ||
|
|
1962
|
+
result.includes('No chat content found') ||
|
|
1963
|
+
result.includes('Text areas found: 0') ||
|
|
1964
|
+
result.includes('Static text found:') ||
|
|
1965
|
+
result.includes('Window count:') ||
|
|
1966
|
+
result.includes('Sample text:') ||
|
|
1967
|
+
result.includes('Could not read') ||
|
|
1968
|
+
result.includes('No readable text content detected');
|
|
1831
1969
|
|
|
1832
1970
|
this.logger.log(`🔍 Diagnostic detection check: isDiagnosticResult = ${isDiagnosticResult}`);
|
|
1833
1971
|
this.logger.log(`🔍 Result contains 'DEBUG:': ${result.includes('DEBUG:')}`);
|
|
@@ -2701,9 +2839,9 @@ end tell
|
|
|
2701
2839
|
try {
|
|
2702
2840
|
// Check for VS Code extension environment variables
|
|
2703
2841
|
return !!(process.env.VSCODE_PID ||
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2842
|
+
process.env.VSCODE_INJECTION ||
|
|
2843
|
+
process.env.VSCODE_IPC_HOOK_CLI ||
|
|
2844
|
+
(typeof require !== 'undefined' && require.main && require.main.filename.includes('vscode')));
|
|
2707
2845
|
} catch (error) {
|
|
2708
2846
|
this.logger.log('⚠️ Could not determine VS Code extension context:', error.message);
|
|
2709
2847
|
return false;
|
|
@@ -3,6 +3,25 @@ const { execSync, spawn } = require('child_process');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
// Rate limit patterns for Claude Code
|
|
9
|
+
const RATE_LIMIT_PATTERNS = [
|
|
10
|
+
/quota limit/i,
|
|
11
|
+
/rate limit/i,
|
|
12
|
+
/too many requests/i,
|
|
13
|
+
/limit exceeded/i,
|
|
14
|
+
/quota: \d+\/\d+/i,
|
|
15
|
+
/you'?ve hit your limit/i,
|
|
16
|
+
/resets? (?:at )?\d+[a-z]{2} \(/i,
|
|
17
|
+
/rate limit reached/i,
|
|
18
|
+
/quota exceeded/i,
|
|
19
|
+
/usage limit reached/i,
|
|
20
|
+
/try again in \d+[mh]\d*[ms]?/i,
|
|
21
|
+
/please try again in/i,
|
|
22
|
+
/session limit reached/i,
|
|
23
|
+
/account limit reached/i
|
|
24
|
+
];
|
|
6
25
|
|
|
7
26
|
// Helper function for timestamps
|
|
8
27
|
function getTimestamp() {
|
|
@@ -18,6 +37,7 @@ class ClaudeCodeCLIManager {
|
|
|
18
37
|
constructor() {
|
|
19
38
|
this.logger = console;
|
|
20
39
|
this.runningProcesses = [];
|
|
40
|
+
this.rateLimitInfo = null;
|
|
21
41
|
}
|
|
22
42
|
|
|
23
43
|
/**
|
|
@@ -124,6 +144,14 @@ class ClaudeCodeCLIManager {
|
|
|
124
144
|
this.runningProcesses = [];
|
|
125
145
|
}
|
|
126
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Get rate limit information if the last operation was rate limited
|
|
149
|
+
* @returns {{isRateLimited: boolean, resetTime?: Date, message?: string} | null}
|
|
150
|
+
*/
|
|
151
|
+
getRateLimitInfo() {
|
|
152
|
+
return this.rateLimitInfo || null;
|
|
153
|
+
}
|
|
154
|
+
|
|
127
155
|
/**
|
|
128
156
|
* Send a prompt to Claude Code and wait for completion
|
|
129
157
|
* @param {string} text - The message/prompt to send
|
|
@@ -134,6 +162,61 @@ class ClaudeCodeCLIManager {
|
|
|
134
162
|
* @param {number} options.timeoutMs - Timeout in milliseconds (default 300000 = 5 minutes)
|
|
135
163
|
* @returns {Promise<{success: boolean, output: string, error?: string}>}
|
|
136
164
|
*/
|
|
165
|
+
/**
|
|
166
|
+
* Check if the error indicates a rate limit
|
|
167
|
+
* @param {string} error - The error message
|
|
168
|
+
* @returns {{isRateLimited: boolean, resetTime?: Date, message?: string}}
|
|
169
|
+
*/
|
|
170
|
+
checkRateLimit(error) {
|
|
171
|
+
if (!error) return { isRateLimited: false };
|
|
172
|
+
|
|
173
|
+
const lowerError = error.toLowerCase();
|
|
174
|
+
|
|
175
|
+
// Check for rate limit patterns
|
|
176
|
+
const isRateLimited = RATE_LIMIT_PATTERNS.some(pattern =>
|
|
177
|
+
pattern.test(error)
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (!isRateLimited) {
|
|
181
|
+
return { isRateLimited: false };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Try to extract reset time if available
|
|
185
|
+
let resetTime;
|
|
186
|
+
// Match patterns like "resets 6am (America/Buenos_Aires)" or "resets at 6am (America/Buenos_Aires)"
|
|
187
|
+
const resetMatch = error.match(/resets? (?:at )?(\d+)(?::(\d+))?\s*([ap]m)?\s*(\([^)]+\))?/i);
|
|
188
|
+
if (resetMatch) {
|
|
189
|
+
try {
|
|
190
|
+
const now = new Date();
|
|
191
|
+
let hours = parseInt(resetMatch[1]);
|
|
192
|
+
const minutes = resetMatch[2] ? parseInt(resetMatch[2]) : 0;
|
|
193
|
+
const period = resetMatch[3] ? resetMatch[3].toLowerCase() : null;
|
|
194
|
+
|
|
195
|
+
// Convert to 24-hour format if period is specified
|
|
196
|
+
if (period === 'pm' && hours < 12) hours += 12;
|
|
197
|
+
if (period === 'am' && hours === 12) hours = 0;
|
|
198
|
+
|
|
199
|
+
// Create reset time
|
|
200
|
+
resetTime = new Date(now);
|
|
201
|
+
resetTime.setHours(hours, minutes, 0, 0);
|
|
202
|
+
|
|
203
|
+
// If reset time is in the past, assume it's for tomorrow
|
|
204
|
+
if (resetTime <= now) {
|
|
205
|
+
resetTime.setDate(resetTime.getDate() + 1);
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.error('Error parsing reset time:', e);
|
|
209
|
+
resetTime = null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
isRateLimited: true,
|
|
215
|
+
resetTime,
|
|
216
|
+
message: `Rate limit reached${resetTime ? `, resets at ${resetTime.toLocaleString()}` : ''}`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
137
220
|
async sendText(text, cwd = process.cwd(), options = {}) {
|
|
138
221
|
if (!this.isInstalled()) {
|
|
139
222
|
return {
|
|
@@ -220,6 +303,24 @@ class ClaudeCodeCLIManager {
|
|
|
220
303
|
const chunkText = chunk.toString();
|
|
221
304
|
stderr += chunkText;
|
|
222
305
|
|
|
306
|
+
// Check for rate limits in stderr
|
|
307
|
+
const rateLimitInfo = this.checkRateLimit(chunkText);
|
|
308
|
+
if (rateLimitInfo.isRateLimited) {
|
|
309
|
+
this.rateLimitInfo = rateLimitInfo;
|
|
310
|
+
console.error(chalk.yellow(`\n⚠️ ${rateLimitInfo.message}`));
|
|
311
|
+
|
|
312
|
+
// If we have a reset time, update the provider manager
|
|
313
|
+
if (rateLimitInfo.resetTime) {
|
|
314
|
+
try {
|
|
315
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
316
|
+
const providerManager = new ProviderManager();
|
|
317
|
+
providerManager.markRateLimited('claude-code', 'claude-code-cli', rateLimitInfo.message);
|
|
318
|
+
} catch (e) {
|
|
319
|
+
console.error('Failed to update rate limit status:', e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
223
324
|
// Only show actual errors
|
|
224
325
|
if (chunkText.includes('Error:') || chunkText.includes('Failed:') || chunkText.includes('error')) {
|
|
225
326
|
if (onError) {
|
|
@@ -290,9 +391,27 @@ class ClaudeCodeCLIManager {
|
|
|
290
391
|
});
|
|
291
392
|
});
|
|
292
393
|
} catch (error) {
|
|
394
|
+
// Check if this is a rate limit error
|
|
395
|
+
const rateLimitInfo = this.checkRateLimit(error.message);
|
|
396
|
+
if (rateLimitInfo.isRateLimited) {
|
|
397
|
+
this.rateLimitInfo = rateLimitInfo;
|
|
398
|
+
console.error(chalk.yellow(`\n⚠️ ${rateLimitInfo.message}`));
|
|
399
|
+
|
|
400
|
+
// Update provider manager with rate limit info
|
|
401
|
+
try {
|
|
402
|
+
const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
|
|
403
|
+
const providerManager = new ProviderManager();
|
|
404
|
+
providerManager.markRateLimited('claude-code', 'claude-code-cli', rateLimitInfo.message);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
console.error('Failed to update rate limit status:', e);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
293
410
|
return {
|
|
294
411
|
success: false,
|
|
295
|
-
error: error.message
|
|
412
|
+
error: error.message,
|
|
413
|
+
rateLimited: rateLimitInfo.isRateLimited,
|
|
414
|
+
rateLimitInfo: rateLimitInfo.isRateLimited ? rateLimitInfo : undefined
|
|
296
415
|
};
|
|
297
416
|
}
|
|
298
417
|
}
|