sandboxbox 3.0.48 → 3.0.49

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.
@@ -0,0 +1,341 @@
1
+ import { mkdtempSync, rmSync, cpSync, existsSync, mkdirSync, writeFileSync, symlinkSync, realpathSync, readFileSync, appendFileSync } from 'fs';
2
+ import { tmpdir, homedir, platform } from 'os';
3
+ import { join, resolve, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { spawn, execSync } from 'child_process';
6
+
7
+ export function createSandbox(projectDir, options = {}) {
8
+ const { useHostSettings = false, headlessMode = false } = options;
9
+ const sandboxDir = mkdtempSync(join(tmpdir(), 'sandboxbox-'));
10
+ const workspaceDir = join(sandboxDir, 'workspace');
11
+ const VERBOSE_OUTPUT = process.env.SANDBOX_VERBOSE === 'true' || process.argv.includes('--verbose');
12
+
13
+ // Ensure host directory is a git repository
14
+ if (!existsSync(join(projectDir, '.git'))) {
15
+ // Initialize git repository in host directory if it doesn't exist
16
+ execSync(`git init "${projectDir}"`, {
17
+ stdio: 'pipe',
18
+ shell: true,
19
+ windowsHide: true
20
+ });
21
+ }
22
+
23
+ // Configure global git safe directories
24
+ execSync(`git config --global --add safe.directory "${projectDir}"`, {
25
+ stdio: 'pipe',
26
+ shell: true,
27
+ windowsHide: true
28
+ });
29
+ execSync(`git config --global --add safe.directory "${projectDir}/.git"`, {
30
+ stdio: 'pipe',
31
+ shell: true,
32
+ windowsHide: true
33
+ });
34
+
35
+ // Configure host repository to accept pushes to current branch
36
+ try {
37
+ execSync(`cd "${projectDir}" && git config receive.denyCurrentBranch updateInstead`, {
38
+ stdio: 'pipe',
39
+ shell: true,
40
+ windowsHide: true
41
+ });
42
+ } catch (error) {
43
+ // Silently skip repository configuration
44
+ }
45
+
46
+ // Copy/clone the project to workspace
47
+ if (existsSync(join(projectDir, '.git'))) {
48
+ // If it's a git repo, do a shallow clone
49
+ execSync(`git clone --depth 1 --no-tags "${projectDir}" "${workspaceDir}"`, {
50
+ stdio: 'pipe',
51
+ shell: true,
52
+ windowsHide: true
53
+ });
54
+ } else {
55
+ // If not a git repo, just copy the files
56
+ mkdirSync(workspaceDir, { recursive: true });
57
+ execSync(`cp -r "${projectDir}"/* "${workspaceDir}/"`, {
58
+ stdio: 'pipe',
59
+ shell: true,
60
+ windowsHide: true
61
+ });
62
+ // Initialize git in workspace
63
+ execSync(`git init "${workspaceDir}"`, {
64
+ stdio: 'pipe',
65
+ shell: true,
66
+ windowsHide: true
67
+ });
68
+ }
69
+
70
+ // Ensure .claude is in .gitignore in the sandbox workspace
71
+ const gitignorePath = join(workspaceDir, '.gitignore');
72
+ if (!existsSync(gitignorePath)) {
73
+ writeFileSync(gitignorePath, `# Claude Code settings (project-specific, not to be committed)
74
+ .claude/
75
+
76
+ # Dependencies
77
+ node_modules/
78
+ *.log
79
+ .DS_Store
80
+ `);
81
+ } else {
82
+ // Add .claude to existing .gitignore if not already present
83
+ const gitignoreContent = readFileSync(gitignorePath, 'utf8');
84
+ if (!gitignoreContent.includes('.claude/')) {
85
+ writeFileSync(gitignorePath, gitignoreContent + '\n# Claude Code settings (project-specific, not to be committed)\n.claude/\n');
86
+ }
87
+ }
88
+
89
+ // Set up host repo as origin in sandbox (pointing to host directory)
90
+ try {
91
+ execSync(`git remote add origin "${projectDir}"`, {
92
+ cwd: workspaceDir,
93
+ stdio: 'pipe',
94
+ shell: true
95
+ });
96
+ } catch (e) {
97
+ // Remote already exists, update it
98
+ execSync(`git remote set-url origin "${projectDir}"`, {
99
+ cwd: workspaceDir,
100
+ stdio: 'pipe',
101
+ shell: true
102
+ });
103
+ // Remote updated silently
104
+ }
105
+
106
+ // Set up upstream tracking for current branch
107
+ try {
108
+ const currentBranch = execSync(`git branch --show-current`, {
109
+ cwd: workspaceDir,
110
+ encoding: 'utf8',
111
+ stdio: 'pipe'
112
+ }).trim();
113
+
114
+ // Ensure the branch exists on the host side
115
+ try {
116
+ execSync(`cd "${projectDir}" && git checkout ${currentBranch}`, {
117
+ stdio: 'pipe',
118
+ shell: true
119
+ });
120
+ } catch (e) {
121
+ // Branch doesn't exist on host, create it
122
+ execSync(`cd "${projectDir}" && git checkout -b ${currentBranch}`, {
123
+ stdio: 'pipe',
124
+ shell: true
125
+ });
126
+ }
127
+
128
+ execSync(`git branch --set-upstream-to=origin/${currentBranch} ${currentBranch}`, {
129
+ cwd: workspaceDir,
130
+ stdio: 'pipe',
131
+ shell: true
132
+ });
133
+ } catch (e) {
134
+ // Upstream may not exist yet, ignore error
135
+ }
136
+
137
+ // Copy project's .claude/settings.json if it exists (project-level Claude settings)
138
+ const projectClaudeSettingsPath = join(projectDir, '.claude', 'settings.json');
139
+ if (existsSync(projectClaudeSettingsPath)) {
140
+ const sandboxClaudeSettingsPath = join(workspaceDir, '.claude', 'settings.json');
141
+ mkdirSync(join(workspaceDir, '.claude'), { recursive: true });
142
+ cpSync(projectClaudeSettingsPath, sandboxClaudeSettingsPath);
143
+
144
+ if (VERBOSE_OUTPUT) {
145
+ console.log('✅ Copied project Claude settings to sandbox');
146
+ }
147
+ }
148
+
149
+ // Batch fetch git identity settings for efficiency
150
+ const gitSettings = execSync(`git config --global --get user.name && git config --global --get user.email && git config --global --get color.ui`, {
151
+ stdio: 'pipe',
152
+ shell: true,
153
+ encoding: 'utf8'
154
+ }).trim().split('\n');
155
+
156
+ const [userName, userEmail, colorUi] = gitSettings;
157
+
158
+ // Batch configure git settings in sandbox
159
+ execSync(`git config user.name "${userName}" && git config user.email "${userEmail}" && git config color.ui "${colorUi}"`, {
160
+ cwd: workspaceDir,
161
+ stdio: 'pipe',
162
+ shell: true
163
+ });
164
+
165
+ // Configure Git remote to host for bidirectional synchronization
166
+ try {
167
+ execSync(`cd "${workspaceDir}" && git remote add host "${projectDir}"`, {
168
+ stdio: 'pipe',
169
+ shell: true
170
+ });
171
+
172
+ if (VERBOSE_OUTPUT) {
173
+ console.log('✅ Configured Git remote to host repository');
174
+ }
175
+ } catch (error) {
176
+ // Remote might already exist, try to update it
177
+ try {
178
+ execSync(`cd "${workspaceDir}" && git remote set-url host "${projectDir}"`, {
179
+ stdio: 'pipe',
180
+ shell: true
181
+ });
182
+
183
+ if (VERBOSE_OUTPUT) {
184
+ console.log('✅ Updated Git remote to host repository');
185
+ }
186
+ } catch (updateError) {
187
+ if (VERBOSE_OUTPUT) {
188
+ console.log('⚠️ Could not configure Git remote to host');
189
+ }
190
+ }
191
+ }
192
+
193
+ // Setup Claude settings in sandbox
194
+ const hostClaudeDir = join(homedir(), '.claude');
195
+ const sandboxClaudeDir = join(sandboxDir, '.claude');
196
+
197
+ // Always use bundled SandboxBox settings unless host settings are requested
198
+ if (!useHostSettings) {
199
+ // Create sandbox Claude directory and copy bundled settings
200
+ mkdirSync(sandboxClaudeDir, { recursive: true });
201
+
202
+ const bundledSettingsPath = join(resolve(fileURLToPath(import.meta.url), '..', '..'), 'sandboxbox-settings.json');
203
+ const sandboxSettingsPath = join(sandboxClaudeDir, 'settings.json');
204
+
205
+ // Copy bundled settings to sandbox
206
+ if (existsSync(bundledSettingsPath)) {
207
+ if (VERBOSE_OUTPUT) {
208
+ console.log(`🔍 Debug: Found bundled settings at ${bundledSettingsPath}`);
209
+ }
210
+ cpSync(bundledSettingsPath, sandboxSettingsPath);
211
+
212
+ // Also copy credentials from host if available
213
+ const hostCredentialsPath = join(hostClaudeDir, '.credentials.json');
214
+ if (existsSync(hostCredentialsPath)) {
215
+ cpSync(hostCredentialsPath, join(sandboxClaudeDir, '.credentials.json'));
216
+ }
217
+
218
+ if (VERBOSE_OUTPUT) {
219
+ console.log('✅ Using bundled SandboxBox settings with Git integration hooks and MCP servers');
220
+ // Show hook and MCP information
221
+ const settings = JSON.parse(readFileSync(bundledSettingsPath, 'utf8'));
222
+ if (settings.hooks) {
223
+ console.log('📋 Bundled hooks configured:');
224
+ Object.keys(settings.hooks).forEach(hookType => {
225
+ const hookCount = settings.hooks[hookType].length;
226
+ console.log(` ${hookType}: ${hookCount} hook(s)`);
227
+ settings.hooks[hookType].forEach((hook, index) => {
228
+ const commandCount = hook.hooks ? hook.hooks.length : 0;
229
+ console.log(` ${index + 1}. ${hook.matcher || '*'} (${commandCount} commands)`);
230
+ });
231
+ });
232
+ }
233
+ if (settings.mcpServers) {
234
+ console.log('🔧 MCP servers configured:');
235
+ Object.keys(settings.mcpServers).forEach(serverName => {
236
+ const server = settings.mcpServers[serverName];
237
+ console.log(` ${serverName}: ${server.command} ${server.args.join(' ')}`);
238
+ });
239
+ }
240
+ }
241
+ } else {
242
+ if (VERBOSE_OUTPUT) {
243
+ console.log(`🔍 Debug: Bundled settings not found at ${bundledSettingsPath}`);
244
+ }
245
+ }
246
+ }
247
+
248
+ // Optimize cache directory handling - use symlinks instead of copying
249
+ const hostCacheDir = join(homedir(), '.cache');
250
+ if (existsSync(hostCacheDir)) {
251
+ const sandboxCacheDir = join(sandboxDir, '.cache');
252
+ mkdirSync(sandboxCacheDir, { recursive: true });
253
+
254
+ // Create symlink to ms-playwright cache instead of copying (major performance improvement)
255
+ const playwrightCacheDir = join(hostCacheDir, 'ms-playwright');
256
+ if (existsSync(playwrightCacheDir)) {
257
+ const sandboxPlaywrightDir = join(sandboxCacheDir, 'ms-playwright');
258
+ try {
259
+ symlinkSync(playwrightCacheDir, sandboxPlaywrightDir, 'dir');
260
+ } catch (error) {
261
+ // Fallback to copying only if symlink fails
262
+ cpSync(playwrightCacheDir, sandboxPlaywrightDir, { recursive: true });
263
+ }
264
+ }
265
+ }
266
+
267
+ const cleanup = () => {
268
+ // Close any log files that might be open
269
+ if (global.logFileHandle) {
270
+ try {
271
+ appendFileSync(global.logFileHandle, `\n# Session ended: ${new Date().toISOString()}\n`);
272
+ global.logFileHandle = null;
273
+ } catch (error) {
274
+ // Don't fail on log cleanup
275
+ }
276
+ }
277
+ rmSync(sandboxDir, { recursive: true, force: true });
278
+ };
279
+
280
+ return { sandboxDir, cleanup };
281
+ }
282
+
283
+ export function createSandboxEnv(sandboxDir, options = {}) {
284
+ const sandboxClaudeDir = join(sandboxDir, '.claude');
285
+ const sandboxCacheDir = join(sandboxDir, '.cache');
286
+
287
+ // Start with all process environment variables
288
+ const env = {
289
+ ...process.env,
290
+ };
291
+
292
+ // IMPORTANT: Set HOME to sandbox directory for Claude to find settings
293
+ // but preserve access to host credentials via symlink
294
+ env.HOME = sandboxDir;
295
+ env.USERPROFILE = sandboxDir;
296
+
297
+ // Set XDG paths to use sandbox Claude directory
298
+ env.XDG_CONFIG_HOME = sandboxClaudeDir;
299
+ env.XDG_DATA_HOME = join(sandboxClaudeDir, '.local', 'share');
300
+ env.XDG_CACHE_HOME = sandboxCacheDir;
301
+ env.TMPDIR = join(sandboxDir, 'tmp');
302
+ env.TEMP = join(sandboxDir, 'tmp');
303
+ env.TMP = join(sandboxDir, 'tmp');
304
+ env.PLAYWRIGHT_BROWSERS_PATH = join(sandboxDir, 'browsers');
305
+ env.PLAYWRIGHT_STORAGE_STATE = join(sandboxDir, '.playwright', 'storage-state.json');
306
+ if (process.env.CLAUDE_CODE_ENTRYPOINT) {
307
+ env.CLAUDE_CODE_ENTRYPOINT = process.env.CLAUDE_CODE_ENTRYPOINT;
308
+ }
309
+
310
+ // Ensure TERM is set with fallback
311
+ env.TERM = process.env.TERM || 'xterm-256color';
312
+
313
+ // Apply any additional options
314
+ Object.assign(env, options);
315
+
316
+ return env;
317
+ }
318
+
319
+ export function runInSandbox(commandStr, args, sandboxDir, env) {
320
+ return new Promise((resolve, reject) => {
321
+ const fullCommand = args.length > 0 ? `${commandStr} ${args.join(' ')}` : commandStr;
322
+
323
+ const proc = spawn(fullCommand, [], {
324
+ cwd: join(sandboxDir, 'workspace'),
325
+ env,
326
+ stdio: 'inherit',
327
+ shell: true,
328
+ windowsHide: false
329
+ });
330
+
331
+ proc.on('close', (code) => {
332
+ if (code === 0) {
333
+ resolve();
334
+ } else {
335
+ reject(new Error(`Process exited with code ${code}`));
336
+ }
337
+ });
338
+
339
+ proc.on('error', reject);
340
+ });
341
+ }
@@ -0,0 +1,231 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ export class SystemOptimizer {
6
+ constructor() {
7
+ this.hasSudo = this.checkSudoAccess();
8
+ }
9
+
10
+ checkSudoAccess() {
11
+ try {
12
+ execSync('sudo -n true', { stdio: 'pipe' });
13
+ return true;
14
+ } catch (e) {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ // Optimize system for Claude Code performance
20
+ async optimizeSystem() {
21
+ if (!this.hasSudo) {
22
+ return false;
23
+ }
24
+
25
+ const optimizations = [
26
+ this.optimizeKernelParameters(),
27
+ this.optimizeNetworkStack(),
28
+ this.preloadCommonLibraries(),
29
+ this.optimizeFilesystem(),
30
+ this.setupClaudeService()
31
+ ];
32
+
33
+ const results = await Promise.allSettled(optimizations);
34
+ const successCount = results.filter(r => r.status === 'fulfilled').length;
35
+
36
+ return successCount > 0;
37
+ }
38
+
39
+ // Optimize kernel parameters for faster process spawning
40
+ async optimizeKernelParameters() {
41
+ if (!this.hasSudo) return false;
42
+
43
+ try {
44
+ const kernelTweaks = [
45
+ 'sysctl -w vm.overcommit_memory=1', // Allow memory overcommit
46
+ 'sysctl -w kernel.sched_min_granularity_ns=1000000', // Faster task scheduling
47
+ 'sysctl -w fs.file-max=1048576', // Increase file descriptor limit
48
+ 'sysctl -w net.core.somaxconn=65535', // Increase connection backlog
49
+ 'sysctl -w net.ipv4.tcp_fin_timeout=15', // Faster TCP connection cleanup
50
+ 'sysctl -w net.ipv4.tcp_keepalive_time=600', // Reduce keepalive timeout
51
+ 'sysctl -w vm.swappiness=10' // Reduce swapping
52
+ ];
53
+
54
+ for (const tweak of kernelTweaks) {
55
+ execSync(`sudo ${tweak}`, { stdio: 'pipe' });
56
+ }
57
+
58
+ return true;
59
+ } catch (error) {
60
+ // Silently skip optimization if it fails
61
+ return false;
62
+ }
63
+ }
64
+
65
+ // Preload Claude and plugin dependencies
66
+ async preloadCommonLibraries() {
67
+ if (!this.hasSudo) return false;
68
+
69
+ try {
70
+ // Create a preload script for common Node.js and Claude dependencies
71
+ const preloadScript = `
72
+ # Preload common libraries for faster Claude startup
73
+ echo "Preloading Claude dependencies..."
74
+
75
+ # Common Node.js modules
76
+ export NODE_PATH="/usr/lib/node_modules:$NODE_PATH"
77
+
78
+ # Preload essential binaries
79
+ which npx >/dev/null && npx --version >/dev/null 2>&1 &
80
+ which node >/dev/null && node --version >/dev/null 2>&1 &
81
+
82
+ # Wait for preloads to complete
83
+ wait
84
+
85
+ echo "Preloading complete"
86
+ `;
87
+
88
+ const preloadPath = '/tmp/claude-preload.sh';
89
+ writeFileSync(preloadPath, preloadScript);
90
+ execSync(`chmod +x ${preloadPath}`, { stdio: 'pipe' });
91
+
92
+ // Execute preload script with elevated privileges if needed
93
+ execSync(preloadPath, { stdio: 'pipe' });
94
+
95
+ return true;
96
+ } catch (error) {
97
+ // Silently skip optimization if it fails
98
+ return false;
99
+ }
100
+ }
101
+
102
+ // Optimize filesystem for faster access
103
+ async optimizeFilesystem() {
104
+ if (!this.hasSudo) return false;
105
+
106
+ try {
107
+ // Optimize tmpfs for faster temporary operations
108
+ const tmpfsOptimizations = [
109
+ 'mount -t tmpfs -o size=1G tmpfs /tmp/claude-cache 2>/dev/null || true',
110
+ 'mkdir -p /tmp/claude-cache /tmp/claude-plugins',
111
+ 'chmod 777 /tmp/claude-cache /tmp/claude-plugins'
112
+ ];
113
+
114
+ for (const opt of tmpfsOptimizations) {
115
+ execSync(`sudo ${opt}`, { stdio: 'pipe' });
116
+ }
117
+
118
+ return true;
119
+ } catch (error) {
120
+ // Silently skip optimization if it fails
121
+ return false;
122
+ }
123
+ }
124
+
125
+ // Setup Claude as a pre-warmed service
126
+ async setupClaudeService() {
127
+ if (!this.hasSudo) return false;
128
+
129
+ try {
130
+ const serviceConfig = `[Unit]
131
+ Description=Claude Code Pre-warm Service
132
+ After=network.target
133
+
134
+ [Service]
135
+ Type=forking
136
+ User=root
137
+ ExecStart=/usr/local/bin/claude-prewarm.sh
138
+ Restart=on-failure
139
+ RestartSec=5
140
+
141
+ [Install]
142
+ WantedBy=multi-user.target
143
+ `;
144
+
145
+ const prewarmScript = `#!/bin/bash
146
+ # Claude Code pre-warming service
147
+ echo "Starting Claude pre-warm service..."
148
+
149
+ # Create necessary directories
150
+ mkdir -p /tmp/claude-cache /tmp/claude-plugins /tmp/claude-sessions
151
+
152
+ # Pre-warm Node.js runtime
153
+ timeout 10s node --version >/dev/null 2>&1 &
154
+
155
+ # Pre-warm NPX
156
+ timeout 10s npx --version >/dev/null 2>&1 &
157
+
158
+ # Pre-warm common plugins if available
159
+ timeout 5s npx -y glootie-cc@anentrypoint-plugins --help >/dev/null 2>&1 &
160
+
161
+ echo "Claude pre-warm service ready"
162
+ `;
163
+
164
+ // Write service file
165
+ execSync('echo "' + serviceConfig.replace(/"/g, '\\"') + '" | sudo tee /etc/systemd/system/claude-prewarm.service', { stdio: 'pipe' });
166
+
167
+ // Write prewarm script
168
+ execSync('echo "' + prewarmScript.replace(/"/g, '\\"') + '" | sudo tee /usr/local/bin/claude-prewarm.sh', { stdio: 'pipe' });
169
+ execSync('sudo chmod +x /usr/local/bin/claude-prewarm.sh', { stdio: 'pipe' });
170
+
171
+ // Enable and start service
172
+ execSync('sudo systemctl daemon-reload', { stdio: 'pipe' });
173
+ execSync('sudo systemctl enable claude-prewarm.service', { stdio: 'pipe' });
174
+ execSync('sudo systemctl start claude-prewarm.service', { stdio: 'pipe' });
175
+
176
+ return true;
177
+ } catch (error) {
178
+ // Silently skip optimization if it fails
179
+ return false;
180
+ }
181
+ }
182
+
183
+ // Optimize network stack for faster plugin communication
184
+ async optimizeNetworkStack() {
185
+ if (!this.hasSudo) return false;
186
+
187
+ try {
188
+ const networkOptimizations = [
189
+ 'sysctl -w net.core.rmem_max=134217728', // Increase receive buffer
190
+ 'sysctl -w net.core.wmem_max=134217728', // Increase send buffer
191
+ 'sysctl -w net.ipv4.tcp_rmem="4096 65536 134217728"', // TCP receive buffer
192
+ 'sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728"', // TCP send buffer
193
+ 'sysctl -w net.ipv4.tcp_congestion_control=bbr' // Use BBR congestion control
194
+ ];
195
+
196
+ for (const opt of networkOptimizations) {
197
+ execSync(`sudo ${opt}`, { stdio: 'pipe' });
198
+ }
199
+
200
+ return true;
201
+ } catch (error) {
202
+ // Silently skip optimization if it fails
203
+ return false;
204
+ }
205
+ }
206
+
207
+ // Create optimized container environment
208
+ createOptimizedContainerEnv() {
209
+ return {
210
+ // System optimizations
211
+ 'NODE_OPTIONS': '--max-old-space-size=4096 --no-experimental-fetch',
212
+ 'CLAUDE_OPTIMIZED': '1',
213
+ 'CLAUDE_CACHE_DIR': '/tmp/claude-cache',
214
+ 'CLAUDE_PLUGIN_DIR': '/tmp/claude-plugins',
215
+
216
+ // Performance tuning
217
+ 'UV_THREADPOOL_SIZE': '16',
218
+ 'NODEUV_THREADPOOL_SIZE': '16',
219
+
220
+ // Fast startup flags
221
+ 'CLAUDE_FAST_INIT': '1',
222
+ 'CLAUDE_SKIP_WELCOME': '1',
223
+ 'CLAUDE_MINIMAL_PLUGINS': '1',
224
+
225
+ // Timeout optimizations
226
+ 'MCP_TIMEOUT': '3000',
227
+ 'MCP_CONNECT_TIMEOUT': '2000',
228
+ 'CLAUDE_PLUGIN_TIMEOUT': '5000'
229
+ };
230
+ }
231
+ }
@@ -0,0 +1,38 @@
1
+ import { color } from './colors.js';
2
+
3
+ export function showBanner() {
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
+ }
10
+ }
11
+
12
+ export function showHelp() {
13
+ console.log(color('yellow', 'Usage:'));
14
+ console.log(' npx sandboxbox <command> [options]');
15
+ console.log('');
16
+ console.log(color('yellow', 'Commands:'));
17
+ console.log(' build [dockerfile] Build container from Dockerfile');
18
+ console.log(' run <project-dir> [cmd] Run project in container');
19
+ console.log(' shell <project-dir> Start interactive shell');
20
+ console.log(' claude <project-dir> [prompt] [--host] [--headless] Start Claude Code with Git integration');
21
+ console.log(' version Show version information');
22
+ console.log('');
23
+ console.log(color('yellow', 'Claude Command Options:'));
24
+ console.log(' --host Use host Claude settings instead of bundled');
25
+ console.log(' --headless Configure headless Playwright MCP (runs before Claude)');
26
+ console.log('');
27
+ console.log(color('yellow', 'Examples:'));
28
+ console.log(' npx sandboxbox build');
29
+ console.log(' npx sandboxbox claude ./my-project');
30
+ console.log(' npx sandboxbox run ./my-project "npm test"');
31
+ console.log(' npx sandboxbox shell ./my-project');
32
+ console.log('');
33
+ console.log(color('yellow', 'Requirements:'));
34
+ console.log(' - Docker/Podman runtime');
35
+ console.log(' - Works on Windows, macOS, and Linux!');
36
+ console.log('');
37
+ console.log(color('magenta', '🚀 Fast startup • True isolation • Claude Code integration'));
38
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "3.0.46",
3
+ "version": "3.0.48",
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",
Binary file
@@ -3,7 +3,8 @@ import { resolve, join } from 'path';
3
3
  import { spawn, execSync } from 'child_process';
4
4
  import { color } from '../colors.js';
5
5
  import { createSandbox, createSandboxEnv } from '../sandbox.js';
6
- import { ClaudeOptimizer } from '../claude-optimizer.js';
6
+ // ClaudeOptimizer disabled to preserve bundled hooks
7
+ // import { ClaudeOptimizer } from '../claude-optimizer.js';
7
8
  import { SystemOptimizer } from '../system-optimizer.js';
8
9
 
9
10
  const ALLOWED_TOOLS = [
@@ -253,10 +254,10 @@ export async function claudeCommand(projectDir, prompt, flags = {}) {
253
254
  const envStartTime = Date.now();
254
255
  if (VERBOSE_OUTPUT) console.log(color('cyan', '⏱️ Stage 2: Setting up environment...'));
255
256
 
256
- // Apply Claude optimizations
257
- const claudeOptimizer = new ClaudeOptimizer(sandboxDir);
258
- claudeOptimizer.optimizeSettings();
259
- await claudeOptimizer.prewarmPlugins();
257
+ // Apply Claude optimizations (disabled to preserve bundled hooks)
258
+ // const claudeOptimizer = new ClaudeOptimizer(sandboxDir);
259
+ // claudeOptimizer.optimizeSettings();
260
+ // await claudeOptimizer.prewarmPlugins();
260
261
 
261
262
  // Apply system optimizations (with sudo access)
262
263
  const systemOptimizer = new SystemOptimizer();
@@ -269,7 +270,7 @@ export async function claudeCommand(projectDir, prompt, flags = {}) {
269
270
 
270
271
  const env = systemOptimizationsApplied
271
272
  ? { ...baseEnv, ...systemOptimizer.createOptimizedContainerEnv() }
272
- : claudeOptimizer.createOptimizedEnv(baseEnv);
273
+ : baseEnv; // Use base environment when optimizer is disabled
273
274
 
274
275
  const envCreateTime = Date.now() - envStartTime;
275
276
  if (VERBOSE_OUTPUT) console.log(color('green', `✅ Environment configured in ${envCreateTime}ms`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "3.0.48",
3
+ "version": "3.0.49",
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",
Binary file