sandboxbox 3.0.16 → 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 +211 -18
- package/utils/ui.js +6 -3
package/package.json
CHANGED
package/utils/commands/claude.js
CHANGED
|
@@ -19,31 +19,190 @@ const ALLOWED_TOOLS = [
|
|
|
19
19
|
|
|
20
20
|
// Console output configuration
|
|
21
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;
|
|
22
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');
|
|
23
25
|
global.toolCallLog = [];
|
|
24
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
|
+
}
|
|
25
104
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const timestamp = new Date().toISOString();
|
|
29
|
-
const logEntry = `[${timestamp}] Tool ${action}: ${toolName}`;
|
|
105
|
+
return metadata;
|
|
106
|
+
}
|
|
30
107
|
|
|
31
|
-
|
|
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);
|
|
32
172
|
|
|
33
173
|
// Keep only the last MAX_CONSOLE_LINES for console display
|
|
34
174
|
if (global.toolCallLog.length > MAX_CONSOLE_LINES) {
|
|
35
175
|
global.toolCallLog = global.toolCallLog.slice(-MAX_CONSOLE_LINES);
|
|
36
176
|
}
|
|
37
177
|
|
|
38
|
-
// Optionally log to file
|
|
178
|
+
// Optionally log to file with full metadata
|
|
39
179
|
if (ENABLE_FILE_LOGGING) {
|
|
40
180
|
try {
|
|
41
181
|
if (!global.logFileHandle) {
|
|
182
|
+
const timestamp = new Date().toISOString();
|
|
42
183
|
const logFileName = `sandboxbox-tool-calls-${Date.now()}.log`;
|
|
43
|
-
writeFileSync(logFileName, `# SandboxBox Tool Calls Log\n# Started: ${timestamp}\n\n`);
|
|
184
|
+
writeFileSync(logFileName, `# SandboxBox Tool Calls Log\n# Started: ${timestamp}\n# Format: [timestamp] Tool: name - "text" (metadata)\n\n`);
|
|
44
185
|
global.logFileHandle = logFileName;
|
|
45
186
|
}
|
|
46
|
-
|
|
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');
|
|
47
206
|
} catch (error) {
|
|
48
207
|
// Don't fail if logging fails
|
|
49
208
|
console.log(color('yellow', `⚠️ Could not write to log file: ${error.message}`));
|
|
@@ -51,6 +210,13 @@ function logToolCall(toolName, action = 'call') {
|
|
|
51
210
|
}
|
|
52
211
|
}
|
|
53
212
|
|
|
213
|
+
// Function to log conversational text
|
|
214
|
+
function logConversationalText(text) {
|
|
215
|
+
if (text && text.trim()) {
|
|
216
|
+
global.conversationalBuffer += text + ' ';
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
54
220
|
// Helper function to display recent tool calls
|
|
55
221
|
function displayRecentToolCalls() {
|
|
56
222
|
if (global.toolCallLog.length > 0) {
|
|
@@ -76,18 +242,18 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
76
242
|
console.log('');
|
|
77
243
|
|
|
78
244
|
const startTime = Date.now();
|
|
79
|
-
console.log(color('cyan', '⏱️ Stage 1: Creating sandbox...'));
|
|
245
|
+
if (VERBOSE_OUTPUT) console.log(color('cyan', '⏱️ Stage 1: Creating sandbox...'));
|
|
80
246
|
|
|
81
247
|
const { sandboxDir, cleanup } = createSandbox(projectDir);
|
|
82
248
|
const sandboxCreateTime = Date.now() - startTime;
|
|
83
|
-
console.log(color('green', `✅ Sandbox created in ${sandboxCreateTime}ms`));
|
|
249
|
+
if (VERBOSE_OUTPUT) console.log(color('green', `✅ Sandbox created in ${sandboxCreateTime}ms`));
|
|
84
250
|
|
|
85
251
|
process.on('SIGINT', cleanup);
|
|
86
252
|
process.on('SIGTERM', cleanup);
|
|
87
253
|
|
|
88
254
|
try {
|
|
89
255
|
const envStartTime = Date.now();
|
|
90
|
-
console.log(color('cyan', '⏱️ Stage 2: Setting up environment...'));
|
|
256
|
+
if (VERBOSE_OUTPUT) console.log(color('cyan', '⏱️ Stage 2: Setting up environment...'));
|
|
91
257
|
|
|
92
258
|
// Apply Claude optimizations
|
|
93
259
|
const claudeOptimizer = new ClaudeOptimizer(sandboxDir);
|
|
@@ -108,13 +274,13 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
108
274
|
: claudeOptimizer.createOptimizedEnv(baseEnv);
|
|
109
275
|
|
|
110
276
|
const envCreateTime = Date.now() - envStartTime;
|
|
111
|
-
console.log(color('green', `✅ Environment configured in ${envCreateTime}ms`));
|
|
277
|
+
if (VERBOSE_OUTPUT) console.log(color('green', `✅ Environment configured in ${envCreateTime}ms`));
|
|
112
278
|
|
|
113
|
-
if (systemOptimizationsApplied) {
|
|
279
|
+
if (systemOptimizationsApplied && VERBOSE_OUTPUT) {
|
|
114
280
|
console.log(color('yellow', `🚀 System-level optimizations applied`));
|
|
115
281
|
}
|
|
116
282
|
|
|
117
|
-
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`));
|
|
118
284
|
|
|
119
285
|
const claudeArgs = [
|
|
120
286
|
'--verbose',
|
|
@@ -123,7 +289,7 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
123
289
|
'--allowed-tools', ALLOWED_TOOLS.join(',')
|
|
124
290
|
];
|
|
125
291
|
|
|
126
|
-
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`));
|
|
127
293
|
|
|
128
294
|
return new Promise((resolve, reject) => {
|
|
129
295
|
const claudeStartTime = Date.now();
|
|
@@ -168,10 +334,37 @@ export async function claudeCommand(projectDir, prompt) {
|
|
|
168
334
|
if (Array.isArray(content)) {
|
|
169
335
|
for (const block of content) {
|
|
170
336
|
if (block.type === 'text') {
|
|
171
|
-
|
|
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
|
+
}
|
|
172
343
|
} else if (block.type === 'tool_use') {
|
|
173
|
-
|
|
174
|
-
|
|
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);
|
|
175
368
|
}
|
|
176
369
|
}
|
|
177
370
|
}
|
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() {
|