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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "3.0.16",
3
+ "version": "3.0.17",
4
4
  "description": "Lightweight process containment sandbox for CLI tools - Playwright, Claude Code, and more. Pure Node.js, no dependencies.",
5
5
  "type": "module",
6
6
  "main": "cli.js",
@@ -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
- // Helper function to log tool calls
27
- function logToolCall(toolName, action = 'call') {
28
- const timestamp = new Date().toISOString();
29
- const logEntry = `[${timestamp}] Tool ${action}: ${toolName}`;
105
+ return metadata;
106
+ }
30
107
 
31
- global.toolCallLog.push(logEntry);
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
- appendFileSync(global.logFileHandle, logEntry + '\n');
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
- process.stdout.write(block.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
+ }
172
343
  } else if (block.type === 'tool_use') {
173
- logToolCall(block.name);
174
- console.log(color('cyan', `\n🔧 Using tool: ${block.name}`));
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
- console.log(color('cyan', '📦 SandboxBox - Portable Container Runner'));
5
- console.log(color('cyan', '═════════════════════════════════════════════════'));
6
- console.log('');
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() {