erosolar-cli 2.1.238 → 2.1.240

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.
Files changed (142) hide show
  1. package/README.md +9 -0
  2. package/dist/contracts/tools.schema.json +3 -1
  3. package/dist/core/agent.d.ts.map +1 -1
  4. package/dist/core/agent.js +5 -1
  5. package/dist/core/agent.js.map +1 -1
  6. package/dist/core/agentOrchestrator.d.ts +4 -0
  7. package/dist/core/agentOrchestrator.d.ts.map +1 -1
  8. package/dist/core/agentOrchestrator.js +58 -6
  9. package/dist/core/agentOrchestrator.js.map +1 -1
  10. package/dist/core/autoExecutionOrchestrator.d.ts +172 -0
  11. package/dist/core/autoExecutionOrchestrator.d.ts.map +1 -0
  12. package/dist/core/autoExecutionOrchestrator.js +591 -0
  13. package/dist/core/autoExecutionOrchestrator.js.map +1 -0
  14. package/dist/core/contextManager.d.ts.map +1 -1
  15. package/dist/core/contextManager.js.map +1 -1
  16. package/dist/core/dualAgentOrchestrator.d.ts +34 -0
  17. package/dist/core/dualAgentOrchestrator.d.ts.map +1 -0
  18. package/dist/core/dualAgentOrchestrator.js +94 -0
  19. package/dist/core/dualAgentOrchestrator.js.map +1 -0
  20. package/dist/core/errors/safetyValidator.d.ts +25 -12
  21. package/dist/core/errors/safetyValidator.d.ts.map +1 -1
  22. package/dist/core/errors/safetyValidator.js +165 -17
  23. package/dist/core/errors/safetyValidator.js.map +1 -1
  24. package/dist/core/governmentProcedures.d.ts +118 -0
  25. package/dist/core/governmentProcedures.d.ts.map +1 -0
  26. package/dist/core/governmentProcedures.js +912 -0
  27. package/dist/core/governmentProcedures.js.map +1 -0
  28. package/dist/core/infrastructureTemplates.d.ts +123 -0
  29. package/dist/core/infrastructureTemplates.d.ts.map +1 -0
  30. package/dist/core/infrastructureTemplates.js +1326 -0
  31. package/dist/core/infrastructureTemplates.js.map +1 -0
  32. package/dist/core/integrityVerification.d.ts +250 -0
  33. package/dist/core/integrityVerification.d.ts.map +1 -0
  34. package/dist/core/integrityVerification.js +616 -0
  35. package/dist/core/integrityVerification.js.map +1 -0
  36. package/dist/core/orchestration.d.ts +534 -0
  37. package/dist/core/orchestration.d.ts.map +1 -0
  38. package/dist/core/orchestration.js +2009 -0
  39. package/dist/core/orchestration.js.map +1 -0
  40. package/dist/core/persistentObjectiveStore.d.ts +292 -0
  41. package/dist/core/persistentObjectiveStore.d.ts.map +1 -0
  42. package/dist/core/persistentObjectiveStore.js +613 -0
  43. package/dist/core/persistentObjectiveStore.js.map +1 -0
  44. package/dist/core/preferences.js +1 -1
  45. package/dist/core/preferences.js.map +1 -1
  46. package/dist/core/reliabilityPrompt.d.ts.map +1 -1
  47. package/dist/core/reliabilityPrompt.js +3 -0
  48. package/dist/core/reliabilityPrompt.js.map +1 -1
  49. package/dist/core/securityDeliverableGenerator.d.ts +292 -0
  50. package/dist/core/securityDeliverableGenerator.d.ts.map +1 -0
  51. package/dist/core/securityDeliverableGenerator.js +1590 -0
  52. package/dist/core/securityDeliverableGenerator.js.map +1 -0
  53. package/dist/core/taskCompletionDetector.d.ts.map +1 -1
  54. package/dist/core/taskCompletionDetector.js +4 -1
  55. package/dist/core/taskCompletionDetector.js.map +1 -1
  56. package/dist/shell/autoExecutor.d.ts.map +1 -1
  57. package/dist/shell/autoExecutor.js +32 -3
  58. package/dist/shell/autoExecutor.js.map +1 -1
  59. package/dist/shell/interactiveShell.d.ts +9 -0
  60. package/dist/shell/interactiveShell.d.ts.map +1 -1
  61. package/dist/shell/interactiveShell.js +282 -190
  62. package/dist/shell/interactiveShell.js.map +1 -1
  63. package/dist/tools/bashTools.d.ts +3 -5
  64. package/dist/tools/bashTools.d.ts.map +1 -1
  65. package/dist/tools/bashTools.js +259 -161
  66. package/dist/tools/bashTools.js.map +1 -1
  67. package/dist/tools/tao/index.d.ts +4 -4
  68. package/dist/tools/tao/index.d.ts.map +1 -1
  69. package/dist/tools/tao/index.js +15 -5
  70. package/dist/tools/tao/index.js.map +1 -1
  71. package/dist/tools/tao/rl.d.ts +164 -0
  72. package/dist/tools/tao/rl.d.ts.map +1 -0
  73. package/dist/tools/tao/rl.js +2998 -0
  74. package/dist/tools/tao/rl.js.map +1 -0
  75. package/dist/tools/taoTools.d.ts +2 -2
  76. package/dist/tools/taoTools.d.ts.map +1 -1
  77. package/dist/tools/taoTools.js +103 -20
  78. package/dist/tools/taoTools.js.map +1 -1
  79. package/dist/ui/PromptController.d.ts +3 -0
  80. package/dist/ui/PromptController.d.ts.map +1 -1
  81. package/dist/ui/PromptController.js +3 -0
  82. package/dist/ui/PromptController.js.map +1 -1
  83. package/dist/ui/UnifiedUIRenderer.d.ts +4 -0
  84. package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
  85. package/dist/ui/UnifiedUIRenderer.js +37 -6
  86. package/dist/ui/UnifiedUIRenderer.js.map +1 -1
  87. package/dist/ui/display.d.ts +9 -1
  88. package/dist/ui/display.d.ts.map +1 -1
  89. package/dist/ui/display.js +66 -9
  90. package/dist/ui/display.js.map +1 -1
  91. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  92. package/dist/ui/shortcutsHelp.js +1 -0
  93. package/dist/ui/shortcutsHelp.js.map +1 -1
  94. package/package.json +3 -2
  95. package/dist/capabilities/askUserCapability.d.ts +0 -14
  96. package/dist/capabilities/askUserCapability.d.ts.map +0 -1
  97. package/dist/capabilities/askUserCapability.js +0 -134
  98. package/dist/capabilities/askUserCapability.js.map +0 -1
  99. package/dist/capabilities/codeGenerationCapability.d.ts +0 -13
  100. package/dist/capabilities/codeGenerationCapability.d.ts.map +0 -1
  101. package/dist/capabilities/codeGenerationCapability.js +0 -25
  102. package/dist/capabilities/codeGenerationCapability.js.map +0 -1
  103. package/dist/capabilities/performanceMonitoringCapability.d.ts +0 -108
  104. package/dist/capabilities/performanceMonitoringCapability.d.ts.map +0 -1
  105. package/dist/capabilities/performanceMonitoringCapability.js +0 -176
  106. package/dist/capabilities/performanceMonitoringCapability.js.map +0 -1
  107. package/dist/capabilities/todoCapability.d.ts +0 -19
  108. package/dist/capabilities/todoCapability.d.ts.map +0 -1
  109. package/dist/capabilities/todoCapability.js +0 -170
  110. package/dist/capabilities/todoCapability.js.map +0 -1
  111. package/dist/core/baseToolFactory.d.ts +0 -187
  112. package/dist/core/baseToolFactory.d.ts.map +0 -1
  113. package/dist/core/baseToolFactory.js +0 -352
  114. package/dist/core/baseToolFactory.js.map +0 -1
  115. package/dist/core/intelligentSummarizer.d.ts +0 -79
  116. package/dist/core/intelligentSummarizer.d.ts.map +0 -1
  117. package/dist/core/intelligentSummarizer.js +0 -273
  118. package/dist/core/intelligentSummarizer.js.map +0 -1
  119. package/dist/core/memorySystem.d.ts +0 -67
  120. package/dist/core/memorySystem.d.ts.map +0 -1
  121. package/dist/core/memorySystem.js +0 -334
  122. package/dist/core/memorySystem.js.map +0 -1
  123. package/dist/core/outputStyles.d.ts +0 -48
  124. package/dist/core/outputStyles.d.ts.map +0 -1
  125. package/dist/core/outputStyles.js +0 -270
  126. package/dist/core/outputStyles.js.map +0 -1
  127. package/dist/core/toolPatternAnalyzer.d.ts +0 -87
  128. package/dist/core/toolPatternAnalyzer.d.ts.map +0 -1
  129. package/dist/core/toolPatternAnalyzer.js +0 -272
  130. package/dist/core/toolPatternAnalyzer.js.map +0 -1
  131. package/dist/tools/backgroundBashTools.d.ts +0 -21
  132. package/dist/tools/backgroundBashTools.d.ts.map +0 -1
  133. package/dist/tools/backgroundBashTools.js +0 -215
  134. package/dist/tools/backgroundBashTools.js.map +0 -1
  135. package/dist/tools/code-quality-dashboard.d.ts +0 -57
  136. package/dist/tools/code-quality-dashboard.d.ts.map +0 -1
  137. package/dist/tools/code-quality-dashboard.js +0 -218
  138. package/dist/tools/code-quality-dashboard.js.map +0 -1
  139. package/dist/tools/tao/rlEngine.d.ts +0 -40
  140. package/dist/tools/tao/rlEngine.d.ts.map +0 -1
  141. package/dist/tools/tao/rlEngine.js +0 -237
  142. package/dist/tools/tao/rlEngine.js.map +0 -1
@@ -1,8 +1,6 @@
1
1
  import type { ToolDefinition } from '../core/toolRuntime.js';
2
- export declare function createBashTools(workingDir: string): ToolDefinition[];
3
- interface SandboxEnvOptions {
2
+ export declare function buildSandboxEnv(workingDir: string, options?: {
4
3
  preserveHome?: boolean;
5
- }
6
- export declare function buildSandboxEnv(workingDir: string, options?: SandboxEnvOptions): Promise<NodeJS.ProcessEnv>;
7
- export {};
4
+ }): Promise<NodeJS.ProcessEnv>;
5
+ export declare function createBashTools(workingDir: string): ToolDefinition[];
8
6
  //# sourceMappingURL=bashTools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashTools.d.ts","sourceRoot":"","sources":["../../src/tools/bashTools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAwM7D,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CAwMpE;AAED,UAAU,iBAAiB;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CA6B5B"}
1
+ {"version":3,"file":"bashTools.d.ts","sourceRoot":"","sources":["../../src/tools/bashTools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA8R7D,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAqB5B;AAMD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CAoOpE"}
@@ -2,16 +2,105 @@ import { spawn } from 'node:child_process';
2
2
  import { mkdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { reportToolProgress } from '../core/toolRuntime.js';
5
- import { createBackgroundBashTools, startBackgroundShell } from './backgroundBashTools.js';
6
5
  import { validateBashCommand, SmartFixer } from '../core/errors/safetyValidator.js';
7
6
  import { toStructuredError } from '../core/errors/errorTypes.js';
8
7
  import { analyzeBashFlow } from '../core/bashCommandGuidance.js';
8
+ import { buildError } from '../core/errors.js';
9
9
  import { verifiedSuccess, verifiedFailure, analyzeOutput, OutputPatterns, createCommandCheck, } from '../core/resultVerification.js';
10
10
  import { createErrorFixer } from '../core/aiErrorFixer.js';
11
- /**
12
- * Execute a bash command with streaming output via reportToolProgress.
13
- * This allows the UI to display live output as the command runs.
14
- */
11
+ // ============================================================================
12
+ // Background Shell Manager (consolidated from backgroundBashTools.ts)
13
+ // ============================================================================
14
+ class BackgroundShell {
15
+ id;
16
+ command;
17
+ workingDir;
18
+ process;
19
+ outputBuffer = [];
20
+ errorBuffer = [];
21
+ lastReadPosition = 0;
22
+ isRunning = false;
23
+ exitCode;
24
+ constructor(id, command, workingDir) {
25
+ this.id = id;
26
+ this.command = command;
27
+ this.workingDir = workingDir;
28
+ }
29
+ start() {
30
+ this.process = spawn('bash', ['-c', this.command], {
31
+ cwd: this.workingDir,
32
+ stdio: ['ignore', 'pipe', 'pipe'],
33
+ });
34
+ this.isRunning = true;
35
+ this.process.stdout?.on('data', (data) => {
36
+ this.outputBuffer.push(data.toString());
37
+ });
38
+ this.process.stderr?.on('data', (data) => {
39
+ this.errorBuffer.push(data.toString());
40
+ });
41
+ this.process.on('exit', (code) => {
42
+ this.exitCode = code ?? 0;
43
+ this.isRunning = false;
44
+ });
45
+ }
46
+ getNewOutput(filter) {
47
+ const allOutput = this.outputBuffer.join('');
48
+ const newOutput = allOutput.substring(this.lastReadPosition);
49
+ this.lastReadPosition = allOutput.length;
50
+ const allError = this.errorBuffer.join('');
51
+ let stdout = newOutput;
52
+ if (filter) {
53
+ const lines = newOutput.split('\n');
54
+ stdout = lines.filter(line => filter.test(line)).join('\n');
55
+ }
56
+ return {
57
+ stdout,
58
+ stderr: allError,
59
+ status: this.isRunning ? 'running' : `exited with code ${this.exitCode}`,
60
+ };
61
+ }
62
+ kill() {
63
+ if (this.process) {
64
+ this.process.kill('SIGTERM');
65
+ setTimeout(() => {
66
+ if (this.process && !this.process.killed) {
67
+ this.process.kill('SIGKILL');
68
+ }
69
+ }, 5000);
70
+ }
71
+ }
72
+ }
73
+ class BackgroundShellManager {
74
+ shells = new Map();
75
+ nextId = 1;
76
+ createShell(command, workingDir) {
77
+ const shellId = `shell_${this.nextId++}`;
78
+ const shell = new BackgroundShell(shellId, command, workingDir);
79
+ this.shells.set(shellId, shell);
80
+ shell.start();
81
+ return shellId;
82
+ }
83
+ getShell(shellId) {
84
+ return this.shells.get(shellId);
85
+ }
86
+ killShell(shellId) {
87
+ const shell = this.shells.get(shellId);
88
+ if (shell) {
89
+ shell.kill();
90
+ this.shells.delete(shellId);
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+ listShells() {
96
+ return Array.from(this.shells.keys());
97
+ }
98
+ }
99
+ // Global shell manager instance
100
+ const shellManager = new BackgroundShellManager();
101
+ // ============================================================================
102
+ // Streaming Execution
103
+ // ============================================================================
15
104
  async function execWithStreaming(command, options) {
16
105
  return new Promise((resolve, reject) => {
17
106
  const stdout = [];
@@ -30,8 +119,7 @@ async function execWithStreaming(command, options) {
30
119
  }, options.timeout);
31
120
  const processLine = (line, isStderr) => {
32
121
  lineCount++;
33
- // Report progress with the latest line
34
- const trimmedLine = line.slice(0, 80); // Truncate long lines for display
122
+ const trimmedLine = line.slice(0, 80);
35
123
  reportToolProgress({
36
124
  current: lineCount,
37
125
  message: isStderr ? `stderr: ${trimmedLine}` : trimmedLine,
@@ -42,9 +130,8 @@ async function execWithStreaming(command, options) {
42
130
  const text = data.toString();
43
131
  stdout.push(text);
44
132
  stdoutBuffer += text;
45
- // Process complete lines
46
133
  const lines = stdoutBuffer.split('\n');
47
- stdoutBuffer = lines.pop() || ''; // Keep incomplete line in buffer
134
+ stdoutBuffer = lines.pop() || '';
48
135
  for (const line of lines) {
49
136
  if (line.trim())
50
137
  processLine(line, false);
@@ -55,7 +142,6 @@ async function execWithStreaming(command, options) {
55
142
  const text = data.toString();
56
143
  stderr.push(text);
57
144
  stderrBuffer += text;
58
- // Process complete lines
59
145
  const lines = stderrBuffer.split('\n');
60
146
  stderrBuffer = lines.pop() || '';
61
147
  for (const line of lines) {
@@ -65,7 +151,6 @@ async function execWithStreaming(command, options) {
65
151
  });
66
152
  child.on('close', (code) => {
67
153
  clearTimeout(timeoutId);
68
- // Process any remaining buffered content
69
154
  if (stdoutBuffer.trim())
70
155
  processLine(stdoutBuffer, false);
71
156
  if (stderrBuffer.trim())
@@ -74,11 +159,7 @@ async function execWithStreaming(command, options) {
74
159
  reject({ killed: true, stdout: stdout.join(''), stderr: stderr.join(''), code });
75
160
  }
76
161
  else {
77
- resolve({
78
- stdout: stdout.join(''),
79
- stderr: stderr.join(''),
80
- exitCode: code ?? 0,
81
- });
162
+ resolve({ stdout: stdout.join(''), stderr: stderr.join(''), exitCode: code ?? 0 });
82
163
  }
83
164
  });
84
165
  child.on('error', (error) => {
@@ -87,11 +168,13 @@ async function execWithStreaming(command, options) {
87
168
  });
88
169
  });
89
170
  }
171
+ // ============================================================================
172
+ // Utility Functions
173
+ // ============================================================================
174
+ // Keep the shell responsive while long commands run
90
175
  function findGuiLauncher(_command) {
91
- // Kinetic capabilities enabled - GUI blocking disabled
92
176
  return null;
93
177
  }
94
- // Shared error fixer instance (lazy initialized per working dir)
95
178
  const errorFixerCache = new Map();
96
179
  function getErrorFixer(workingDir) {
97
180
  let fixer = errorFixerCache.get(workingDir);
@@ -101,18 +184,8 @@ function getErrorFixer(workingDir) {
101
184
  }
102
185
  return fixer;
103
186
  }
104
- const sandboxCache = new Map();
105
187
  /**
106
- * Smart timeout detection for bash commands.
107
- * Returns appropriate timeout based on command type.
108
- * - python/torch training & fine-tuning: 10 minutes
109
- * - npm install/ci: 5 minutes
110
- * - npm build/test: 5 minutes
111
- * - npx create-*: 5 minutes
112
- * - pytest/python -m pytest: 5 minutes
113
- * - docker build/pull: 10 minutes
114
- * - git clone: 3 minutes
115
- * - Default: 30 seconds
188
+ * Smart timeout detection based on command type
116
189
  */
117
190
  function getSmartTimeout(command) {
118
191
  const cmd = command.toLowerCase().trim();
@@ -120,96 +193,111 @@ function getSmartTimeout(command) {
120
193
  if (/\b(python|python3)\b/.test(cmd) && /\b(train|fine[-\s]?tune|finetune)\b/.test(cmd)) {
121
194
  return 600000;
122
195
  }
123
- // Torch/accelerate launchers (10 minutes)
124
- if (/\b(torchrun|accelerate\s+launch)\b/.test(cmd)) {
196
+ if (/\b(torchrun|accelerate\s+launch)\b/.test(cmd))
125
197
  return 600000;
126
- }
127
198
  // Python/pytest test runners (5 minutes)
128
- if (/\b(pytest|python\s+-m\s+pytest)\b/.test(cmd)) {
199
+ if (/\b(pytest|python\s+-m\s+pytest)\b/.test(cmd))
129
200
  return 300000;
130
- }
131
- // NPM/Yarn/PNPM package operations (5 minutes)
132
- if (/\b(npm|yarn|pnpm)\s+(install|ci|i|add|update|upgrade)\b/.test(cmd)) {
201
+ // NPM/Yarn/PNPM operations (5 minutes)
202
+ if (/\b(npm|yarn|pnpm)\s+(install|ci|i|add|update|upgrade)\b/.test(cmd))
133
203
  return 300000;
134
- }
135
- // NPM/Yarn build and test operations (5 minutes)
136
- if (/\b(npm|yarn|pnpm)\s+(run\s+)?(build|test|lint|start|dev)\b/.test(cmd)) {
204
+ if (/\b(npm|yarn|pnpm)\s+(run\s+)?(build|test|lint|start|dev)\b/.test(cmd))
137
205
  return 300000;
138
- }
139
- // NPX create commands (5 minutes)
140
- if (/\bnpx\s+(create-|degit|giget)\b/.test(cmd)) {
206
+ if (/\bnpx\s+(create-|degit|giget)\b/.test(cmd))
141
207
  return 300000;
142
- }
143
- // Prisma operations (3 minutes)
144
- if (/\b(prisma|npx prisma)\s+(generate|migrate|db push|db pull)\b/.test(cmd)) {
208
+ // Prisma (3 minutes)
209
+ if (/\b(prisma|npx prisma)\s+(generate|migrate|db push|db pull)\b/.test(cmd))
145
210
  return 180000;
146
- }
147
- // Docker operations (10 minutes)
148
- if (/\bdocker\s+(build|pull|push|compose)\b/.test(cmd)) {
211
+ // Docker (10 minutes)
212
+ if (/\bdocker\s+(build|pull|push|compose)\b/.test(cmd))
149
213
  return 600000;
150
- }
151
- // Git clone (3 minutes)
152
- if (/\bgit\s+clone\b/.test(cmd)) {
214
+ // Git clone / pip install (3 minutes)
215
+ if (/\bgit\s+clone\b/.test(cmd))
153
216
  return 180000;
154
- }
155
- // Pip install (3 minutes)
156
- if (/\bpip\s+install\b/.test(cmd)) {
217
+ if (/\bpip\s+install\b/.test(cmd))
157
218
  return 180000;
219
+ return 30000; // Default
220
+ }
221
+ const sandboxCache = new Map();
222
+ async function ensureSandboxPaths(workingDir) {
223
+ let pending = sandboxCache.get(workingDir);
224
+ if (!pending) {
225
+ pending = createSandboxPaths(workingDir);
226
+ sandboxCache.set(workingDir, pending);
158
227
  }
159
- // Default timeout
160
- return 30000;
228
+ return pending;
161
229
  }
230
+ async function createSandboxPaths(workingDir) {
231
+ const root = join(workingDir, '.erosolar', 'shell-sandbox');
232
+ const home = join(root, 'home');
233
+ const cache = join(root, 'cache');
234
+ const config = join(root, 'config');
235
+ const data = join(root, 'data');
236
+ const tmp = join(root, 'tmp');
237
+ await Promise.all([home, cache, config, data, tmp].map((dir) => mkdir(dir, { recursive: true })));
238
+ return { root, home, cache, config, data, tmp };
239
+ }
240
+ export async function buildSandboxEnv(workingDir, options) {
241
+ const envPreference = process.env['EROSOLAR_PRESERVE_HOME'];
242
+ const preserveHome = envPreference === '1' ? true : envPreference === '0' ? false : Boolean(options?.preserveHome);
243
+ const paths = await ensureSandboxPaths(workingDir);
244
+ const env = {
245
+ ...process.env,
246
+ EROSOLAR_SANDBOX_ROOT: paths.root,
247
+ EROSOLAR_SANDBOX_HOME: paths.home,
248
+ EROSOLAR_SANDBOX_TMP: paths.tmp,
249
+ };
250
+ if (!preserveHome)
251
+ env['HOME'] = paths.home;
252
+ env['XDG_CACHE_HOME'] = paths.cache;
253
+ env['XDG_CONFIG_HOME'] = paths.config;
254
+ env['XDG_DATA_HOME'] = paths.data;
255
+ env['TMPDIR'] = paths.tmp;
256
+ env['TMP'] = paths.tmp;
257
+ env['TEMP'] = paths.tmp;
258
+ return env;
259
+ }
260
+ // ============================================================================
261
+ // Main Tool Factory
262
+ // ============================================================================
162
263
  export function createBashTools(workingDir) {
163
- const backgroundTools = createBackgroundBashTools(workingDir);
164
264
  return [
265
+ // Main bash execution tool
165
266
  {
166
267
  name: 'execute_bash',
167
- description: 'Execute a bash command in the working directory. Commands auto-timeout based on type: npm/docker/git commands get extended timeouts. Exit code 0 = success, non-zero = failure. Use run_in_background: true for long-running servers/watchers. IMPORTANT: Always check command output for errors even if exit code is 0.',
268
+ description: 'Execute a bash command. Commands auto-timeout based on type. Use run_in_background: true for servers/watchers.',
168
269
  parameters: {
169
270
  type: 'object',
170
271
  properties: {
171
- command: {
172
- type: 'string',
173
- description: 'The bash command to execute (e.g., "npm test", "git status", "ls -la"). Will be validated for safety before execution.',
174
- },
175
- timeout: {
176
- type: 'number',
177
- description: 'Timeout in milliseconds. Default is smart: 30s for most commands, 5min for npm/docker, 3min for git clone. Not used when run_in_background is true.',
178
- },
179
- run_in_background: {
180
- type: 'boolean',
181
- description: 'Set to true to run this command in the background (for servers, watchers, long-running processes). Returns a shell ID that can be used with BashOutput to monitor output.',
182
- },
272
+ command: { type: 'string', description: 'The bash command to execute' },
273
+ timeout: { type: 'number', description: 'Timeout in milliseconds (smart defaults apply)' },
274
+ run_in_background: { type: 'boolean', description: 'Run in background for long-running processes' },
183
275
  },
184
276
  required: ['command'],
185
277
  },
186
278
  handler: async (args) => {
187
279
  const command = args['command'];
188
280
  const runInBackground = args['run_in_background'] === true;
189
- // Smart timeout: auto-extend for known long-running commands
190
281
  const userTimeout = args['timeout'];
191
282
  const timeout = userTimeout ?? getSmartTimeout(command);
192
- // Lightweight flow guidance (publish/git) without blocking execution
283
+ // Flow guidance
193
284
  const flowWarnings = analyzeBashFlow(command);
194
285
  for (const warning of flowWarnings) {
195
286
  const suffix = warning.suggestion ? ` — ${warning.suggestion}` : '';
196
287
  console.warn(`[Bash Flow] ${warning.message}${suffix}`);
197
288
  }
198
- // Enhanced safety validation with structured errors
289
+ // Safety validation
199
290
  const validation = validateBashCommand(command);
200
291
  if (!validation.valid) {
201
292
  const structuredError = validation.error ? toStructuredError(validation.error) : null;
202
293
  if (structuredError) {
203
- // Return formatted error with suggestions
204
294
  let errorMsg = structuredError.toDisplayString();
205
- // Add auto-fix suggestion if available
206
295
  if (validation.autoFix?.available) {
207
296
  const { fixed, changes } = SmartFixer.fixDangerousCommand(command);
208
297
  if (changes.length > 0) {
209
298
  errorMsg += '\n\nAuto-fix available:';
210
- for (const change of changes) {
299
+ for (const change of changes)
211
300
  errorMsg += `\n - ${change}`;
212
- }
213
301
  errorMsg += `\n\nFixed command: ${fixed}`;
214
302
  }
215
303
  }
@@ -217,67 +305,50 @@ export function createBashTools(workingDir) {
217
305
  }
218
306
  return 'Error: Command validation failed';
219
307
  }
220
- // Log warnings if any
221
308
  if (validation.warnings.length > 0) {
222
309
  for (const warning of validation.warnings) {
223
310
  console.warn(`[Bash Safety] ${warning}`);
224
311
  }
225
312
  }
226
- // Block GUI/permission pop-up commands by default
313
+ // GUI blocking check (disabled for red team ops)
227
314
  const guiBlocked = findGuiLauncher(command);
228
315
  if (guiBlocked) {
229
- return `Blocked GUI/launcher command "${guiBlocked}". erosolar-cli runs headless and will not launch GUI apps or keychain dialogs.`;
316
+ return `Blocked: ${guiBlocked}`;
230
317
  }
231
- // Handle background execution
318
+ // Background execution
232
319
  if (runInBackground) {
233
- const shellId = startBackgroundShell(command, workingDir);
234
- return `Background shell started: ${shellId}\n\nUse BashOutput with bash_id="${shellId}" to monitor output.\nUse KillShell with shell_id="${shellId}" to terminate.`;
320
+ const shellId = shellManager.createShell(command, workingDir);
321
+ return `Background shell started: ${shellId}\n\nUse BashOutput with bash_id="${shellId}" to monitor.\nUse KillShell with shell_id="${shellId}" to terminate.`;
235
322
  }
236
- // Handle foreground execution with streaming output
323
+ // Foreground execution
237
324
  const startTime = Date.now();
238
325
  try {
239
326
  const env = await buildSandboxEnv(workingDir);
240
- const { stdout, stderr, exitCode } = await execWithStreaming(command, {
241
- cwd: workingDir,
242
- timeout,
243
- env,
244
- });
327
+ const { stdout, stderr, exitCode } = await execWithStreaming(command, { cwd: workingDir, timeout, env });
245
328
  const durationMs = Date.now() - startTime;
246
329
  const combinedOutput = [stdout, stderr].filter(Boolean).join('\n');
247
- // Select appropriate patterns based on command type
248
330
  const commandLower = command.toLowerCase().trim();
249
331
  let patterns = OutputPatterns.command;
250
- if (commandLower.startsWith('git ') || commandLower === 'git') {
332
+ if (commandLower.startsWith('git ') || commandLower === 'git')
251
333
  patterns = OutputPatterns.git;
252
- }
253
- else if (commandLower.startsWith('npm ') || commandLower.startsWith('npx ')) {
334
+ else if (commandLower.startsWith('npm ') || commandLower.startsWith('npx '))
254
335
  patterns = OutputPatterns.npm;
255
- }
256
- // Analyze the output to determine actual success
257
336
  const analysis = analyzeOutput(combinedOutput, patterns, exitCode);
258
337
  const commandCheck = createCommandCheck('Command execution', exitCode, combinedOutput);
259
- // Non-zero exit code = failure
260
338
  if (exitCode !== 0) {
261
- // AI Error Analysis - automatically analyze failures
262
339
  const errorFixer = getErrorFixer(workingDir);
263
340
  const aiErrors = errorFixer.analyzeOutput(combinedOutput, command);
264
341
  const aiGuidance = aiErrors.length > 0 ? errorFixer.formatForAI(aiErrors) : '';
265
- const suggestions = ['Review the error message', 'Fix the issue and retry the command'];
342
+ const suggestions = ['Review the error message', 'Fix the issue and retry'];
266
343
  const firstError = aiErrors[0];
267
- if (firstError && firstError.suggestedFixes.length > 0) {
268
- const bestFix = firstError.suggestedFixes[0];
269
- if (bestFix) {
270
- suggestions.unshift(`AI Suggestion: ${bestFix.description}`);
271
- }
344
+ if (firstError?.suggestedFixes[0]) {
345
+ suggestions.unshift(`AI Suggestion: ${firstError.suggestedFixes[0].description}`);
272
346
  }
273
347
  return verifiedFailure(`Command failed with exit code ${exitCode}`, `Command: ${command}\n\nOutput:\n${combinedOutput || '(none)'}${aiGuidance}`, suggestions, [commandCheck], durationMs);
274
348
  }
275
- // Check for explicit failure patterns in output despite exit code 0
276
- // Some commands exit 0 but print errors (e.g., "error:" in output)
277
349
  if (analysis.isFailure) {
278
- return verifiedFailure(`Command completed with exit code 0 but output indicates failure`, `Command: ${command}\n\nOutput:\n${combinedOutput || '(no output)'}`, ['Review the error message in the output', 'Fix the underlying issue and retry'], [commandCheck, { check: 'Output analysis', passed: false, details: `Failure pattern matched: ${analysis.matchedPattern}` }], durationMs);
350
+ return verifiedFailure(`Command completed with exit code 0 but output indicates failure`, `Command: ${command}\n\nOutput:\n${combinedOutput || '(no output)'}`, ['Review the error message in the output', 'Fix the underlying issue and retry'], [commandCheck, { check: 'Output analysis', passed: false, details: `Failure pattern: ${analysis.matchedPattern}` }], durationMs);
279
351
  }
280
- // Exit code 0 + no failure patterns = success
281
352
  return verifiedSuccess(combinedOutput.trim() ? `Command executed successfully` : `Command executed successfully (no output)`, `Command: ${command}${combinedOutput.trim() ? `\n\nOutput:\n${combinedOutput}` : ''}`, [commandCheck, ...(analysis.isSuccess ? [{ check: 'Output analysis', passed: true, details: `Success pattern matched` }] : [])], durationMs);
282
353
  }
283
354
  catch (error) {
@@ -286,70 +357,97 @@ export function createBashTools(workingDir) {
286
357
  const exitCode = execError.code ?? 1;
287
358
  const combinedError = [execError.stdout, execError.stderr, execError.message].filter(Boolean).join('\n');
288
359
  if (execError.killed) {
289
- return verifiedFailure(`Command timed out after ${timeout}ms`, `Command: ${command}\n\nThe command was killed after exceeding the timeout.\nPartial output:\n${combinedError || '(none)'}`, ['Increase timeout if command legitimately needs more time', 'Check if command is hanging on input'], [{ check: 'Timeout', passed: false, details: `Exceeded ${timeout}ms` }], durationMs);
360
+ return verifiedFailure(`Command timed out after ${timeout}ms`, `Command: ${command}\n\nPartial output:\n${combinedError || '(none)'}`, ['Increase timeout if command legitimately needs more time', 'Check if command is hanging'], [{ check: 'Timeout', passed: false, details: `Exceeded ${timeout}ms` }], durationMs);
290
361
  }
291
- // AI Error Analysis - automatically analyze failures
292
362
  const errorFixer = getErrorFixer(workingDir);
293
363
  const aiErrors = errorFixer.analyzeOutput(combinedError, command);
294
364
  const aiGuidance = aiErrors.length > 0 ? errorFixer.formatForAI(aiErrors) : '';
295
- // Build suggestions with AI help
296
- const suggestions = ['Review the error message', 'Fix the issue and retry the command'];
365
+ const suggestions = ['Review the error message', 'Fix the issue and retry'];
297
366
  const firstError = aiErrors[0];
298
- if (firstError && firstError.suggestedFixes.length > 0) {
299
- const bestFix = firstError.suggestedFixes[0];
300
- if (bestFix) {
301
- suggestions.unshift(`AI Suggestion: ${bestFix.description}`);
302
- }
367
+ if (firstError?.suggestedFixes[0]) {
368
+ suggestions.unshift(`AI Suggestion: ${firstError.suggestedFixes[0].description}`);
303
369
  }
304
370
  return verifiedFailure(`Command failed with exit code ${exitCode}`, `Command: ${command}\n\nError output:\n${combinedError || '(none)'}${aiGuidance}`, suggestions, [createCommandCheck('Command execution', exitCode, combinedError)], durationMs);
305
371
  }
306
372
  },
307
373
  },
308
- ...backgroundTools,
374
+ // Background shell output retrieval
375
+ {
376
+ name: 'BashOutput',
377
+ description: 'Retrieve output from a running or completed background bash shell.',
378
+ parameters: {
379
+ type: 'object',
380
+ properties: {
381
+ bash_id: { type: 'string', description: 'The ID of the background shell' },
382
+ filter: { type: 'string', description: 'Optional regex to filter output lines' },
383
+ },
384
+ required: ['bash_id'],
385
+ additionalProperties: false,
386
+ },
387
+ handler: async (args) => {
388
+ const bashId = args['bash_id'];
389
+ const filterStr = args['filter'];
390
+ if (typeof bashId !== 'string' || !bashId.trim()) {
391
+ return 'Error: bash_id must be a non-empty string.';
392
+ }
393
+ try {
394
+ const shell = shellManager.getShell(bashId);
395
+ if (!shell) {
396
+ const available = shellManager.listShells();
397
+ return `Error: Shell "${bashId}" not found.\n\nAvailable: ${available.length > 0 ? available.join(', ') : 'none'}`;
398
+ }
399
+ const filter = filterStr && typeof filterStr === 'string' ? new RegExp(filterStr) : undefined;
400
+ const { stdout, stderr, status } = shell.getNewOutput(filter);
401
+ const parts = [`Shell: ${bashId}`, `Status: ${status}`];
402
+ if (stdout) {
403
+ parts.push('\n=== New Output ===');
404
+ parts.push(stdout);
405
+ }
406
+ if (stderr) {
407
+ parts.push('\n=== Errors ===');
408
+ parts.push(stderr);
409
+ }
410
+ if (!stdout && !stderr)
411
+ parts.push('\n(No new output)');
412
+ return parts.join('\n');
413
+ }
414
+ catch (error) {
415
+ return buildError('retrieving shell output', error, { bash_id: bashId });
416
+ }
417
+ },
418
+ },
419
+ // Kill background shell
420
+ {
421
+ name: 'KillShell',
422
+ description: 'Kill a running background bash shell by its ID.',
423
+ parameters: {
424
+ type: 'object',
425
+ properties: {
426
+ shell_id: { type: 'string', description: 'The ID of the background shell to kill' },
427
+ },
428
+ required: ['shell_id'],
429
+ additionalProperties: false,
430
+ },
431
+ handler: async (args) => {
432
+ const shellId = args['shell_id'];
433
+ if (typeof shellId !== 'string' || !shellId.trim()) {
434
+ return 'Error: shell_id must be a non-empty string.';
435
+ }
436
+ try {
437
+ const success = shellManager.killShell(shellId);
438
+ if (success) {
439
+ return `Shell "${shellId}" has been terminated.`;
440
+ }
441
+ else {
442
+ const available = shellManager.listShells();
443
+ return `Error: Shell "${shellId}" not found.\n\nAvailable: ${available.length > 0 ? available.join(', ') : 'none'}`;
444
+ }
445
+ }
446
+ catch (error) {
447
+ return buildError('killing shell', error, { shell_id: shellId });
448
+ }
449
+ },
450
+ },
309
451
  ];
310
452
  }
311
- export async function buildSandboxEnv(workingDir, options) {
312
- const envPreference = process.env['EROSOLAR_PRESERVE_HOME'];
313
- const preserveHome = envPreference === '1'
314
- ? true
315
- : envPreference === '0'
316
- ? false
317
- : Boolean(options?.preserveHome);
318
- const paths = await ensureSandboxPaths(workingDir);
319
- const env = {
320
- ...process.env,
321
- EROSOLAR_SANDBOX_ROOT: paths.root,
322
- EROSOLAR_SANDBOX_HOME: paths.home,
323
- EROSOLAR_SANDBOX_TMP: paths.tmp,
324
- };
325
- if (!preserveHome) {
326
- env['HOME'] = paths.home;
327
- }
328
- env['XDG_CACHE_HOME'] = paths.cache;
329
- env['XDG_CONFIG_HOME'] = paths.config;
330
- env['XDG_DATA_HOME'] = paths.data;
331
- env['TMPDIR'] = paths.tmp;
332
- env['TMP'] = paths.tmp;
333
- env['TEMP'] = paths.tmp;
334
- return env;
335
- }
336
- async function ensureSandboxPaths(workingDir) {
337
- const key = workingDir;
338
- let pending = sandboxCache.get(key);
339
- if (!pending) {
340
- pending = createSandboxPaths(workingDir);
341
- sandboxCache.set(key, pending);
342
- }
343
- return pending;
344
- }
345
- async function createSandboxPaths(workingDir) {
346
- const root = join(workingDir, '.erosolar', 'shell-sandbox');
347
- const home = join(root, 'home');
348
- const cache = join(root, 'cache');
349
- const config = join(root, 'config');
350
- const data = join(root, 'data');
351
- const tmp = join(root, 'tmp');
352
- await Promise.all([home, cache, config, data, tmp].map((dir) => mkdir(dir, { recursive: true })));
353
- return { root, home, cache, config, data, tmp };
354
- }
355
453
  //# sourceMappingURL=bashTools.js.map