sandboxbox 2.5.4 → 3.0.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.
package/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * SandboxBox CLI - Portable Container Runner with Podman
5
- * Cross-platform container runner using Podman with Claude Code integration
4
+ * SandboxBox CLI - Process Containment Sandbox
5
+ * Lightweight process isolation for CLI tools
6
6
  */
7
7
 
8
8
  import { resolve } from 'path';
@@ -10,7 +10,7 @@ import { color } from './utils/colors.js';
10
10
  import { showBanner, showHelp } from './utils/ui.js';
11
11
  import { buildCommand, runCommand, shellCommand, claudeCommand, versionCommand } from './utils/commands/index.js';
12
12
 
13
- function main() {
13
+ async function main() {
14
14
  const args = process.argv.slice(2);
15
15
  showBanner();
16
16
 
@@ -22,53 +22,57 @@ function main() {
22
22
  const command = args[0].toLowerCase();
23
23
  const commandArgs = args.slice(1);
24
24
 
25
- switch (command) {
26
- case 'build':
27
- const dockerfilePath = commandArgs[0] || './Dockerfile';
28
- if (!buildCommand(dockerfilePath)) process.exit(1);
29
- break;
25
+ try {
26
+ switch (command) {
27
+ case 'build':
28
+ const dockerfilePath = commandArgs[0] || './Dockerfile';
29
+ if (!buildCommand(dockerfilePath)) process.exit(1);
30
+ break;
30
31
 
31
- case 'run':
32
- if (commandArgs.length === 0) {
33
- console.log(color('red', '❌ Please specify a project directory'));
34
- console.log(color('yellow', 'Usage: npx sandboxbox run <project-dir> [command]'));
35
- process.exit(1);
36
- }
37
- // For NPX usage, resolve relative to current working directory, not script location
38
- const projectDir = resolve(process.cwd(), commandArgs[0]);
39
- const cmd = commandArgs[1] || 'bash';
40
- if (!runCommand(projectDir, cmd)) process.exit(1);
41
- break;
32
+ case 'run':
33
+ if (commandArgs.length === 0) {
34
+ console.log(color('red', '❌ Please specify a project directory'));
35
+ console.log(color('yellow', 'Usage: npx sandboxbox run <project-dir> [command]'));
36
+ process.exit(1);
37
+ }
38
+ const projectDir = resolve(process.cwd(), commandArgs[0]);
39
+ const cmd = commandArgs.slice(1).join(' ') || 'bash';
40
+ if (!(await runCommand(projectDir, cmd))) process.exit(1);
41
+ break;
42
42
 
43
- case 'shell':
44
- if (commandArgs.length === 0) {
45
- console.log(color('red', '❌ Please specify a project directory'));
46
- console.log(color('yellow', 'Usage: npx sandboxbox shell <project-dir>'));
47
- process.exit(1);
48
- }
49
- const shellProjectDir = resolve(process.cwd(), commandArgs[0]);
50
- if (!shellCommand(shellProjectDir)) process.exit(1);
51
- break;
43
+ case 'shell':
44
+ if (commandArgs.length === 0) {
45
+ console.log(color('red', '❌ Please specify a project directory'));
46
+ console.log(color('yellow', 'Usage: npx sandboxbox shell <project-dir>'));
47
+ process.exit(1);
48
+ }
49
+ const shellProjectDir = resolve(process.cwd(), commandArgs[0]);
50
+ if (!(await shellCommand(shellProjectDir))) process.exit(1);
51
+ break;
52
52
 
53
- case 'claude':
54
- if (commandArgs.length === 0) {
55
- console.log(color('red', '❌ Please specify a project directory'));
56
- console.log(color('yellow', 'Usage: npx sandboxbox claude <project-dir>'));
57
- process.exit(1);
58
- }
59
- const claudeProjectDir = resolve(process.cwd(), commandArgs[0]);
60
- const claudeCmd = commandArgs.slice(1).join(' ') || 'claude';
61
- if (!claudeCommand(claudeProjectDir, claudeCmd)) process.exit(1);
62
- break;
53
+ case 'claude':
54
+ if (commandArgs.length === 0) {
55
+ console.log(color('red', '❌ Please specify a project directory'));
56
+ console.log(color('yellow', 'Usage: npx sandboxbox claude <project-dir>'));
57
+ process.exit(1);
58
+ }
59
+ const claudeProjectDir = resolve(process.cwd(), commandArgs[0]);
60
+ const claudeCmd = commandArgs.slice(1).join(' ') || 'claude';
61
+ if (!(await claudeCommand(claudeProjectDir, claudeCmd))) process.exit(1);
62
+ break;
63
63
 
64
- case 'version':
65
- if (!versionCommand()) process.exit(1);
66
- break;
64
+ case 'version':
65
+ if (!versionCommand()) process.exit(1);
66
+ break;
67
67
 
68
- default:
69
- console.log(color('red', `❌ Unknown command: ${command}`));
70
- console.log(color('yellow', 'Use --help for usage information'));
71
- process.exit(1);
68
+ default:
69
+ console.log(color('red', `❌ Unknown command: ${command}`));
70
+ console.log(color('yellow', 'Use --help for usage information'));
71
+ process.exit(1);
72
+ }
73
+ } catch (error) {
74
+ console.log(color('red', `\n❌ Fatal error: ${error.message}`));
75
+ process.exit(1);
72
76
  }
73
77
  }
74
78
 
package/package.json CHANGED
@@ -1,28 +1,29 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "2.5.4",
4
- "description": "Portable container runner with Podman - Claude Code & Playwright support. Works on Windows, macOS, and Linux.",
3
+ "version": "3.0.0",
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",
7
7
  "bin": {
8
8
  "sandboxbox": "cli.js"
9
9
  },
10
10
  "scripts": {
11
- "start": "node cli.js",
12
- "postinstall": "node scripts/download-podman.js"
11
+ "start": "node cli.js"
13
12
  },
14
13
  "keywords": [
15
- "containers",
16
- "podman",
14
+ "sandbox",
15
+ "process-isolation",
17
16
  "playwright",
18
17
  "claude-code",
19
- "sandbox",
20
- "isolation",
18
+ "cli-tools",
19
+ "environment-isolation",
21
20
  "cross-platform",
22
21
  "windows",
23
22
  "macos",
24
23
  "linux",
25
- "portable"
24
+ "portable",
25
+ "npm",
26
+ "npx"
26
27
  ],
27
28
  "author": "",
28
29
  "license": "MIT",
@@ -0,0 +1,3 @@
1
+ FROM node:20-alpine
2
+ WORKDIR /app
3
+ RUN echo "Test container"
@@ -1,16 +1,10 @@
1
- import { existsSync, writeFileSync } from 'fs';
2
- import { execSync } from 'child_process';
3
- import { resolve, dirname } from 'path';
4
- import { fileURLToPath } from 'url';
1
+ import { existsSync, cpSync, mkdirSync } from 'fs';
2
+ import { resolve, join } from 'path';
3
+ import { homedir } from 'os';
5
4
  import { color } from '../colors.js';
6
- import { checkPodman, setupBackendNonBlocking, getPodmanPath } from '../podman.js';
7
- import { buildClaudeContainerCommand, createClaudeDockerfile } from '../claude-workspace.js';
8
- import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
5
+ import { createSandbox, createSandboxEnv, runInSandbox } from '../sandbox.js';
9
6
 
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- export function claudeCommand(projectDir, command = 'claude') {
7
+ export async function claudeCommand(projectDir, command = 'claude') {
14
8
  if (!existsSync(projectDir)) {
15
9
  console.log(color('red', `❌ Project directory not found: ${projectDir}`));
16
10
  return false;
@@ -22,90 +16,40 @@ export function claudeCommand(projectDir, command = 'claude') {
22
16
  return false;
23
17
  }
24
18
 
25
- const podmanPath = getPodmanPath();
26
- try {
27
- execSync(`"${podmanPath}" image inspect sandboxbox-local:latest`, {
28
- stdio: 'pipe',
29
- shell: process.platform === 'win32',
30
- windowsHide: process.platform === 'win32'
31
- });
32
- } catch {
33
- console.log(color('yellow', '📦 Building Claude Code container...'));
34
- if (!buildClaudeContainer()) {
35
- return false;
36
- }
37
- }
38
-
39
- console.log(color('blue', '🚀 Starting Claude Code in isolated environment...'));
19
+ console.log(color('blue', '🚀 Starting Claude Code in sandbox...'));
40
20
  console.log(color('yellow', `Project: ${projectDir}`));
41
- console.log(color('yellow', `Command: ${command}`));
42
- console.log(color('cyan', '📦 Note: Changes will be isolated and will NOT affect the original repository\n'));
43
-
44
- const buildPodman = checkPodman();
45
- if (!buildPodman) return false;
46
- if (!setupBackendNonBlocking(buildPodman)) return false;
21
+ console.log(color('yellow', `Command: ${command}\n`));
47
22
 
48
- // Create isolated environment once (outside retry loop)
49
- const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
50
- setupCleanupHandlers(cleanup);
51
- const mounts = buildContainerMounts(tempProjectDir, projectDir);
52
- const containerCommand = buildClaudeContainerCommand(tempProjectDir, buildPodman, command, mounts);
23
+ const { sandboxDir, cleanup } = createSandbox(projectDir);
53
24
 
54
- // Retry container operation with backend readiness check
55
- let retries = 0;
56
- const maxRetries = process.platform === 'linux' ? 3 : 12; // More retries for Windows/macOS
25
+ process.on('SIGINT', cleanup);
26
+ process.on('SIGTERM', cleanup);
57
27
 
58
- while (retries < maxRetries) {
59
- try {
60
- execSync(containerCommand, {
61
- stdio: 'inherit',
62
- shell: process.platform === 'win32',
63
- windowsHide: process.platform === 'win32'
64
- // No timeout for interactive Claude sessions
65
- });
28
+ try {
29
+ const claudeDir = join(sandboxDir, '.claude');
30
+ mkdirSync(claudeDir, { recursive: true });
66
31
 
67
- cleanup();
68
- console.log(color('green', '\n✅ Claude Code session completed! (Isolated - no host changes)'));
69
- return true;
70
- } catch (error) {
71
- retries++;
72
- if (retries < maxRetries && (error.message.includes('Cannot connect to Podman') || error.message.includes('connectex') || error.message.includes('No connection could be made') || error.message.includes('actively refused') || error.message.includes('Command failed'))) {
73
- console.log(color('yellow', ` Backend not ready yet (${retries}/${maxRetries}), waiting 15 seconds...`));
74
- const start = Date.now();
75
- while (Date.now() - start < 15000) {
76
- // Wait 15 seconds
77
- }
78
- continue;
79
- }
80
- cleanup(); // Cleanup on final failure
81
- console.log(color('red', `\n❌ Claude Code failed: ${error.message}`));
82
- return false;
32
+ const hostClaudeDir = join(homedir(), '.claude');
33
+ if (existsSync(hostClaudeDir)) {
34
+ cpSync(hostClaudeDir, claudeDir, { recursive: true });
83
35
  }
84
- }
85
- }
86
36
 
87
- function buildClaudeContainer() {
88
- const dockerfilePath = resolve(__dirname, '..', '..', 'Dockerfile.claude');
89
- const dockerfileContent = createClaudeDockerfile();
37
+ const env = createSandboxEnv(sandboxDir, {
38
+ ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,
39
+ CLAUDECODE: '1'
40
+ });
90
41
 
91
- writeFileSync(dockerfilePath, dockerfileContent);
92
- console.log(color('blue', '🏗️ Building Claude Code container...'));
42
+ console.log(color('green', `✅ Sandbox created: ${sandboxDir}`));
43
+ console.log(color('cyan', '📦 Claude Code running in isolated environment...\n'));
93
44
 
94
- const podmanPath = checkPodman();
95
- if (!podmanPath) return false;
96
- if (!setupBackendNonBlocking(podmanPath)) return false;
45
+ await runInSandbox(`claude ${command}`, [], sandboxDir, env);
97
46
 
98
- try {
99
- execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox-local:latest .`, {
100
- stdio: 'inherit',
101
- cwd: resolve(__dirname, '..', '..'),
102
- shell: process.platform === 'win32',
103
- windowsHide: process.platform === 'win32'
104
- });
105
- console.log(color('green', '\n✅ Claude Code container built successfully!'));
47
+ console.log(color('green', '\n✅ Claude Code session completed!'));
48
+ cleanup();
106
49
  return true;
107
50
  } catch (error) {
108
- console.log(color('red', `\n❌ Build failed: ${error.message}`));
51
+ console.log(color('red', `\n❌ Claude Code failed: ${error.message}`));
52
+ cleanup();
109
53
  return false;
110
54
  }
111
55
  }
@@ -1,295 +1,60 @@
1
1
  import { existsSync } from 'fs';
2
- import { execSync } from 'child_process';
3
- import { dirname } from 'path';
2
+ import { resolve, join } from 'path';
4
3
  import { color } from '../colors.js';
5
- import { checkPodman, setupBackendNonBlocking } from '../podman.js';
6
- import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
4
+ import { createSandbox, createSandboxEnv, runInSandbox } from '../sandbox.js';
7
5
 
8
6
  export function buildCommand(dockerfilePath) {
9
- if (!existsSync(dockerfilePath)) {
10
- console.log(color('red', `❌ Dockerfile not found: ${dockerfilePath}`));
11
- return false;
12
- }
13
-
14
- console.log(color('blue', '🏗️ Building container...'));
15
- console.log(color('yellow', `Dockerfile: ${dockerfilePath}\n`));
16
-
17
- const podmanPath = checkPodman();
18
- if (!podmanPath) return false;
19
-
20
- // Start backend setup but don't block on Windows/macOS
21
- const backendReady = setupBackendNonBlocking(podmanPath);
22
- if (process.platform === 'linux' && !backendReady) {
23
- return false; // Only block on Linux for rootless service
24
- }
25
-
26
- // Retry container operation with backend readiness check
27
- let retries = 0;
28
- const maxRetries = process.platform === 'linux' ? 3 : 12; // More retries for Windows/macOS
29
-
30
- while (retries < maxRetries) {
31
- try {
32
- execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
33
- stdio: 'inherit',
34
- cwd: dirname(dockerfilePath),
35
- shell: process.platform === 'win32',
36
- windowsHide: process.platform === 'win32',
37
- timeout: 300000 // 5 minutes for container builds
38
- });
39
- console.log(color('green', '\n✅ Container built successfully!'));
40
- return true;
41
- } catch (error) {
42
- retries++;
43
- if (retries < maxRetries && (error.message.includes('Cannot connect to Podman') || error.message.includes('connectex') || error.message.includes('No connection could be made') || error.message.includes('actively refused') || error.message.includes('Command failed'))) {
44
- console.log(color('yellow', ` Backend not ready yet (${retries}/${maxRetries}), waiting 15 seconds...`));
45
- const start = Date.now();
46
- while (Date.now() - start < 15000) {
47
- // Wait 15 seconds
48
- }
49
- continue;
50
- }
51
- console.log(color('red', `\n❌ Build failed: ${error.message}`));
52
- return false;
53
- }
54
- }
7
+ console.log(color('yellow', '⚠️ Build command not yet implemented'));
8
+ return false;
55
9
  }
56
10
 
57
- export function runCommand(projectDir, cmd = 'bash') {
11
+ export async function runCommand(projectDir, cmd = 'bash') {
58
12
  if (!existsSync(projectDir)) {
59
13
  console.log(color('red', `❌ Project directory not found: ${projectDir}`));
60
14
  return false;
61
15
  }
62
16
 
63
- console.log(color('blue', '🚀 Running project in isolated container...'));
17
+ console.log(color('blue', '🚀 Creating sandbox environment...'));
64
18
  console.log(color('yellow', `Project: ${projectDir}`));
65
19
  console.log(color('yellow', `Command: ${cmd}\n`));
66
- console.log(color('cyan', '📦 Note: Changes will NOT affect host files (isolated environment)'));
67
-
68
- const podmanPath = checkPodman();
69
- if (!podmanPath) return false;
70
-
71
- // Start backend setup but don't block on Windows/macOS
72
- const backendReady = setupBackendNonBlocking(podmanPath);
73
- if (process.platform === 'linux' && !backendReady) {
74
- return false; // Only block on Linux for rootless service
75
- }
76
-
77
- // Create isolated environment once (outside retry loop)
78
- const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
79
- setupCleanupHandlers(cleanup);
80
- const mounts = buildContainerMounts(tempProjectDir, projectDir);
81
-
82
- // Retry container operation with backend readiness check
83
- let retries = 0;
84
- const maxRetries = process.platform === 'linux' ? 3 : 12; // More retries for Windows/macOS
85
-
86
- while (retries < maxRetries) {
87
- try {
88
- // Try container operation first
89
- const containerOptions = {
90
- stdio: process.platform === 'win32' ? ['pipe', 'pipe', 'pipe'] : 'inherit',
91
- shell: process.platform === 'win32',
92
- windowsHide: process.platform === 'win32',
93
- timeout: 30000 // 30 second timeout
94
- };
95
20
 
96
- // For echo command, capture and display output
97
- if (cmd === 'echo' || cmd.startsWith('echo ')) {
98
- const echoCmd = cmd.replace('echo ', '');
99
- const output = execSync(`"${podmanPath}" run --rm ${mounts.join(' ')} -w /workspace sandboxbox:latest echo ${echoCmd}`, {
100
- ...containerOptions,
101
- encoding: 'utf8'
102
- }).trim();
103
- console.log(output);
104
- } else {
105
- // For longer-running commands, use extended timeout
106
- const longRunningOptions = {
107
- ...containerOptions,
108
- timeout: 600000 // 10 minutes for longer operations
109
- };
110
- execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest ${cmd}`, longRunningOptions);
111
- }
21
+ const { sandboxDir, cleanup } = createSandbox(projectDir);
112
22
 
113
- cleanup();
114
- console.log(color('green', '\n✅ Container execution completed! (Isolated - no host changes)'));
115
- return true;
116
- } catch (error) {
117
- retries++;
118
- if (retries < maxRetries && (error.message.includes('Cannot connect to Podman') || error.message.includes('connectex') || error.message.includes('No connection could be made') || error.message.includes('actively refused') || error.message.includes('Command failed'))) {
119
- console.log(color('yellow', ` Backend not ready yet (${retries}/${maxRetries}), initializing machine...`));
23
+ process.on('SIGINT', cleanup);
24
+ process.on('SIGTERM', cleanup);
120
25
 
121
- // Actually initialize the machine instead of just waiting
122
- if (process.platform === 'win32') {
123
- try {
124
- // First try to start existing machine
125
- console.log(color('cyan', ' Starting Podman machine...'));
126
- execSync(`"${podmanPath}" machine start`, {
127
- stdio: 'pipe',
128
- shell: true,
129
- windowsHide: true,
130
- timeout: 60000 // 1 minute for machine start
131
- });
26
+ try {
27
+ const env = createSandboxEnv(sandboxDir, {
28
+ PLAYWRIGHT_BROWSERS_PATH: join(sandboxDir, 'browsers')
29
+ });
132
30
 
133
- console.log(color('green', ' ✅ Podman machine started!'));
134
- // Wait a bit more for machine to be fully ready
135
- console.log(color('cyan', ' Waiting 5 seconds for machine to fully initialize...'));
136
- const readyStart = Date.now();
137
- while (Date.now() - readyStart < 5000) {
138
- // Wait 5 seconds
139
- }
140
- continue; // Try container again
141
- } catch (startError) {
142
- // Machine doesn't exist or failed to start, try initializing it
143
- if (startError.message.includes('does not exist') || startError.message.includes('not found')) {
144
- try {
145
- console.log(color('cyan', ' Initializing new Podman machine...'));
146
- execSync(`"${podmanPath}" machine init --rootful=false`, {
147
- stdio: 'pipe',
148
- shell: true,
149
- windowsHide: true,
150
- timeout: 180000 // 3 minutes for machine init
151
- });
31
+ console.log(color('green', `✅ Sandbox created: ${sandboxDir}`));
32
+ console.log(color('cyan', '📦 Running in isolated environment...\n'));
152
33
 
153
- console.log(color('cyan', ' Starting new Podman machine...'));
154
- execSync(`"${podmanPath}" machine start`, {
155
- stdio: 'pipe',
156
- shell: true,
157
- windowsHide: true,
158
- timeout: 60000 // 1 minute for machine start
159
- });
34
+ await runInSandbox(cmd, [], sandboxDir, env);
160
35
 
161
- console.log(color('green', ' Podman machine ready!'));
162
- continue; // Try container again immediately
163
- } catch (initError) {
164
- console.log(color('red', ` Machine init failed: ${initError.message}`));
165
- }
166
- } else {
167
- console.log(color('red', ` Machine start failed: ${startError.message}`));
168
- }
169
- }
170
- }
171
-
172
- // If machine setup failed or not Windows, wait and retry
173
- console.log(color('yellow', ` Waiting 15 seconds before retry...`));
174
- const start = Date.now();
175
- while (Date.now() - start < 15000) {
176
- // Wait 15 seconds
177
- }
178
- continue;
179
- }
180
- cleanup(); // Cleanup on final failure
181
- console.log(color('red', `\n❌ Run failed: ${error.message}`));
182
- return false;
183
- }
36
+ console.log(color('green', '\nCommand completed!'));
37
+ cleanup();
38
+ return true;
39
+ } catch (error) {
40
+ console.log(color('red', `\n❌ Command failed: ${error.message}`));
41
+ cleanup();
42
+ return false;
184
43
  }
185
44
  }
186
45
 
187
- export function shellCommand(projectDir) {
46
+ export async function shellCommand(projectDir) {
188
47
  if (!existsSync(projectDir)) {
189
48
  console.log(color('red', `❌ Project directory not found: ${projectDir}`));
190
49
  return false;
191
50
  }
192
51
 
193
- console.log(color('blue', '🐚 Starting interactive shell in isolated container...'));
194
- console.log(color('yellow', `Project: ${projectDir}\n`));
195
- console.log(color('cyan', '📦 Note: Changes will NOT affect host files (isolated environment)'));
196
-
197
- const podmanPath = checkPodman();
198
- if (!podmanPath) return false;
199
-
200
- // Start backend setup but don't block on Windows/macOS
201
- const backendReady = setupBackendNonBlocking(podmanPath);
202
- if (process.platform === 'linux' && !backendReady) {
203
- return false; // Only block on Linux for rootless service
204
- }
205
-
206
- // Create isolated environment once (outside retry loop)
207
- const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
208
- setupCleanupHandlers(cleanup);
209
- const mounts = buildContainerMounts(tempProjectDir, projectDir);
210
-
211
- // Retry container operation with backend readiness check
212
- let retries = 0;
213
- const maxRetries = process.platform === 'linux' ? 3 : 12; // More retries for Windows/macOS
214
-
215
- while (retries < maxRetries) {
216
- try {
217
- execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest /bin/bash`, {
218
- stdio: process.platform === 'win32' ? ['pipe', 'pipe', 'pipe'] : 'inherit',
219
- shell: process.platform === 'win32',
220
- windowsHide: process.platform === 'win32',
221
- timeout: 600000 // 10 minutes for interactive shell sessions
222
- });
223
-
224
- cleanup();
225
- return true;
226
- } catch (error) {
227
- retries++;
228
- if (retries < maxRetries && (error.message.includes('Cannot connect to Podman') || error.message.includes('connectex') || error.message.includes('No connection could be made') || error.message.includes('actively refused') || error.message.includes('Command failed'))) {
229
- console.log(color('yellow', ` Backend not ready yet (${retries}/${maxRetries}), initializing machine...`));
230
-
231
- // Actually initialize the machine instead of just waiting
232
- if (process.platform === 'win32') {
233
- try {
234
- // First try to start existing machine
235
- console.log(color('cyan', ' Starting Podman machine...'));
236
- execSync(`"${podmanPath}" machine start`, {
237
- stdio: 'pipe',
238
- shell: true,
239
- windowsHide: true,
240
- timeout: 60000 // 1 minute for machine start
241
- });
242
-
243
- console.log(color('green', ' ✅ Podman machine started!'));
244
- // Wait a bit more for machine to be fully ready
245
- console.log(color('cyan', ' Waiting 5 seconds for machine to fully initialize...'));
246
- const readyStart = Date.now();
247
- while (Date.now() - readyStart < 5000) {
248
- // Wait 5 seconds
249
- }
250
- continue; // Try container again
251
- } catch (startError) {
252
- // Machine doesn't exist or failed to start, try initializing it
253
- if (startError.message.includes('does not exist') || startError.message.includes('not found')) {
254
- try {
255
- console.log(color('cyan', ' Initializing new Podman machine...'));
256
- execSync(`"${podmanPath}" machine init --rootful=false`, {
257
- stdio: 'pipe',
258
- shell: true,
259
- windowsHide: true,
260
- timeout: 180000 // 3 minutes for machine init
261
- });
262
-
263
- console.log(color('cyan', ' Starting new Podman machine...'));
264
- execSync(`"${podmanPath}" machine start`, {
265
- stdio: 'pipe',
266
- shell: true,
267
- windowsHide: true,
268
- timeout: 60000 // 1 minute for machine start
269
- });
270
-
271
- console.log(color('green', ' ✅ Podman machine ready!'));
272
- continue; // Try container again immediately
273
- } catch (initError) {
274
- console.log(color('red', ` Machine init failed: ${initError.message}`));
275
- }
276
- } else {
277
- console.log(color('red', ` Machine start failed: ${startError.message}`));
278
- }
279
- }
280
- }
52
+ console.log(color('blue', '🚀 Starting interactive shell...'));
53
+ return runCommand(projectDir, 'bash');
54
+ }
281
55
 
282
- // If machine setup failed or not Windows, wait and retry
283
- console.log(color('yellow', ` Waiting 15 seconds before retry...`));
284
- const start = Date.now();
285
- while (Date.now() - start < 15000) {
286
- // Wait 15 seconds
287
- }
288
- continue;
289
- }
290
- cleanup(); // Cleanup on final failure
291
- console.log(color('red', `\n❌ Shell failed: ${error.message}`));
292
- return false;
293
- }
294
- }
56
+ export function versionCommand() {
57
+ console.log(color('green', 'SandboxBox - Process Containment Sandbox'));
58
+ console.log(color('cyan', 'Using Node.js process isolation'));
59
+ return true;
295
60
  }
@@ -2,7 +2,6 @@ import { readFileSync } from 'fs';
2
2
  import { resolve, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { color } from '../colors.js';
5
- import { checkPodman } from '../podman.js';
6
5
 
7
6
  const __filename = fileURLToPath(import.meta.url);
8
7
  const __dirname = dirname(__filename);
@@ -14,10 +13,8 @@ export function versionCommand() {
14
13
  try {
15
14
  const packageJson = JSON.parse(readFileSync(resolve(__dirname, '..', '..', 'package.json'), 'utf-8'));
16
15
  console.log(color('green', `SandboxBox v${packageJson.version}`));
17
- console.log(color('cyan', 'Portable containers with Claude Code integration'));
18
- if (checkPodman()) {
19
- console.log('');
20
- }
16
+ console.log(color('cyan', 'Process containment sandbox for CLI tools'));
17
+ console.log(color('yellow', 'Supports: Playwright, Claude Code, and more'));
21
18
  return true;
22
19
  } catch (error) {
23
20
  console.log(color('red', '❌ Could not read version'));