@vfarcic/dot-ai
Version:
AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance
131 lines (130 loc) • 5.29 kB
JavaScript
;
/**
* Shared Command Executor
*
* Provides common command execution logic for tools (remediate, operate, etc.)
* Uses continue-on-error pattern: executes all commands sequentially regardless
* of individual failures.
*
* PRD #343: Commands are executed through the plugin system via shell_exec tool.
* The plugin container has RBAC permissions; the MCP server does not.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeCommands = executeCommands;
const plugin_registry_1 = require("./plugin-registry");
/**
* Execute a list of commands sequentially with continue-on-error pattern
*
* PRD #343: Commands are executed through the plugin's shell_exec tool.
* PRD #359: Uses unified plugin registry for tool invocation.
* The plugin container has RBAC; commands are executed exactly as provided
* (no parsing or transformation).
*
* @param commands - Array of command strings to execute
* @param logger - Logger instance for tracking execution
* @param options - Optional execution context and metadata
* @returns Array of execution results and overall success status
*/
async function executeCommands(commands, logger, options = {}) {
if (!(0, plugin_registry_1.isPluginInitialized)()) {
throw new Error('Plugin system not initialized');
}
const results = [];
let overallSuccess = true;
const { sessionId, context = 'command execution', logMetadata = {} } = options;
logger.info(`Starting ${context}`, {
...logMetadata,
sessionId,
commandCount: commands.length
});
// Execute each command sequentially
for (let i = 0; i < commands.length; i++) {
const command = commands[i];
const commandNum = i + 1;
try {
logger.debug(`Executing command ${commandNum}/${commands.length}`, {
...logMetadata,
sessionId,
command
});
// Clean up escape sequences that AI models sometimes add
const cleanCommand = command.replace(/\\"/g, '"');
// PRD #359: Execute command via unified plugin registry
const response = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'shell_exec', { command: cleanCommand });
if (response.success) {
// Check for nested error - plugin wraps command errors in { success: false, error: "..." }
if (typeof response.result === 'object' && response.result !== null) {
const result = response.result;
if (result.success === false) {
throw new Error(result.error || result.message || 'Command execution failed');
}
}
// Extract only the data field - never pass JSON wrapper
let output;
if (typeof response.result === 'object' && response.result !== null) {
const result = response.result;
if (result.data !== undefined) {
output = String(result.data);
}
else if (typeof result === 'string') {
output = result;
}
else {
throw new Error('Plugin returned unexpected response format - missing data field');
}
}
else {
output = String(response.result || '');
}
results.push({
command,
success: true,
output: output,
timestamp: new Date()
});
logger.debug(`Command ${commandNum} succeeded`, {
...logMetadata,
sessionId
});
}
else {
const errorMessage = response.error?.message || 'Command execution failed';
overallSuccess = false;
results.push({
command,
success: false,
error: errorMessage,
timestamp: new Date()
});
logger.error(`Command ${commandNum} failed`, new Error(errorMessage), {
...logMetadata,
sessionId,
command
});
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
overallSuccess = false;
results.push({
command,
success: false,
error: errorMessage,
timestamp: new Date()
});
logger.error(`Command ${commandNum} failed`, error, {
...logMetadata,
sessionId,
command
});
// Continue to next command (continue-on-error pattern)
}
}
logger.info(`${context} completed`, {
...logMetadata,
sessionId,
successCount: results.filter(r => r.success).length,
failureCount: results.filter(r => !r.success).length
});
return { results, overallSuccess };
}