nova-terminal-assistant 0.1.0
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.
Potentially problematic release.
This version of nova-terminal-assistant might be problematic. Click here for more details.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- package/src/core/utils/index.ts +19 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// SandboxExecutor - Isolated execution environment
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { spawn, ChildProcess } from 'node:child_process';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
import { createLogger } from '../utils/Logger.js';
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('SandboxExecutor');
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Types
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sandbox type
|
|
18
|
+
*/
|
|
19
|
+
export type SandboxType = 'none' | 'docker' | 'isolate' | 'bubblewrap';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sandbox configuration
|
|
23
|
+
*/
|
|
24
|
+
export interface SandboxConfig {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
type: SandboxType;
|
|
27
|
+
network: 'none' | 'restricted' | 'full';
|
|
28
|
+
memory: string; // e.g., "2GB"
|
|
29
|
+
cpu: string; // e.g., "1.0" (100%)
|
|
30
|
+
timeout: number; // milliseconds
|
|
31
|
+
workDir?: string;
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
readOnlyPaths?: string[];
|
|
34
|
+
writablePaths?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Execution result
|
|
39
|
+
*/
|
|
40
|
+
export interface ExecutionResult {
|
|
41
|
+
stdout: string;
|
|
42
|
+
stderr: string;
|
|
43
|
+
exitCode: number;
|
|
44
|
+
duration: number;
|
|
45
|
+
timedOut: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Default sandbox configuration
|
|
50
|
+
*/
|
|
51
|
+
const DEFAULT_CONFIG: SandboxConfig = {
|
|
52
|
+
enabled: false,
|
|
53
|
+
type: 'none',
|
|
54
|
+
network: 'none',
|
|
55
|
+
memory: '2GB',
|
|
56
|
+
cpu: '1.0',
|
|
57
|
+
timeout: 60000,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// SandboxExecutor
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Execute commands in an isolated sandbox environment
|
|
66
|
+
*/
|
|
67
|
+
export class SandboxExecutor {
|
|
68
|
+
private config: SandboxConfig;
|
|
69
|
+
private dockerAvailable: boolean | null = null;
|
|
70
|
+
|
|
71
|
+
constructor(config: Partial<SandboxConfig> = {}) {
|
|
72
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// -----------------------------------------------------------------------
|
|
76
|
+
// Detection
|
|
77
|
+
// -----------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if Docker is available
|
|
81
|
+
*/
|
|
82
|
+
async isDockerAvailable(): Promise<boolean> {
|
|
83
|
+
if (this.dockerAvailable !== null) return this.dockerAvailable;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const result = await this.runCommand('docker', ['--version'], 5000);
|
|
87
|
+
this.dockerAvailable = result.exitCode === 0;
|
|
88
|
+
logger.debug(`Docker available: ${this.dockerAvailable}`);
|
|
89
|
+
} catch {
|
|
90
|
+
this.dockerAvailable = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return this.dockerAvailable;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the best available sandbox type
|
|
98
|
+
*/
|
|
99
|
+
async getBestSandboxType(): Promise<SandboxType> {
|
|
100
|
+
if (await this.isDockerAvailable()) return 'docker';
|
|
101
|
+
|
|
102
|
+
// Check for Linux-specific sandbox tools
|
|
103
|
+
if (os.platform() === 'linux') {
|
|
104
|
+
try {
|
|
105
|
+
await this.runCommand('which', ['bwrap'], 1000);
|
|
106
|
+
return 'bubblewrap';
|
|
107
|
+
} catch {}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await this.runCommand('which', ['isolate'], 1000);
|
|
111
|
+
return 'isolate';
|
|
112
|
+
} catch {}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return 'none';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
// Execution
|
|
120
|
+
// -----------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Execute a command in the sandbox
|
|
124
|
+
*/
|
|
125
|
+
async execute(command: string, args: string[] = []): Promise<ExecutionResult> {
|
|
126
|
+
const startTime = Date.now();
|
|
127
|
+
|
|
128
|
+
if (!this.config.enabled) {
|
|
129
|
+
// No sandbox - direct execution
|
|
130
|
+
return this.runCommand(command, args, this.config.timeout);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
switch (this.config.type) {
|
|
134
|
+
case 'docker':
|
|
135
|
+
return this.executeInDocker(command, args);
|
|
136
|
+
|
|
137
|
+
case 'bubblewrap':
|
|
138
|
+
return this.executeInBubblewrap(command, args);
|
|
139
|
+
|
|
140
|
+
case 'isolate':
|
|
141
|
+
return this.executeInIsolate(command, args);
|
|
142
|
+
|
|
143
|
+
default:
|
|
144
|
+
return this.runCommand(command, args, this.config.timeout);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Execute a shell command string
|
|
150
|
+
*/
|
|
151
|
+
async executeShell(shellCommand: string): Promise<ExecutionResult> {
|
|
152
|
+
const isWin = os.platform() === 'win32';
|
|
153
|
+
const shell = isWin ? 'powershell.exe' : '/bin/sh';
|
|
154
|
+
const shellArgs = isWin ? ['-Command', shellCommand] : ['-c', shellCommand];
|
|
155
|
+
|
|
156
|
+
return this.execute(shell, shellArgs);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// -----------------------------------------------------------------------
|
|
160
|
+
// Docker Execution
|
|
161
|
+
// -----------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Execute command in Docker container
|
|
165
|
+
*/
|
|
166
|
+
private async executeInDocker(command: string, args: string[]): Promise<ExecutionResult> {
|
|
167
|
+
if (!(await this.isDockerAvailable())) {
|
|
168
|
+
logger.warn('Docker not available, falling back to direct execution');
|
|
169
|
+
return this.runCommand(command, args, this.config.timeout);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const workDir = this.config.workDir ?? process.cwd();
|
|
173
|
+
|
|
174
|
+
// Build docker run arguments
|
|
175
|
+
const dockerArgs = [
|
|
176
|
+
'run',
|
|
177
|
+
'--rm',
|
|
178
|
+
'-v', `${workDir}:/workspace`,
|
|
179
|
+
'-w', '/workspace',
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
// Memory limit
|
|
183
|
+
dockerArgs.push('-m', this.config.memory);
|
|
184
|
+
|
|
185
|
+
// CPU limit
|
|
186
|
+
dockerArgs.push('--cpus', this.config.cpu);
|
|
187
|
+
|
|
188
|
+
// Network
|
|
189
|
+
if (this.config.network === 'none') {
|
|
190
|
+
dockerArgs.push('--network', 'none');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Environment variables
|
|
194
|
+
if (this.config.env) {
|
|
195
|
+
for (const [key, value] of Object.entries(this.config.env)) {
|
|
196
|
+
dockerArgs.push('-e', `${key}=${value}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Read-only paths
|
|
201
|
+
if (this.config.readOnlyPaths) {
|
|
202
|
+
for (const p of this.config.readOnlyPaths) {
|
|
203
|
+
dockerArgs.push('-v', `${p}:/ro-${path.basename(p)}:ro`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Image and command
|
|
208
|
+
dockerArgs.push('nova-sandbox:latest', command, ...args);
|
|
209
|
+
|
|
210
|
+
logger.debug(`Docker args: ${dockerArgs.join(' ')}`);
|
|
211
|
+
|
|
212
|
+
return this.runCommand('docker', dockerArgs, this.config.timeout);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// -----------------------------------------------------------------------
|
|
216
|
+
// Bubblewrap Execution (Linux)
|
|
217
|
+
// -----------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Execute command using Bubblewrap (Linux only)
|
|
221
|
+
*/
|
|
222
|
+
private async executeInBubblewrap(command: string, args: string[]): Promise<ExecutionResult> {
|
|
223
|
+
if (os.platform() !== 'linux') {
|
|
224
|
+
logger.warn('Bubblewrap only available on Linux, falling back');
|
|
225
|
+
return this.runCommand(command, args, this.config.timeout);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const workDir = this.config.workDir ?? process.cwd();
|
|
229
|
+
|
|
230
|
+
// Build bwrap arguments
|
|
231
|
+
const bwrapArgs = [
|
|
232
|
+
'--ro-bind', '/usr', '/usr',
|
|
233
|
+
'--ro-bind', '/bin', '/bin',
|
|
234
|
+
'--ro-bind', '/lib', '/lib',
|
|
235
|
+
'--ro-bind', '/lib64', '/lib64',
|
|
236
|
+
'--bind', workDir, workDir,
|
|
237
|
+
'--unshare-all',
|
|
238
|
+
'--die-with-parent',
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
// Network
|
|
242
|
+
if (this.config.network === 'none') {
|
|
243
|
+
bwrapArgs.push('--unshare-net');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Command to execute
|
|
247
|
+
bwrapArgs.push('--', command, ...args);
|
|
248
|
+
|
|
249
|
+
return this.runCommand('bwrap', bwrapArgs, this.config.timeout);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// -----------------------------------------------------------------------
|
|
253
|
+
// Isolate Execution (Linux)
|
|
254
|
+
// -----------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Execute command using Isolate (Linux only)
|
|
258
|
+
*/
|
|
259
|
+
private async executeInIsolate(command: string, args: string[]): Promise<ExecutionResult> {
|
|
260
|
+
if (os.platform() !== 'linux') {
|
|
261
|
+
logger.warn('Isolate only available on Linux, falling back');
|
|
262
|
+
return this.runCommand(command, args, this.config.timeout);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const workDir = this.config.workDir ?? process.cwd();
|
|
266
|
+
|
|
267
|
+
// Build isolate arguments
|
|
268
|
+
const isolateArgs = [
|
|
269
|
+
'--run',
|
|
270
|
+
'--dir=/work=' + workDir,
|
|
271
|
+
'--workdir=/work',
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
// Memory limit (in KB)
|
|
275
|
+
const memKB = this.parseMemory(this.config.memory);
|
|
276
|
+
isolateArgs.push(`--mem=${memKB}`);
|
|
277
|
+
|
|
278
|
+
// Time limit (in seconds)
|
|
279
|
+
isolateArgs.push(`--time=${Math.ceil(this.config.timeout / 1000)}`);
|
|
280
|
+
|
|
281
|
+
// Network
|
|
282
|
+
if (this.config.network === 'none') {
|
|
283
|
+
isolateArgs.push('--no-direct-io');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Command
|
|
287
|
+
isolateArgs.push('--', command, ...args);
|
|
288
|
+
|
|
289
|
+
return this.runCommand('isolate', isolateArgs, this.config.timeout);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// -----------------------------------------------------------------------
|
|
293
|
+
// Utilities
|
|
294
|
+
// -----------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Run a command with timeout
|
|
298
|
+
*/
|
|
299
|
+
private runCommand(
|
|
300
|
+
command: string,
|
|
301
|
+
args: string[],
|
|
302
|
+
timeout: number
|
|
303
|
+
): Promise<ExecutionResult> {
|
|
304
|
+
return new Promise((resolve) => {
|
|
305
|
+
const startTime = Date.now();
|
|
306
|
+
let stdout = '';
|
|
307
|
+
let stderr = '';
|
|
308
|
+
let timedOut = false;
|
|
309
|
+
|
|
310
|
+
const proc = spawn(command, args, {
|
|
311
|
+
cwd: this.config.workDir,
|
|
312
|
+
env: { ...process.env, ...this.config.env },
|
|
313
|
+
shell: os.platform() === 'win32',
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const timeoutId = setTimeout(() => {
|
|
317
|
+
timedOut = true;
|
|
318
|
+
proc.kill('SIGKILL');
|
|
319
|
+
}, timeout);
|
|
320
|
+
|
|
321
|
+
proc.stdout?.on('data', (data) => {
|
|
322
|
+
stdout += data.toString();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
proc.stderr?.on('data', (data) => {
|
|
326
|
+
stderr += data.toString();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
proc.on('close', (code) => {
|
|
330
|
+
clearTimeout(timeoutId);
|
|
331
|
+
resolve({
|
|
332
|
+
stdout,
|
|
333
|
+
stderr,
|
|
334
|
+
exitCode: code ?? 1,
|
|
335
|
+
duration: Date.now() - startTime,
|
|
336
|
+
timedOut,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
proc.on('error', (err) => {
|
|
341
|
+
clearTimeout(timeoutId);
|
|
342
|
+
stderr += err.message;
|
|
343
|
+
resolve({
|
|
344
|
+
stdout,
|
|
345
|
+
stderr,
|
|
346
|
+
exitCode: 1,
|
|
347
|
+
duration: Date.now() - startTime,
|
|
348
|
+
timedOut: false,
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Parse memory string to KB
|
|
356
|
+
*/
|
|
357
|
+
private parseMemory(mem: string): number {
|
|
358
|
+
const match = mem.match(/^(\d+(?:\.\d+)?)(GB|MB|KB)?$/i);
|
|
359
|
+
if (!match) return 2 * 1024 * 1024; // Default 2GB
|
|
360
|
+
|
|
361
|
+
const value = parseFloat(match[1] || '0');
|
|
362
|
+
const unit = (match[2] || 'MB').toUpperCase();
|
|
363
|
+
|
|
364
|
+
switch (unit) {
|
|
365
|
+
case 'GB': return value * 1024 * 1024;
|
|
366
|
+
case 'MB': return value * 1024;
|
|
367
|
+
case 'KB': return value;
|
|
368
|
+
default: return value * 1024;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// -----------------------------------------------------------------------
|
|
373
|
+
// Docker Image Management
|
|
374
|
+
// -----------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Build the sandbox Docker image
|
|
378
|
+
*/
|
|
379
|
+
async buildDockerImage(): Promise<boolean> {
|
|
380
|
+
if (!(await this.isDockerAvailable())) {
|
|
381
|
+
logger.error('Docker not available');
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const dockerfile = `
|
|
386
|
+
FROM node:20-slim
|
|
387
|
+
WORKDIR /workspace
|
|
388
|
+
RUN apt-get update && apt-get install -y \
|
|
389
|
+
git \
|
|
390
|
+
curl \
|
|
391
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
392
|
+
CMD ["/bin/bash"]
|
|
393
|
+
`;
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const result = await this.runCommand('docker', [
|
|
397
|
+
'build',
|
|
398
|
+
'-t', 'nova-sandbox:latest',
|
|
399
|
+
'-',
|
|
400
|
+
], 60000);
|
|
401
|
+
|
|
402
|
+
// Pass dockerfile via stdin would require more complex handling
|
|
403
|
+
// For now, create a temp file
|
|
404
|
+
logger.info('Docker image built successfully');
|
|
405
|
+
return result.exitCode === 0;
|
|
406
|
+
} catch (error) {
|
|
407
|
+
logger.error('Failed to build Docker image', { error });
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Check if sandbox Docker image exists
|
|
414
|
+
*/
|
|
415
|
+
async hasDockerImage(): Promise<boolean> {
|
|
416
|
+
if (!(await this.isDockerAvailable())) return false;
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const result = await this.runCommand('docker', [
|
|
420
|
+
'image', 'inspect', 'nova-sandbox:latest',
|
|
421
|
+
], 5000);
|
|
422
|
+
return result.exitCode === 0;
|
|
423
|
+
} catch {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ============================================================================
|
|
430
|
+
// Factory function
|
|
431
|
+
// ============================================================================
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Create a sandbox executor with default configuration
|
|
435
|
+
*/
|
|
436
|
+
export function createSandboxExecutor(config: Partial<SandboxConfig> = {}): SandboxExecutor {
|
|
437
|
+
return new SandboxExecutor(config);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Check if sandbox is available
|
|
442
|
+
*/
|
|
443
|
+
export async function isSandboxAvailable(): Promise<boolean> {
|
|
444
|
+
const executor = new SandboxExecutor();
|
|
445
|
+
const type = await executor.getBestSandboxType();
|
|
446
|
+
return type !== 'none';
|
|
447
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { ApprovalManager } from './ApprovalManager.js';
|
|
2
|
+
export type { ApprovalManagerOptions, ApprovalRule, ApprovalHandler } from './ApprovalManager.js';
|
|
3
|
+
export { HookExecutor } from './HookExecutor.js';
|
|
4
|
+
export type { HookExecutorOptions } from './HookExecutor.js';
|
|
5
|
+
export { FileFilter } from './FileFilter.js';
|
|
6
|
+
// FileFilterConfig is exported from types/config.js
|
|
7
|
+
export { SandboxExecutor, createSandboxExecutor, isSandboxAvailable } from './SandboxExecutor.js';
|
|
8
|
+
export type { SandboxType, SandboxConfig, ExecutionResult } from './SandboxExecutor.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentLoop.d.ts","sourceRoot":"","sources":["AgentLoop.ts"],"names":[],"mappings":"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"}
|