erosolar-cli 2.1.11 → 2.1.12
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/dist/providers/openaiChatCompletionsProvider.js +16 -5
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +5 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +273 -137
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/PromptController.d.ts +1 -0
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +1 -0
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +2 -7
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +1 -0
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stdin as input, stdout as output, exit } from 'node:process';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
2
3
|
import { exec } from 'node:child_process';
|
|
3
4
|
import { promisify } from 'node:util';
|
|
4
5
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -237,6 +238,11 @@ export class InteractiveShell {
|
|
|
237
238
|
description: 'Show available and loaded plugins',
|
|
238
239
|
category: 'configuration',
|
|
239
240
|
});
|
|
241
|
+
this.slashCommands.push({
|
|
242
|
+
command: '/approvals',
|
|
243
|
+
description: 'Switch between auto and approval mode for high-impact actions',
|
|
244
|
+
category: 'configuration',
|
|
245
|
+
});
|
|
240
246
|
this.slashCommands.push({
|
|
241
247
|
command: '/offsec',
|
|
242
248
|
description: 'AlphaZero offensive security run (start/status/next)',
|
|
@@ -283,6 +289,8 @@ export class InteractiveShell {
|
|
|
283
289
|
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
284
290
|
this.setupStatusTracking();
|
|
285
291
|
this.refreshContextGauge();
|
|
292
|
+
// Prime renderer state before it first paints
|
|
293
|
+
this.refreshControlBar();
|
|
286
294
|
// Start terminal input (sets up handlers)
|
|
287
295
|
this.terminalInput.start();
|
|
288
296
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
@@ -290,7 +298,6 @@ export class InteractiveShell {
|
|
|
290
298
|
// Set up command autocomplete with all slash commands
|
|
291
299
|
this.setupCommandAutocomplete();
|
|
292
300
|
// Render chat box immediately using the streaming UI lifecycle
|
|
293
|
-
this.refreshControlBar();
|
|
294
301
|
this.syncRendererInput();
|
|
295
302
|
this.renderer?.render();
|
|
296
303
|
this.rebuildAgent();
|
|
@@ -420,6 +427,15 @@ export class InteractiveShell {
|
|
|
420
427
|
return this.editGuardMode;
|
|
421
428
|
}
|
|
422
429
|
}
|
|
430
|
+
abbreviatePath(pathValue) {
|
|
431
|
+
if (!pathValue)
|
|
432
|
+
return '—';
|
|
433
|
+
const home = homedir();
|
|
434
|
+
if (home && pathValue.startsWith(home)) {
|
|
435
|
+
return pathValue.replace(home, '~');
|
|
436
|
+
}
|
|
437
|
+
return pathValue;
|
|
438
|
+
}
|
|
423
439
|
async checkAndShowUpdates() {
|
|
424
440
|
try {
|
|
425
441
|
const { checkForUpdates, formatUpdateNotification } = await import('../core/updateChecker.js');
|
|
@@ -490,6 +506,7 @@ export class InteractiveShell {
|
|
|
490
506
|
const statusParts = [
|
|
491
507
|
theme.success('Ready'),
|
|
492
508
|
`${theme.primary('Auto')}: ${this.autoContinueEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
509
|
+
`${theme.primary('Approvals')}: ${this.criticalApprovalMode === 'auto' ? theme.ui.muted('auto') : theme.warning('ask')}`,
|
|
493
510
|
`${theme.primary('Verify')}: ${this.verificationEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
494
511
|
`${theme.primary('Thinking')}: ${theme.info(thinkingLabel)}`,
|
|
495
512
|
`${theme.primary('Autosave')}: ${this.autosaveEnabled ? theme.success('on') : theme.ui.muted('off')}`,
|
|
@@ -649,6 +666,7 @@ export class InteractiveShell {
|
|
|
649
666
|
'/bug',
|
|
650
667
|
'/changes', '/summary',
|
|
651
668
|
'/export',
|
|
669
|
+
'/approvals',
|
|
652
670
|
// Configuration menus
|
|
653
671
|
'/model', '/models',
|
|
654
672
|
'/secrets',
|
|
@@ -1230,6 +1248,78 @@ export class InteractiveShell {
|
|
|
1230
1248
|
display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
|
|
1231
1249
|
this.syncRendererInput();
|
|
1232
1250
|
}
|
|
1251
|
+
async runWithCriticalApproval(label, detail, action) {
|
|
1252
|
+
if (this.criticalApprovalMode === 'auto') {
|
|
1253
|
+
await action();
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
if (this.pendingInteraction && this.pendingInteraction.type === 'critical-approval') {
|
|
1257
|
+
display.showWarning('Finish the pending approval first.');
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
await new Promise((resolve) => {
|
|
1261
|
+
this.pendingInteraction = {
|
|
1262
|
+
type: 'critical-approval',
|
|
1263
|
+
label,
|
|
1264
|
+
detail,
|
|
1265
|
+
onApprove: async () => {
|
|
1266
|
+
this.pendingInteraction = null;
|
|
1267
|
+
try {
|
|
1268
|
+
await action();
|
|
1269
|
+
}
|
|
1270
|
+
catch (error) {
|
|
1271
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
1272
|
+
}
|
|
1273
|
+
resolve();
|
|
1274
|
+
},
|
|
1275
|
+
onCancel: () => {
|
|
1276
|
+
this.pendingInteraction = null;
|
|
1277
|
+
display.showInfo('Action cancelled.');
|
|
1278
|
+
resolve();
|
|
1279
|
+
},
|
|
1280
|
+
};
|
|
1281
|
+
this.showCriticalApprovalPrompt(label, detail);
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
showCriticalApprovalPrompt(label, detail) {
|
|
1285
|
+
const lines = [];
|
|
1286
|
+
lines.push(theme.gradient.primary('⚠️ Approval required'));
|
|
1287
|
+
lines.push('');
|
|
1288
|
+
lines.push(theme.bold(label));
|
|
1289
|
+
if (detail) {
|
|
1290
|
+
lines.push(theme.ui.muted(detail));
|
|
1291
|
+
}
|
|
1292
|
+
lines.push('');
|
|
1293
|
+
lines.push('Type "yes" to proceed or "no" to cancel.');
|
|
1294
|
+
display.showSystemMessage(lines.join('\n'));
|
|
1295
|
+
this.syncRendererInput();
|
|
1296
|
+
}
|
|
1297
|
+
async handleCriticalApprovalInput(input) {
|
|
1298
|
+
const pending = this.pendingInteraction;
|
|
1299
|
+
if (!pending || pending.type !== 'critical-approval') {
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const normalized = input.trim().toLowerCase();
|
|
1303
|
+
if (!normalized) {
|
|
1304
|
+
display.showWarning('Enter "yes" to proceed or "no" to cancel.');
|
|
1305
|
+
this.syncRendererInput();
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
if (normalized === 'yes' || normalized === 'y') {
|
|
1309
|
+
this.pendingInteraction = null;
|
|
1310
|
+
await pending.onApprove();
|
|
1311
|
+
this.syncRendererInput();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
if (normalized === 'no' || normalized === 'n' || normalized === 'cancel' || normalized === 'c') {
|
|
1315
|
+
this.pendingInteraction = null;
|
|
1316
|
+
pending.onCancel?.();
|
|
1317
|
+
this.syncRendererInput();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
display.showWarning('Please respond with "yes" to proceed or "no" to cancel.');
|
|
1321
|
+
this.syncRendererInput();
|
|
1322
|
+
}
|
|
1233
1323
|
setupHandlers() {
|
|
1234
1324
|
// Handle terminal resize
|
|
1235
1325
|
output.on('resize', () => {
|
|
@@ -1350,9 +1440,11 @@ export class InteractiveShell {
|
|
|
1350
1440
|
criticalApprovalMode: this.criticalApprovalMode,
|
|
1351
1441
|
criticalApprovalHotkey: 'ctrl+shift+a',
|
|
1352
1442
|
});
|
|
1443
|
+
const workspaceDisplay = this.abbreviatePath(this.workingDir);
|
|
1353
1444
|
this.terminalInput.setChromeMeta({
|
|
1354
1445
|
profile: this.profileLabel,
|
|
1355
|
-
workspace:
|
|
1446
|
+
workspace: workspaceDisplay,
|
|
1447
|
+
directory: workspaceDisplay,
|
|
1356
1448
|
writes: this.describeEditGuardMode(),
|
|
1357
1449
|
sessionLabel: this.describeSessionLabel(),
|
|
1358
1450
|
thinkingLabel: (this.thinkingMode || 'off').toString(),
|
|
@@ -1432,15 +1524,6 @@ export class InteractiveShell {
|
|
|
1432
1524
|
* All three can be shown simultaneously (Erosolar-CLI style).
|
|
1433
1525
|
*/
|
|
1434
1526
|
refreshStatusLine(forceRender = false) {
|
|
1435
|
-
const statusText = this.formatStatusLine(this.statusLineState);
|
|
1436
|
-
// Compose streaming/override/base status into a single line so the prompt
|
|
1437
|
-
// looks the same before and during streaming.
|
|
1438
|
-
this.terminalInput.setStatusLine({
|
|
1439
|
-
streaming: this.streamingStatusLabel,
|
|
1440
|
-
override: this.statusMessageOverride,
|
|
1441
|
-
main: statusText,
|
|
1442
|
-
});
|
|
1443
|
-
// Surface meta header (elapsed + context usage) above the divider
|
|
1444
1527
|
const elapsedSeconds = this.getAiRuntimeSeconds();
|
|
1445
1528
|
const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
|
|
1446
1529
|
const tokensUsed = this.latestTokenUsage.used;
|
|
@@ -1873,6 +1956,9 @@ export class InteractiveShell {
|
|
|
1873
1956
|
case 'plan-approval':
|
|
1874
1957
|
await this.handlePlanApprovalInput(input);
|
|
1875
1958
|
return true;
|
|
1959
|
+
case 'critical-approval':
|
|
1960
|
+
await this.handleCriticalApprovalInput(input);
|
|
1961
|
+
return true;
|
|
1876
1962
|
default:
|
|
1877
1963
|
return false;
|
|
1878
1964
|
}
|
|
@@ -1892,6 +1978,9 @@ export class InteractiveShell {
|
|
|
1892
1978
|
case '/features':
|
|
1893
1979
|
this.showFeaturesMenu(input);
|
|
1894
1980
|
break;
|
|
1981
|
+
case '/approvals':
|
|
1982
|
+
this.handleApprovalsCommand(input);
|
|
1983
|
+
break;
|
|
1895
1984
|
case '/learn':
|
|
1896
1985
|
this.showLearningStatus(input);
|
|
1897
1986
|
break;
|
|
@@ -2075,6 +2164,7 @@ export class InteractiveShell {
|
|
|
2075
2164
|
` ${theme.info('Shift+Tab')} ${theme.ui.muted('Toggle edit mode (auto/ask)')}`,
|
|
2076
2165
|
` ${theme.info('Option+V')} ${theme.ui.muted('Toggle verification')}`,
|
|
2077
2166
|
` ${theme.info('Option+C')} ${theme.ui.muted('Toggle auto-continue')}`,
|
|
2167
|
+
` ${theme.info('Ctrl+Shift+A')} ${theme.ui.muted('Toggle approvals for high-impact actions')}`,
|
|
2078
2168
|
` ${theme.info('Option+T')} ${theme.ui.muted('Cycle thinking mode')}`,
|
|
2079
2169
|
` ${theme.info('Option+E')} ${theme.ui.muted('Toggle edit permission mode')}`,
|
|
2080
2170
|
` ${theme.info('Option+X')} ${theme.ui.muted('Clear/compact context')}`,
|
|
@@ -2639,40 +2729,43 @@ export class InteractiveShell {
|
|
|
2639
2729
|
return;
|
|
2640
2730
|
}
|
|
2641
2731
|
if (subcommand === 'apply') {
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2732
|
+
const runApply = async () => {
|
|
2733
|
+
display.showSystemMessage(theme.gradient.primary('🚀 Running Self-Improvement Cycle...'));
|
|
2734
|
+
display.showSystemMessage('');
|
|
2735
|
+
try {
|
|
2736
|
+
const result = await runSelfImprovementCycle(this.workingDir, {
|
|
2737
|
+
maxChanges: 3,
|
|
2738
|
+
minConfidence: 0.7,
|
|
2739
|
+
runTests: true,
|
|
2740
|
+
autoCommit: true,
|
|
2741
|
+
});
|
|
2742
|
+
const lines = [];
|
|
2743
|
+
lines.push(theme.bold('Results:'));
|
|
2744
|
+
lines.push(result.summary);
|
|
2745
|
+
lines.push('');
|
|
2746
|
+
if (result.applied > 0) {
|
|
2747
|
+
lines.push(theme.success(`✅ Applied ${result.applied} improvements!`));
|
|
2748
|
+
for (const r of result.results.filter(r => r.success)) {
|
|
2749
|
+
lines.push(` - Files: ${r.filesChanged.join(', ')}`);
|
|
2750
|
+
if (r.commitHash) {
|
|
2751
|
+
lines.push(` Commit: ${theme.ui.muted(r.commitHash)}`);
|
|
2752
|
+
lines.push(` Rollback: ${theme.dim(r.rollbackCommand ?? '')}`);
|
|
2753
|
+
}
|
|
2662
2754
|
}
|
|
2663
2755
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2756
|
+
else {
|
|
2757
|
+
lines.push(theme.warning('No improvements were applied.'));
|
|
2758
|
+
for (const r of result.results.filter(r => !r.success)) {
|
|
2759
|
+
lines.push(` ${theme.error('✗')} ${r.error}`);
|
|
2760
|
+
}
|
|
2669
2761
|
}
|
|
2762
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2670
2763
|
}
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2764
|
+
catch (error) {
|
|
2765
|
+
display.showError(`Self-improvement failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2766
|
+
}
|
|
2767
|
+
};
|
|
2768
|
+
await this.runWithCriticalApproval('Apply self-improvement changes', 'Runs automated fixes with tests and commits before continuing.', runApply);
|
|
2676
2769
|
return;
|
|
2677
2770
|
}
|
|
2678
2771
|
if (subcommand === 'dry-run') {
|
|
@@ -2701,7 +2794,7 @@ export class InteractiveShell {
|
|
|
2701
2794
|
return;
|
|
2702
2795
|
}
|
|
2703
2796
|
if (subcommand === 'auto') {
|
|
2704
|
-
|
|
2797
|
+
await this.runWithCriticalApproval('Start autonomous improvement mode', 'Allows the CLI to apply and validate changes until stopped.', () => this.runAutonomousImprovementMode());
|
|
2705
2798
|
return;
|
|
2706
2799
|
}
|
|
2707
2800
|
if (subcommand === 'stop') {
|
|
@@ -2710,13 +2803,15 @@ export class InteractiveShell {
|
|
|
2710
2803
|
return;
|
|
2711
2804
|
}
|
|
2712
2805
|
if (subcommand === 'rollback') {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2806
|
+
await this.runWithCriticalApproval('Emergency rollback of self-improvement', 'Restores the last checkpoint created by the improvement engine.', () => {
|
|
2807
|
+
const result = emergencyRollback(this.workingDir);
|
|
2808
|
+
if (result.success) {
|
|
2809
|
+
display.showSuccess(result.message);
|
|
2810
|
+
}
|
|
2811
|
+
else {
|
|
2812
|
+
display.showError(result.message);
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2720
2815
|
return;
|
|
2721
2816
|
}
|
|
2722
2817
|
if (subcommand === 'status') {
|
|
@@ -2919,64 +3014,67 @@ export class InteractiveShell {
|
|
|
2919
3014
|
return;
|
|
2920
3015
|
}
|
|
2921
3016
|
if (subcommand === 'start') {
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
3017
|
+
const startEvolution = async () => {
|
|
3018
|
+
display.showSystemMessage(theme.gradient.primary('🧬 Starting Self-Evolution Mode'));
|
|
3019
|
+
display.showSystemMessage('');
|
|
3020
|
+
display.showSystemMessage(theme.bold('Safety Features:'));
|
|
3021
|
+
display.showSystemMessage(' • Git checkpoint created before starting');
|
|
3022
|
+
display.showSystemMessage(' • Each change validated with build + tests');
|
|
3023
|
+
display.showSystemMessage(' • Auto-rollback on failures');
|
|
3024
|
+
display.showSystemMessage(' • Auto-relaunch with improved code');
|
|
3025
|
+
display.showSystemMessage(' • Press Ctrl+C to stop gracefully');
|
|
3026
|
+
display.showSystemMessage('');
|
|
3027
|
+
try {
|
|
3028
|
+
const result = await runSelfEvolution(this.workingDir, {
|
|
3029
|
+
maxIterations: 50,
|
|
3030
|
+
minConfidence: 0.8,
|
|
3031
|
+
runTests: true,
|
|
3032
|
+
autoRelaunch: true,
|
|
3033
|
+
}, {
|
|
3034
|
+
onStart: () => {
|
|
3035
|
+
display.showInfo('Evolution started. Creating checkpoint...');
|
|
3036
|
+
},
|
|
3037
|
+
onIteration: (iteration, issues) => {
|
|
3038
|
+
display.showSystemMessage(`\n[Iteration ${iteration}] Found ${issues.length} high-confidence issues`);
|
|
3039
|
+
},
|
|
3040
|
+
onFix: (issue, success) => {
|
|
3041
|
+
if (success) {
|
|
3042
|
+
display.showSuccess(`Fixed: ${issue.description.slice(0, 50)}`);
|
|
3043
|
+
}
|
|
3044
|
+
else {
|
|
3045
|
+
display.showWarning(`Failed: ${issue.description.slice(0, 50)}`);
|
|
3046
|
+
}
|
|
3047
|
+
},
|
|
3048
|
+
onRelaunch: () => {
|
|
3049
|
+
display.showSystemMessage('');
|
|
3050
|
+
display.showSystemMessage(theme.gradient.primary('🔄 Relaunching with improved code...'));
|
|
3051
|
+
},
|
|
3052
|
+
onComplete: (result) => {
|
|
3053
|
+
display.showSystemMessage('');
|
|
3054
|
+
display.showSuccess(`Evolution complete! Fixed ${result.issuesFixed} issues.`);
|
|
3055
|
+
},
|
|
3056
|
+
onError: (error) => {
|
|
3057
|
+
display.showError(`Evolution error: ${error}`);
|
|
3058
|
+
},
|
|
3059
|
+
});
|
|
3060
|
+
const lines = [];
|
|
3061
|
+
lines.push('');
|
|
3062
|
+
lines.push(theme.bold('Evolution Result:'));
|
|
3063
|
+
lines.push(` Success: ${result.success ? theme.success('Yes') : theme.error('No')}`);
|
|
3064
|
+
lines.push(` Iterations: ${result.iteration}`);
|
|
3065
|
+
lines.push(` Issues Found: ${result.issuesFound}`);
|
|
3066
|
+
lines.push(` Issues Fixed: ${result.issuesFixed}`);
|
|
3067
|
+
if (result.error) {
|
|
3068
|
+
lines.push(` Error: ${theme.error(result.error)}`);
|
|
3069
|
+
}
|
|
3070
|
+
lines.push(` Next Action: ${result.nextAction}`);
|
|
3071
|
+
display.showSystemMessage(lines.join('\n'));
|
|
2973
3072
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
}
|
|
3073
|
+
catch (error) {
|
|
3074
|
+
display.showError(`Evolution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
await this.runWithCriticalApproval('Start self-evolution mode', 'Automates repo changes with checkpoints, tests, and relaunch.', startEvolution);
|
|
2980
3078
|
return;
|
|
2981
3079
|
}
|
|
2982
3080
|
if (subcommand === 'stop') {
|
|
@@ -3243,42 +3341,48 @@ export class InteractiveShell {
|
|
|
3243
3341
|
' /offsec runs';
|
|
3244
3342
|
if (sub === 'start') {
|
|
3245
3343
|
const rest = args.slice(1);
|
|
3246
|
-
const
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3344
|
+
const startRun = async () => {
|
|
3345
|
+
const scope = [];
|
|
3346
|
+
const objectiveParts = [];
|
|
3347
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3348
|
+
if (rest[i]?.toLowerCase() === '--scope') {
|
|
3349
|
+
const scopeArg = rest[i + 1];
|
|
3350
|
+
if (scopeArg) {
|
|
3351
|
+
scope.push(...scopeArg.split(',').map((s) => s.trim()).filter(Boolean));
|
|
3352
|
+
}
|
|
3353
|
+
i += 1;
|
|
3354
|
+
continue;
|
|
3253
3355
|
}
|
|
3254
|
-
i
|
|
3255
|
-
continue;
|
|
3356
|
+
objectiveParts.push(rest[i]);
|
|
3256
3357
|
}
|
|
3257
|
-
objectiveParts.
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3358
|
+
const objective = objectiveParts.join(' ').trim();
|
|
3359
|
+
if (!objective) {
|
|
3360
|
+
display.showWarning('Provide an objective. Example: /offsec start gain shell on api.example.com --scope api.example.com');
|
|
3361
|
+
display.showInfo(usage);
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
const run = startOffsecRun(objective, scope);
|
|
3365
|
+
this.offsecRunId = run.id;
|
|
3366
|
+
const next = getOffsecNextActions(run.id, 3);
|
|
3367
|
+
display.showSystemMessage(theme.gradient.primary('🛡️ Offsec AlphaZero run started'));
|
|
3368
|
+
display.showSystemMessage(formatOffsecStatus(run, next));
|
|
3369
|
+
};
|
|
3370
|
+
await this.runWithCriticalApproval('Start offensive security run', 'Begins an automated security workflow and logs results to ~/.erosolar/offsec.', startRun);
|
|
3270
3371
|
return;
|
|
3271
3372
|
}
|
|
3272
3373
|
if (sub === 'resume') {
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3374
|
+
const resumeRun = async () => {
|
|
3375
|
+
const targetRun = args[1] ?? this.offsecRunId;
|
|
3376
|
+
const run = resumeOffsecRun(targetRun);
|
|
3377
|
+
if (!run) {
|
|
3378
|
+
display.showWarning(`No offsec run found for id ${targetRun ?? '<unset>'}`);
|
|
3379
|
+
return;
|
|
3380
|
+
}
|
|
3381
|
+
this.offsecRunId = run.id;
|
|
3382
|
+
display.showSystemMessage(theme.gradient.primary(`Resumed offsec run ${run.id}`));
|
|
3383
|
+
display.showSystemMessage(formatOffsecStatus(run, getOffsecNextActions(run.id, 3)));
|
|
3384
|
+
};
|
|
3385
|
+
await this.runWithCriticalApproval('Resume offensive security run', 'Continues a previously started offensive security workflow.', resumeRun);
|
|
3282
3386
|
return;
|
|
3283
3387
|
}
|
|
3284
3388
|
if (sub === 'runs') {
|
|
@@ -3933,6 +4037,35 @@ export class InteractiveShell {
|
|
|
3933
4037
|
lines.push(theme.ui.muted('Shift+Enter enables multi-line input.'));
|
|
3934
4038
|
display.showSystemMessage(lines.join('\n'));
|
|
3935
4039
|
}
|
|
4040
|
+
handleApprovalsCommand(input) {
|
|
4041
|
+
const mode = input.split(/\s+/)[1]?.toLowerCase();
|
|
4042
|
+
const showStatus = () => {
|
|
4043
|
+
const lines = [];
|
|
4044
|
+
lines.push(theme.bold('High-Impact Action Approvals'));
|
|
4045
|
+
lines.push('');
|
|
4046
|
+
lines.push(`Current mode: ${this.criticalApprovalMode === 'auto' ? theme.ui.muted('auto (no prompt)') : theme.warning('ask before high-impact actions')}`);
|
|
4047
|
+
lines.push('');
|
|
4048
|
+
lines.push(theme.secondary('Switch mode:'));
|
|
4049
|
+
lines.push(' /approvals auto - run critical flows without prompting');
|
|
4050
|
+
lines.push(' /approvals ask - require approval for major actions');
|
|
4051
|
+
lines.push('');
|
|
4052
|
+
lines.push(theme.ui.muted('Keyboard: Ctrl+Shift+A toggles modes.'));
|
|
4053
|
+
display.showSystemMessage(lines.join('\n'));
|
|
4054
|
+
};
|
|
4055
|
+
if (!mode || mode === 'status') {
|
|
4056
|
+
showStatus();
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
if (['auto', 'default', 'off'].includes(mode)) {
|
|
4060
|
+
this.setCriticalApprovalMode('auto', 'command');
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
if (['ask', 'approval', 'require', 'confirm', 'on'].includes(mode)) {
|
|
4064
|
+
this.setCriticalApprovalMode('approval', 'command');
|
|
4065
|
+
return;
|
|
4066
|
+
}
|
|
4067
|
+
display.showWarning('Usage: /approvals [auto|ask|status]');
|
|
4068
|
+
}
|
|
3936
4069
|
handlePermissionsCommand() {
|
|
3937
4070
|
const lines = [];
|
|
3938
4071
|
lines.push(theme.bold('Tool Permissions'));
|
|
@@ -3947,6 +4080,9 @@ export class InteractiveShell {
|
|
|
3947
4080
|
lines.push(' Shift+Tab Cycle through modes');
|
|
3948
4081
|
lines.push(' Option+E Toggle edit permission');
|
|
3949
4082
|
lines.push('');
|
|
4083
|
+
lines.push(theme.secondary('High-impact approvals:'));
|
|
4084
|
+
lines.push(' /approvals ask|auto (Ctrl+Shift+A to toggle)');
|
|
4085
|
+
lines.push('');
|
|
3950
4086
|
lines.push(theme.ui.muted('Use /tools to manage which tool suites are enabled.'));
|
|
3951
4087
|
display.showSystemMessage(lines.join('\n'));
|
|
3952
4088
|
}
|