sandboxbox 3.0.15 ā 3.0.17
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/package.json +1 -1
- package/utils/commands/claude.js +320 -49
- package/utils/sandbox.js +9 -0
- package/utils/ui.js +6 -3
package/package.json
CHANGED
package/utils/commands/claude.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
1
|
+
import { existsSync, writeFileSync, appendFileSync } from 'fs';
|
|
2
2
|
import { resolve, join } from 'path';
|
|
3
3
|
import { spawn, execSync } from 'child_process';
|
|
4
4
|
import { color } from '../colors.js';
|
|
@@ -17,6 +17,216 @@ const ALLOWED_TOOLS = [
|
|
|
17
17
|
'mcp__vexify__search_code'
|
|
18
18
|
];
|
|
19
19
|
|
|
20
|
+
// Console output configuration
|
|
21
|
+
const MAX_CONSOLE_LINES = parseInt(process.env.SANDBOX_MAX_CONSOLE_LINES) || 5;
|
|
22
|
+
const MAX_LOG_ENTRY_LENGTH = parseInt(process.env.SANDBOX_MAX_LOG_LENGTH) || 200;
|
|
23
|
+
const ENABLE_FILE_LOGGING = process.env.SANDBOX_ENABLE_FILE_LOGGING === 'true';
|
|
24
|
+
const VERBOSE_OUTPUT = process.env.SANDBOX_VERBOSE === 'true' || process.argv.includes('--verbose');
|
|
25
|
+
global.toolCallLog = [];
|
|
26
|
+
global.logFileHandle = null;
|
|
27
|
+
global.pendingToolCalls = new Map(); // Track tool calls by ID for result matching
|
|
28
|
+
global.conversationalBuffer = ''; // Track conversational text between tool calls
|
|
29
|
+
|
|
30
|
+
// Helper function to extract tool metadata without showing actual content
|
|
31
|
+
function extractToolMetadata(toolUse) {
|
|
32
|
+
const metadata = {
|
|
33
|
+
name: toolUse.name || 'unknown',
|
|
34
|
+
id: toolUse.id || 'no-id',
|
|
35
|
+
inputCount: 0,
|
|
36
|
+
inputTypes: {},
|
|
37
|
+
inputSizes: {},
|
|
38
|
+
totalInputSize: 0
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (toolUse.input && typeof toolUse.input === 'object') {
|
|
42
|
+
metadata.inputCount = Object.keys(toolUse.input).length;
|
|
43
|
+
|
|
44
|
+
for (const [key, value] of Object.entries(toolUse.input)) {
|
|
45
|
+
const type = Array.isArray(value) ? 'array' : typeof value;
|
|
46
|
+
metadata.inputTypes[key] = type;
|
|
47
|
+
|
|
48
|
+
// Calculate sizes without exposing content
|
|
49
|
+
if (type === 'string') {
|
|
50
|
+
metadata.inputSizes[key] = `${value.length} chars`;
|
|
51
|
+
metadata.totalInputSize += value.length;
|
|
52
|
+
} else if (type === 'array') {
|
|
53
|
+
metadata.inputSizes[key] = `${value.length} items`;
|
|
54
|
+
metadata.totalInputSize += JSON.stringify(value).length;
|
|
55
|
+
} else if (type === 'object' && value !== null) {
|
|
56
|
+
const objSize = JSON.stringify(value).length;
|
|
57
|
+
metadata.inputSizes[key] = `${objSize} chars`;
|
|
58
|
+
metadata.totalInputSize += objSize;
|
|
59
|
+
} else {
|
|
60
|
+
metadata.inputSizes[key] = `${type}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return metadata;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Helper function to extract tool result metadata
|
|
69
|
+
function extractResultMetadata(result) {
|
|
70
|
+
const metadata = {
|
|
71
|
+
type: 'unknown',
|
|
72
|
+
size: 0,
|
|
73
|
+
hasContent: false,
|
|
74
|
+
isToolResult: false,
|
|
75
|
+
isError: false
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (result && typeof result === 'object') {
|
|
79
|
+
metadata.isToolResult = result.type === 'tool_result';
|
|
80
|
+
metadata.isError = result.is_error || false;
|
|
81
|
+
|
|
82
|
+
if (result.content) {
|
|
83
|
+
metadata.hasContent = true;
|
|
84
|
+
|
|
85
|
+
if (typeof result.content === 'string') {
|
|
86
|
+
metadata.type = 'text';
|
|
87
|
+
metadata.size = result.content.length;
|
|
88
|
+
} else if (Array.isArray(result.content)) {
|
|
89
|
+
metadata.type = 'array';
|
|
90
|
+
metadata.size = result.content.length;
|
|
91
|
+
// Count items by type without showing content
|
|
92
|
+
const typeCounts = {};
|
|
93
|
+
result.content.forEach(item => {
|
|
94
|
+
const itemType = item?.type || 'unknown';
|
|
95
|
+
typeCounts[itemType] = (typeCounts[itemType] || 0) + 1;
|
|
96
|
+
});
|
|
97
|
+
metadata.itemTypes = typeCounts;
|
|
98
|
+
} else if (typeof result.content === 'object') {
|
|
99
|
+
metadata.type = 'object';
|
|
100
|
+
metadata.size = Object.keys(result.content).length;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return metadata;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
// Helper function to truncate text to a sensible length
|
|
110
|
+
function truncateText(text, maxLength = MAX_LOG_ENTRY_LENGTH) {
|
|
111
|
+
if (text.length <= maxLength) return text;
|
|
112
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Enhanced tool logging with detailed metadata and length limiting
|
|
116
|
+
function logToolCall(toolName, action = 'call', toolUse = null, result = null) {
|
|
117
|
+
const shortTime = new Date().toLocaleTimeString();
|
|
118
|
+
let logEntry = `Tool: ${toolName}`;
|
|
119
|
+
|
|
120
|
+
// Add conversational text if available
|
|
121
|
+
if (global.conversationalBuffer.trim()) {
|
|
122
|
+
const truncatedText = truncateText(global.conversationalBuffer.trim(), 80);
|
|
123
|
+
logEntry += ` - "${truncatedText}"`;
|
|
124
|
+
global.conversationalBuffer = ''; // Clear buffer after using
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add compact metadata
|
|
128
|
+
if (toolUse && action === 'call') {
|
|
129
|
+
const metadata = extractToolMetadata(toolUse);
|
|
130
|
+
const metaInfo = [];
|
|
131
|
+
|
|
132
|
+
if (metadata.inputCount > 0) {
|
|
133
|
+
metaInfo.push(`${metadata.inputCount} inputs`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (metadata.totalInputSize > 0) {
|
|
137
|
+
metaInfo.push(`${metadata.totalInputSize} chars`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (metaInfo.length > 0) {
|
|
141
|
+
logEntry += ` (${metaInfo.join(', ')})`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add result metadata
|
|
146
|
+
if (result && action === 'result') {
|
|
147
|
+
const metadata = extractResultMetadata(result);
|
|
148
|
+
const resultInfo = [];
|
|
149
|
+
|
|
150
|
+
if (metadata.hasContent) {
|
|
151
|
+
if (metadata.size > 0) {
|
|
152
|
+
if (metadata.type === 'text') {
|
|
153
|
+
resultInfo.push(`${metadata.size} chars`);
|
|
154
|
+
} else if (metadata.type === 'array') {
|
|
155
|
+
resultInfo.push(`${metadata.size} items`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (metadata.isError) {
|
|
160
|
+
resultInfo.push('ERROR');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (resultInfo.length > 0) {
|
|
165
|
+
logEntry += ` ā ${resultInfo.join(', ')}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const finalLogEntry = `[${shortTime}] ${truncateText(logEntry)}`;
|
|
170
|
+
|
|
171
|
+
global.toolCallLog.push(finalLogEntry);
|
|
172
|
+
|
|
173
|
+
// Keep only the last MAX_CONSOLE_LINES for console display
|
|
174
|
+
if (global.toolCallLog.length > MAX_CONSOLE_LINES) {
|
|
175
|
+
global.toolCallLog = global.toolCallLog.slice(-MAX_CONSOLE_LINES);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Optionally log to file with full metadata
|
|
179
|
+
if (ENABLE_FILE_LOGGING) {
|
|
180
|
+
try {
|
|
181
|
+
if (!global.logFileHandle) {
|
|
182
|
+
const timestamp = new Date().toISOString();
|
|
183
|
+
const logFileName = `sandboxbox-tool-calls-${Date.now()}.log`;
|
|
184
|
+
writeFileSync(logFileName, `# SandboxBox Tool Calls Log\n# Started: ${timestamp}\n# Format: [timestamp] Tool: name - "text" (metadata)\n\n`);
|
|
185
|
+
global.logFileHandle = logFileName;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const timestamp = new Date().toISOString();
|
|
189
|
+
let fileLogEntry = `[${timestamp}] Tool: ${toolName}`;
|
|
190
|
+
|
|
191
|
+
if (global.conversationalBuffer.trim()) {
|
|
192
|
+
fileLogEntry += ` - "${global.conversationalBuffer.trim()}"`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (toolUse && action === 'call') {
|
|
196
|
+
const metadata = extractToolMetadata(toolUse);
|
|
197
|
+
fileLogEntry += `\n Input details: ${JSON.stringify(metadata.inputSizes, null, 2)}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (result && action === 'result') {
|
|
201
|
+
const metadata = extractResultMetadata(result);
|
|
202
|
+
fileLogEntry += `\n Result details: ${JSON.stringify(metadata, null, 2)}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
appendFileSync(global.logFileHandle, fileLogEntry + '\n');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
// Don't fail if logging fails
|
|
208
|
+
console.log(color('yellow', `ā ļø Could not write to log file: ${error.message}`));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Function to log conversational text
|
|
214
|
+
function logConversationalText(text) {
|
|
215
|
+
if (text && text.trim()) {
|
|
216
|
+
global.conversationalBuffer += text + ' ';
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Helper function to display recent tool calls
|
|
221
|
+
function displayRecentToolCalls() {
|
|
222
|
+
if (global.toolCallLog.length > 0) {
|
|
223
|
+
console.log(color('cyan', `\nš§ Recent tool calls (showing last ${global.toolCallLog.length}):`));
|
|
224
|
+
global.toolCallLog.forEach(log => {
|
|
225
|
+
console.log(color('cyan', ` ${log}`));
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
20
230
|
|
|
21
231
|
export async function claudeCommand(projectDir, prompt) {
|
|
22
232
|
if (!existsSync(projectDir)) {
|
|
@@ -32,18 +242,18 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
32
242
|
console.log('');
|
|
33
243
|
|
|
34
244
|
const startTime = Date.now();
|
|
35
|
-
console.log(color('cyan', 'ā±ļø Stage 1: Creating sandbox...'));
|
|
245
|
+
if (VERBOSE_OUTPUT) console.log(color('cyan', 'ā±ļø Stage 1: Creating sandbox...'));
|
|
36
246
|
|
|
37
247
|
const { sandboxDir, cleanup } = createSandbox(projectDir);
|
|
38
248
|
const sandboxCreateTime = Date.now() - startTime;
|
|
39
|
-
console.log(color('green', `ā
Sandbox created in ${sandboxCreateTime}ms`));
|
|
249
|
+
if (VERBOSE_OUTPUT) console.log(color('green', `ā
Sandbox created in ${sandboxCreateTime}ms`));
|
|
40
250
|
|
|
41
251
|
process.on('SIGINT', cleanup);
|
|
42
252
|
process.on('SIGTERM', cleanup);
|
|
43
253
|
|
|
44
254
|
try {
|
|
45
255
|
const envStartTime = Date.now();
|
|
46
|
-
console.log(color('cyan', 'ā±ļø Stage 2: Setting up environment...'));
|
|
256
|
+
if (VERBOSE_OUTPUT) console.log(color('cyan', 'ā±ļø Stage 2: Setting up environment...'));
|
|
47
257
|
|
|
48
258
|
// Apply Claude optimizations
|
|
49
259
|
const claudeOptimizer = new ClaudeOptimizer(sandboxDir);
|
|
@@ -64,13 +274,13 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
64
274
|
: claudeOptimizer.createOptimizedEnv(baseEnv);
|
|
65
275
|
|
|
66
276
|
const envCreateTime = Date.now() - envStartTime;
|
|
67
|
-
console.log(color('green', `ā
Environment configured in ${envCreateTime}ms`));
|
|
277
|
+
if (VERBOSE_OUTPUT) console.log(color('green', `ā
Environment configured in ${envCreateTime}ms`));
|
|
68
278
|
|
|
69
|
-
if (systemOptimizationsApplied) {
|
|
279
|
+
if (systemOptimizationsApplied && VERBOSE_OUTPUT) {
|
|
70
280
|
console.log(color('yellow', `š System-level optimizations applied`));
|
|
71
281
|
}
|
|
72
282
|
|
|
73
|
-
console.log(color('cyan', `š¦ Using host Claude settings with all available tools\n`));
|
|
283
|
+
if (VERBOSE_OUTPUT) console.log(color('cyan', `š¦ Using host Claude settings with all available tools\n`));
|
|
74
284
|
|
|
75
285
|
const claudeArgs = [
|
|
76
286
|
'--verbose',
|
|
@@ -79,7 +289,7 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
79
289
|
'--allowed-tools', ALLOWED_TOOLS.join(',')
|
|
80
290
|
];
|
|
81
291
|
|
|
82
|
-
console.log(color('blue', `š Running Claude Code with host settings\n`));
|
|
292
|
+
if (VERBOSE_OUTPUT) console.log(color('blue', `š Running Claude Code with host settings\n`));
|
|
83
293
|
|
|
84
294
|
return new Promise((resolve, reject) => {
|
|
85
295
|
const claudeStartTime = Date.now();
|
|
@@ -99,56 +309,98 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
99
309
|
});
|
|
100
310
|
|
|
101
311
|
let claudeStarted = false;
|
|
312
|
+
let jsonBuffer = ''; // Buffer for incomplete JSON lines
|
|
313
|
+
|
|
314
|
+
function handleEvent(event) {
|
|
315
|
+
if (event.type === 'system' && event.subtype === 'init') {
|
|
316
|
+
if (!claudeStarted) {
|
|
317
|
+
const claudeCreateTime = Date.now() - claudeStartTime;
|
|
318
|
+
console.log(color('green', `ā
Claude Code started in ${claudeCreateTime}ms`));
|
|
319
|
+
claudeStarted = true;
|
|
320
|
+
}
|
|
321
|
+
console.log(color('green', `ā
Session started (${event.session_id.substring(0, 8)}...)`));
|
|
322
|
+
console.log(color('cyan', `š¦ Model: ${event.model}`));
|
|
323
|
+
console.log(color('cyan', `š§ Tools: ${event.tools.length} available`));
|
|
324
|
+
|
|
325
|
+
// List available tools
|
|
326
|
+
if (event.tools && event.tools.length > 0) {
|
|
327
|
+
const toolNames = event.tools.map(tool => tool.name || tool).sort();
|
|
328
|
+
console.log(color('yellow', ` Available: ${toolNames.join(', ')}\n`));
|
|
329
|
+
} else {
|
|
330
|
+
console.log('');
|
|
331
|
+
}
|
|
332
|
+
} else if (event.type === 'assistant' && event.message) {
|
|
333
|
+
const content = event.message.content;
|
|
334
|
+
if (Array.isArray(content)) {
|
|
335
|
+
for (const block of content) {
|
|
336
|
+
if (block.type === 'text') {
|
|
337
|
+
// Capture conversational text and display if verbose
|
|
338
|
+
if (VERBOSE_OUTPUT) {
|
|
339
|
+
process.stdout.write(block.text);
|
|
340
|
+
} else {
|
|
341
|
+
logConversationalText(block.text);
|
|
342
|
+
}
|
|
343
|
+
} else if (block.type === 'tool_use') {
|
|
344
|
+
// Track the tool call for later result matching
|
|
345
|
+
if (block.id) {
|
|
346
|
+
global.pendingToolCalls.set(block.id, block.name);
|
|
347
|
+
}
|
|
348
|
+
logToolCall(block.name, 'call', block);
|
|
349
|
+
if (VERBOSE_OUTPUT) {
|
|
350
|
+
console.log(color('cyan', `\nš§ Using tool: ${block.name}`));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} else if (event.type === 'user' && event.message) {
|
|
356
|
+
const content = event.message.content;
|
|
357
|
+
if (Array.isArray(content)) {
|
|
358
|
+
for (const block of content) {
|
|
359
|
+
if (block.type === 'tool_result' && block.tool_use_id) {
|
|
360
|
+
// Match the result with the original tool call
|
|
361
|
+
const toolUseId = block.tool_use_id;
|
|
362
|
+
const toolName = global.pendingToolCalls.get(toolUseId) || `unknown_tool_${toolUseId}`;
|
|
363
|
+
|
|
364
|
+
logToolCall(toolName, 'result', null, block);
|
|
365
|
+
|
|
366
|
+
// Remove from pending calls after matching
|
|
367
|
+
global.pendingToolCalls.delete(toolUseId);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
} else if (event.type === 'result') {
|
|
372
|
+
const usage = event.usage || {};
|
|
373
|
+
const cost = event.total_cost_usd || 0;
|
|
374
|
+
console.log(color('green', `\n\nā
Completed in ${event.duration_ms}ms`));
|
|
375
|
+
console.log(color('yellow', `š° Cost: $${cost.toFixed(4)}`));
|
|
376
|
+
if (usage.input_tokens) {
|
|
377
|
+
console.log(color('cyan', `š Tokens: ${usage.input_tokens} in, ${usage.output_tokens} out`));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
102
381
|
|
|
103
382
|
function handleStreamingOutput(data) {
|
|
104
|
-
|
|
383
|
+
jsonBuffer += data.toString();
|
|
384
|
+
|
|
385
|
+
// Split by newlines but keep the last incomplete line in buffer
|
|
386
|
+
const lines = jsonBuffer.split('\n');
|
|
387
|
+
jsonBuffer = lines.pop() || ''; // Keep last line (might be incomplete)
|
|
105
388
|
|
|
106
389
|
for (const line of lines) {
|
|
390
|
+
if (!line.trim()) continue; // Skip empty lines
|
|
391
|
+
|
|
107
392
|
try {
|
|
108
393
|
const event = JSON.parse(line);
|
|
109
|
-
|
|
110
|
-
if (event.type === 'system' && event.subtype === 'init') {
|
|
111
|
-
if (!claudeStarted) {
|
|
112
|
-
const claudeCreateTime = Date.now() - claudeStartTime;
|
|
113
|
-
console.log(color('green', `ā
Claude Code started in ${claudeCreateTime}ms`));
|
|
114
|
-
claudeStarted = true;
|
|
115
|
-
}
|
|
116
|
-
console.log(color('green', `ā
Session started (${event.session_id.substring(0, 8)}...)`));
|
|
117
|
-
console.log(color('cyan', `š¦ Model: ${event.model}`));
|
|
118
|
-
console.log(color('cyan', `š§ Tools: ${event.tools.length} available`));
|
|
119
|
-
|
|
120
|
-
// List available tools
|
|
121
|
-
if (event.tools && event.tools.length > 0) {
|
|
122
|
-
const toolNames = event.tools.map(tool => tool.name || tool).sort();
|
|
123
|
-
console.log(color('yellow', ` Available: ${toolNames.join(', ')}\n`));
|
|
124
|
-
} else {
|
|
125
|
-
console.log('');
|
|
126
|
-
}
|
|
127
|
-
} else if (event.type === 'assistant' && event.message) {
|
|
128
|
-
const content = event.message.content;
|
|
129
|
-
if (Array.isArray(content)) {
|
|
130
|
-
for (const block of content) {
|
|
131
|
-
if (block.type === 'text') {
|
|
132
|
-
process.stdout.write(block.text);
|
|
133
|
-
} else if (block.type === 'tool_use') {
|
|
134
|
-
console.log(color('cyan', `\nš§ Using tool: ${block.name}`));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} else if (event.type === 'result') {
|
|
139
|
-
const usage = event.usage || {};
|
|
140
|
-
const cost = event.total_cost_usd || 0;
|
|
141
|
-
console.log(color('green', `\n\nā
Completed in ${event.duration_ms}ms`));
|
|
142
|
-
console.log(color('yellow', `š° Cost: $${cost.toFixed(4)}`));
|
|
143
|
-
if (usage.input_tokens) {
|
|
144
|
-
console.log(color('cyan', `š Tokens: ${usage.input_tokens} in, ${usage.output_tokens} out`));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
394
|
+
handleEvent(event);
|
|
147
395
|
} catch (jsonError) {
|
|
148
396
|
// Log JSON parsing errors for troubleshooting
|
|
149
397
|
console.log(color('red', `š JSON parse error: ${jsonError.message}`));
|
|
150
|
-
console.log(color('yellow', `š Problematic line: ${line}`));
|
|
151
|
-
console.log(color('cyan',
|
|
398
|
+
console.log(color('yellow', `š Problematic line (${line.length} chars): ${line.substring(0, 200)}${line.length > 200 ? '...' : ''}`));
|
|
399
|
+
console.log(color('cyan', `š Buffer state: ${jsonBuffer.length} chars in buffer`));
|
|
400
|
+
|
|
401
|
+
// If we can't parse, put the line back in buffer and try to recover
|
|
402
|
+
jsonBuffer = line + '\n' + jsonBuffer;
|
|
403
|
+
console.log(color('yellow', `š Attempting to recover - ${jsonBuffer.length} chars in buffer`));
|
|
152
404
|
}
|
|
153
405
|
}
|
|
154
406
|
}
|
|
@@ -183,6 +435,20 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
183
435
|
const totalTime = sessionEndTime - startTime;
|
|
184
436
|
console.log(color('cyan', `\nā±ļø Stage 4: Session completed in ${totalTime}ms`));
|
|
185
437
|
|
|
438
|
+
// Try to parse any remaining data in buffer
|
|
439
|
+
if (jsonBuffer.trim()) {
|
|
440
|
+
try {
|
|
441
|
+
const event = JSON.parse(jsonBuffer);
|
|
442
|
+
handleEvent(event);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.log(color('yellow', `ā ļø Could not parse remaining buffer data: ${error.message}`));
|
|
445
|
+
console.log(color('yellow', `ā ļø Remaining buffer: ${jsonBuffer.substring(0, 100)}...`));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Display recent tool calls
|
|
450
|
+
displayRecentToolCalls();
|
|
451
|
+
|
|
186
452
|
// Performance summary
|
|
187
453
|
console.log(color('cyan', `\nš Performance Summary:`));
|
|
188
454
|
console.log(color('cyan', ` ⢠Sandbox creation: ${sandboxCreateTime}ms`));
|
|
@@ -190,6 +456,11 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
190
456
|
console.log(color('cyan', ` ⢠Claude Code session: ${totalTime - sandboxCreateTime - envCreateTime}ms`));
|
|
191
457
|
console.log(color('cyan', ` ⢠Total time: ${totalTime}ms`));
|
|
192
458
|
|
|
459
|
+
// Log file information if enabled
|
|
460
|
+
if (ENABLE_FILE_LOGGING && global.logFileHandle) {
|
|
461
|
+
console.log(color('yellow', `š Tool calls logged to: ${global.logFileHandle}`));
|
|
462
|
+
}
|
|
463
|
+
|
|
193
464
|
cleanup();
|
|
194
465
|
resolve(true);
|
|
195
466
|
});
|
package/utils/sandbox.js
CHANGED
|
@@ -190,6 +190,15 @@ export function createSandbox(projectDir) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
const cleanup = () => {
|
|
193
|
+
// Close any log files that might be open
|
|
194
|
+
if (global.logFileHandle) {
|
|
195
|
+
try {
|
|
196
|
+
appendFileSync(global.logFileHandle, `\n# Session ended: ${new Date().toISOString()}\n`);
|
|
197
|
+
global.logFileHandle = null;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
// Don't fail on log cleanup
|
|
200
|
+
}
|
|
201
|
+
}
|
|
193
202
|
rmSync(sandboxDir, { recursive: true, force: true });
|
|
194
203
|
};
|
|
195
204
|
|
package/utils/ui.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { color } from './colors.js';
|
|
2
2
|
|
|
3
3
|
export function showBanner() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const VERBOSE_OUTPUT = process.env.SANDBOX_VERBOSE === 'true' || process.argv.includes('--verbose');
|
|
5
|
+
if (VERBOSE_OUTPUT) {
|
|
6
|
+
console.log(color('cyan', 'š¦ SandboxBox - Portable Container Runner'));
|
|
7
|
+
console.log(color('cyan', 'āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
8
|
+
console.log('');
|
|
9
|
+
}
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function showHelp() {
|