erosolar-cli 2.1.281 → 2.1.283

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.
@@ -5,6 +5,7 @@ import { promisify } from 'node:util';
5
5
  import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
6
6
  import { join, resolve } from 'node:path';
7
7
  import { UnifiedOrchestrator, } from '../core/unifiedOrchestrator.js';
8
+ import { createOrchestrationUIBridge, } from '../ui/orchestration/OrchestrationUIBridge.js';
8
9
  // Alias for backward compatibility
9
10
  const AgentOrchestrator = UnifiedOrchestrator;
10
11
  import { display } from '../ui/display.js';
@@ -1850,6 +1851,83 @@ export class InteractiveShell {
1850
1851
  this.appendInlineNotice('Please respond with "yes" to proceed or "no" to cancel.', 'warning');
1851
1852
  this.syncRendererInput();
1852
1853
  }
1854
+ isOffensiveIntent(text) {
1855
+ const lower = text.toLowerCase();
1856
+ const keywords = [
1857
+ 'attack',
1858
+ 'offensive',
1859
+ 'exploit',
1860
+ 'apt',
1861
+ 'kill-chain',
1862
+ 'pentest',
1863
+ 'pwn',
1864
+ 'red team',
1865
+ 'execute_real',
1866
+ 'malicious',
1867
+ 'counter-attack',
1868
+ 'counterattack',
1869
+ 'retaliate',
1870
+ 'symmetric response',
1871
+ ];
1872
+ return keywords.some((kw) => lower.includes(kw));
1873
+ }
1874
+ showAttackConfirmationPrompt(request) {
1875
+ const lines = [];
1876
+ lines.push(theme.gradient.primary('⚖️ Confirm operation type'));
1877
+ lines.push('');
1878
+ lines.push(theme.bold('Choose how to proceed:'));
1879
+ lines.push(`${theme.info('1)')} Symmetric cyber response (defensive/retaliatory)`);
1880
+ lines.push(`${theme.info('2)')} Offensive attack (malicious intent)`);
1881
+ lines.push('');
1882
+ lines.push(theme.ui.muted(`Request: ${request.slice(0, 120)}${request.length > 120 ? '…' : ''}`));
1883
+ lines.push('');
1884
+ lines.push('Type 1 or 2 to continue, or cancel/no to abort.');
1885
+ this.showInlinePanel(lines);
1886
+ this.syncRendererInput();
1887
+ }
1888
+ async handleAttackConfirmationInput(input) {
1889
+ const pending = this.pendingInteraction;
1890
+ if (!pending || pending.type !== 'attack-confirmation') {
1891
+ return;
1892
+ }
1893
+ const normalized = input.trim().toLowerCase();
1894
+ if (!normalized) {
1895
+ this.appendInlineNotice('Type 1 for symmetric response, 2 for offensive, or cancel.', 'warning');
1896
+ this.syncRendererInput();
1897
+ return;
1898
+ }
1899
+ if (['cancel', 'c', 'no', 'n'].includes(normalized)) {
1900
+ this.pendingInteraction = null;
1901
+ this.showInlineStatus('Operation cancelled.', 'info', { autoClearMs: 1400 });
1902
+ this.syncRendererInput();
1903
+ return;
1904
+ }
1905
+ let choice = null;
1906
+ if (normalized === '1' || normalized.startsWith('sym')) {
1907
+ choice = 'symmetric';
1908
+ }
1909
+ else if (normalized === '2' || normalized.startsWith('off')) {
1910
+ choice = 'offensive';
1911
+ }
1912
+ if (!choice) {
1913
+ this.appendInlineNotice('Choose 1 (symmetric) or 2 (offensive), or cancel.', 'warning');
1914
+ this.syncRendererInput();
1915
+ return;
1916
+ }
1917
+ this.pendingInteraction = null;
1918
+ const prefix = choice === 'symmetric'
1919
+ ? 'SYMMETRIC CYBER RESPONSE (defensive/retaliatory): '
1920
+ : 'OFFENSIVE ATTACK REQUEST (authorized): ';
1921
+ const forwardedRequest = `${prefix}${pending.request}`;
1922
+ this.showInlineStatus(choice === 'symmetric'
1923
+ ? 'Proceeding with symmetric cyber response...'
1924
+ : 'Proceeding with offensive attack workflow...', choice === 'symmetric' ? 'info' : 'warning', { autoClearMs: 1600 });
1925
+ this.syncRendererInput();
1926
+ await this.processRequest(forwardedRequest, {
1927
+ ...(pending.options ?? {}),
1928
+ skipAttackConfirmation: true,
1929
+ });
1930
+ }
1853
1931
  setupHandlers() {
1854
1932
  // Handle terminal resize
1855
1933
  output.on('resize', () => {
@@ -2929,6 +3007,9 @@ Begin execution immediately.`;
2929
3007
  case 'critical-approval':
2930
3008
  await this.handleCriticalApprovalInput(input);
2931
3009
  return true;
3010
+ case 'attack-confirmation':
3011
+ await this.handleAttackConfirmationInput(input);
3012
+ return true;
2932
3013
  default:
2933
3014
  return false;
2934
3015
  }
@@ -5947,9 +6028,9 @@ serving as both intelligence and deterrence documentation.`;
5947
6028
  }
5948
6029
  /**
5949
6030
  * Execute unified attack chain with real TAO technique execution.
6031
+ * Uses OrchestrationUIBridge for real-time UI updates.
5950
6032
  */
5951
6033
  async executeUnifiedAttackChain(mode, targets, extraOptions = {}) {
5952
- const orchestrator = new AgentOrchestrator(this.workingDir);
5953
6034
  // Build options
5954
6035
  const options = {
5955
6036
  mode,
@@ -5960,9 +6041,23 @@ serving as both intelligence and deterrence documentation.`;
5960
6041
  };
5961
6042
  this.isProcessing = true;
5962
6043
  this.showAttackChainBanner(mode, targets);
6044
+ // Create UI bridge for real-time updates
6045
+ const uiBridge = createOrchestrationUIBridge(display, {
6046
+ updateCoordinator: this.uiAdapter.getUpdateCoordinator(),
6047
+ showRealTimeOutput: true,
6048
+ showProgressBar: true,
6049
+ verboseMode: true,
6050
+ });
6051
+ // Subscribe to events for enhanced feedback
6052
+ const unsubscribeFinding = uiBridge.on('finding', (event) => {
6053
+ const finding = event.data;
6054
+ display.showSystemMessage(`[${finding.severity.toUpperCase()}] ${finding.title}`);
6055
+ });
5963
6056
  try {
5964
- const result = await orchestrator.runUnified(`Execute ${mode} operation against: ${targets.join(', ') || 'infrastructure deployment'}`, options);
5965
- // Display results
6057
+ const objective = `Execute ${mode} operation against: ${targets.join(', ') || 'infrastructure deployment'}`;
6058
+ const result = await uiBridge.execute(objective, options);
6059
+ // Display results - the bridge already shows real-time updates,
6060
+ // but we still show the final summary
5966
6061
  this.displayAttackChainResult(result);
5967
6062
  }
5968
6063
  catch (error) {
@@ -5970,6 +6065,7 @@ serving as both intelligence and deterrence documentation.`;
5970
6065
  this.showSlashWarning(`Attack chain failed: ${message}`);
5971
6066
  }
5972
6067
  finally {
6068
+ uiBridge.dispose();
5973
6069
  this.isProcessing = false;
5974
6070
  this.syncRendererInput();
5975
6071
  }
@@ -7524,6 +7620,15 @@ serving as both intelligence and deterrence documentation.`;
7524
7620
  if (!trimmedRequest) {
7525
7621
  return;
7526
7622
  }
7623
+ if (!options?.skipAttackConfirmation && this.isOffensiveIntent(trimmedRequest)) {
7624
+ this.pendingInteraction = {
7625
+ type: 'attack-confirmation',
7626
+ request: trimmedRequest,
7627
+ options: { orchestrate: options?.orchestrate ?? true, ...options },
7628
+ };
7629
+ this.showAttackConfirmationPrompt(trimmedRequest);
7630
+ return;
7631
+ }
7527
7632
  if (this.isProcessing) {
7528
7633
  this.enqueueFollowUpAction({ type: 'request', text: trimmedRequest });
7529
7634
  return;
@@ -7586,18 +7691,36 @@ serving as both intelligence and deterrence documentation.`;
7586
7691
  let responseText = '';
7587
7692
  let orchestratorResult = null;
7588
7693
  // Always use AI agent - orchestrator tools are available to the AI as needed
7589
- const orchestrate = options?.orchestrate ?? false;
7694
+ const orchestrate = options?.orchestrate ?? true;
7590
7695
  try {
7591
7696
  // Start streaming - no header needed, the input area already provides context
7592
7697
  this.startStreamingHeartbeat('Streaming response');
7593
7698
  // Inject authorization context for security-related requests
7594
7699
  const preparedRequest = this.prepareSecurityRequest(trimmedRequest);
7595
7700
  if (orchestrate) {
7596
- const orchestrator = new AgentOrchestrator(this.workingDir);
7597
- orchestratorResult = await orchestrator.runToCompletion(preparedRequest, {
7598
- verbose: true,
7701
+ // Use OrchestrationUIBridge for real-time UI updates during orchestration
7702
+ const uiBridge = createOrchestrationUIBridge(display, {
7703
+ updateCoordinator: this.uiAdapter.getUpdateCoordinator(),
7704
+ showRealTimeOutput: true,
7705
+ showProgressBar: true,
7706
+ verboseMode: true,
7599
7707
  });
7600
- responseText = orchestratorResult.finalResponse ?? '';
7708
+ // Subscribe to progress events for status updates
7709
+ uiBridge.on('progress', (event) => {
7710
+ const progress = event.data;
7711
+ if (progress.currentCommand) {
7712
+ this.renderer?.setActivity(`Running: ${progress.currentCommand.slice(0, 40)}`);
7713
+ }
7714
+ });
7715
+ try {
7716
+ orchestratorResult = await uiBridge.execute(preparedRequest, {
7717
+ verbose: true,
7718
+ });
7719
+ responseText = orchestratorResult.finalResponse ?? '';
7720
+ }
7721
+ finally {
7722
+ uiBridge.dispose();
7723
+ }
7601
7724
  }
7602
7725
  else {
7603
7726
  responseText = await agent.send(preparedRequest, true);
@@ -7642,12 +7765,20 @@ serving as both intelligence and deterrence documentation.`;
7642
7765
  return 'Received an empty reply while orchestrating; showing last response.';
7643
7766
  case 'no-action':
7644
7767
  return 'No concrete actions were detected; the response may be a plan-only summary.';
7768
+ case 'no-commands':
7769
+ return 'No actions were executed; ensure the request includes a specific, actionable task.';
7645
7770
  case 'verification-needed':
7646
7771
  return 'Verification is recommended based on the model response.';
7647
7772
  case 'blocked':
7648
7773
  return 'The model reported being blocked; check keys, permissions, or missing context.';
7774
+ case 'failed-commands':
7775
+ return 'Some orchestration commands failed; inspect failed commands and retry.';
7776
+ case 'critical-findings':
7777
+ return 'Critical findings detected; address the recommendations and rerun.';
7649
7778
  case 'incomplete':
7650
- return 'Orchestration stopped without a clear completion signal; review the output.';
7779
+ return orchestratorResult.statusSummary
7780
+ ? `Orchestration incomplete: ${orchestratorResult.statusSummary}`
7781
+ : 'Orchestration stopped without a clear completion signal; review the output.';
7651
7782
  default:
7652
7783
  return null;
7653
7784
  }
@@ -7665,6 +7796,9 @@ serving as both intelligence and deterrence documentation.`;
7665
7796
  display.showSystemMessage(`Next steps: ${orchestratorResult.recommendations.join(' | ')}`);
7666
7797
  }
7667
7798
  }
7799
+ // Nudge follow-up when orchestration stops early
7800
+ this.showInlineStatus('Orchestration incomplete: review findings and rerun or apply fixes.', 'warning', { autoClearMs: 3200 });
7801
+ this.renderer?.setActivity('Review findings');
7668
7802
  }
7669
7803
  else if (orchestratorResult && orchestratorResult.exitReason === 'complete') {
7670
7804
  // Task fully complete - show emphasis with elapsed time