sandboxbox 2.3.1 → 2.3.3

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,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * SandboxBox CLI - Portable Container Runner with Podman
@@ -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
- async function main() {
13
+ function main() {
14
14
  const args = process.argv.slice(2);
15
15
  showBanner();
16
16
 
@@ -71,10 +71,4 @@ async function main() {
71
71
  }
72
72
  }
73
73
 
74
- main().catch(error => {
75
- console.error(color('red', 'āŒ SandboxBox failed:'));
76
- console.error(color('red', error.message));
77
- console.error('');
78
- console.error(color('yellow', 'šŸ’” Try: npx sandboxbox --help'));
79
- process.exit(1);
80
- });
74
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "Portable container runner with Podman - Claude Code & Playwright support. Works on Windows, macOS, and Linux.",
5
5
  "type": "module",
6
6
  "main": "cli.js",
@@ -3,7 +3,7 @@ import { execSync } from 'child_process';
3
3
  import { resolve, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { color } from '../colors.js';
6
- import { checkPodman, checkBackend, getPodmanPath } from '../podman.js';
6
+ import { checkPodman, setupBackendNonBlocking, getPodmanPath } from '../podman.js';
7
7
  import { buildClaudeContainerCommand, createClaudeDockerfile } from '../claude-workspace.js';
8
8
  import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
9
9
 
@@ -42,7 +42,7 @@ export function claudeCommand(projectDir, command = 'claude') {
42
42
 
43
43
  const buildPodman = checkPodman();
44
44
  if (!buildPodman) return false;
45
- if (!checkBackend(buildPodman)) return false;
45
+ if (!setupBackendNonBlocking(buildPodman)) return false;
46
46
 
47
47
  try {
48
48
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
@@ -73,7 +73,7 @@ function buildClaudeContainer() {
73
73
 
74
74
  const podmanPath = checkPodman();
75
75
  if (!podmanPath) return false;
76
- if (!checkBackend(podmanPath)) return false;
76
+ if (!setupBackendNonBlocking(podmanPath)) return false;
77
77
 
78
78
  try {
79
79
  execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox-local:latest .`, {
@@ -2,7 +2,7 @@ import { existsSync } from 'fs';
2
2
  import { execSync } from 'child_process';
3
3
  import { dirname } from 'path';
4
4
  import { color } from '../colors.js';
5
- import { checkPodman, checkBackend } from '../podman.js';
5
+ import { checkPodman, setupBackendNonBlocking } from '../podman.js';
6
6
  import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
7
7
 
8
8
  export function buildCommand(dockerfilePath) {
@@ -16,7 +16,7 @@ export function buildCommand(dockerfilePath) {
16
16
 
17
17
  const podmanPath = checkPodman();
18
18
  if (!podmanPath) return false;
19
- if (!checkBackend(podmanPath)) return false;
19
+ if (!setupBackendNonBlocking(podmanPath)) return false;
20
20
 
21
21
  try {
22
22
  execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
@@ -45,7 +45,7 @@ export function runCommand(projectDir, cmd = 'bash') {
45
45
 
46
46
  const podmanPath = checkPodman();
47
47
  if (!podmanPath) return false;
48
- if (!checkBackend(podmanPath)) return false;
48
+ if (!setupBackendNonBlocking(podmanPath)) return false;
49
49
 
50
50
  try {
51
51
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
@@ -78,7 +78,7 @@ export function shellCommand(projectDir) {
78
78
 
79
79
  const podmanPath = checkPodman();
80
80
  if (!podmanPath) return false;
81
- if (!checkBackend(podmanPath)) return false;
81
+ if (!setupBackendNonBlocking(podmanPath)) return false;
82
82
 
83
83
  try {
84
84
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
package/utils/podman.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from 'fs';
2
- import { execSync } from 'child_process';
2
+ import { execSync, spawn } from 'child_process';
3
3
  import { resolve, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { color } from './colors.js';
@@ -26,6 +26,119 @@ export function getPodmanPath() {
26
26
  return 'podman';
27
27
  }
28
28
 
29
+ export function setupBackendNonBlocking(podmanPath) {
30
+ if (process.platform === 'linux') return true;
31
+
32
+ const execOptions = { encoding: 'utf-8', stdio: 'pipe', shell: true };
33
+
34
+ try {
35
+ execSync(`"${podmanPath}" info`, execOptions);
36
+ return true;
37
+ } catch (infoError) {
38
+ if (!infoError.message.includes('Cannot connect to Podman')) {
39
+ return false;
40
+ }
41
+
42
+ console.log(color('yellow', '\nšŸ”§ Initializing Podman backend (one-time setup, takes 2-3 minutes)...'));
43
+
44
+ try {
45
+ // Check existing machines with timeout
46
+ let machines = [];
47
+ try {
48
+ const machineListOutput = execSync(`"${podmanPath}" machine list --format json`, {
49
+ ...execOptions,
50
+ timeout: 10000 // 10 seconds timeout
51
+ });
52
+ machines = JSON.parse(machineListOutput || '[]');
53
+ console.log(color('cyan', ` Found ${machines.length} existing machine(s)`));
54
+ } catch (machineListError) {
55
+ console.log(color('yellow', ' Could not list machines, assuming no machines exist'));
56
+ }
57
+
58
+ if (machines.length === 0) {
59
+ console.log(color('cyan', ' Creating Podman machine...'));
60
+ const initCmd = process.platform === 'win32'
61
+ ? `"${podmanPath}" machine init --rootful=false`
62
+ : `"${podmanPath}" machine init`;
63
+
64
+ try {
65
+ execSync(initCmd, {
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ shell: true,
68
+ timeout: 120000 // 2 minutes
69
+ });
70
+ } catch (initError) {
71
+ const errorOutput = initError.stdout?.toString() + initError.stderr?.toString();
72
+ if (errorOutput?.includes('already exists') ||
73
+ errorOutput?.includes('VM already exists') ||
74
+ initError.message.includes('already exists')) {
75
+ console.log(color('cyan', ' Machine already exists, proceeding...'));
76
+ } else {
77
+ console.log(color('red', ` Error output: ${errorOutput}`));
78
+ throw initError;
79
+ }
80
+ }
81
+ } else {
82
+ console.log(color('cyan', ' Using existing machine'));
83
+ }
84
+
85
+ console.log(color('cyan', ' Checking machine status...'));
86
+ try {
87
+ const statusOutput = execSync(`"${podmanPath}" machine list`, {
88
+ encoding: 'utf-8',
89
+ stdio: 'pipe',
90
+ shell: true,
91
+ timeout: 10000
92
+ });
93
+
94
+ if (statusOutput.includes('Running')) {
95
+ console.log(color('green', ' Machine is already running'));
96
+ } else {
97
+ console.log(color('cyan', ' Starting Podman machine...'));
98
+ execSync(`"${podmanPath}" machine start`, {
99
+ stdio: 'inherit',
100
+ shell: true,
101
+ timeout: 180000 // 3 minutes
102
+ });
103
+ }
104
+ } catch (statusError) {
105
+ console.log(color('cyan', ' Could not check status, attempting to start machine...'));
106
+ execSync(`"${podmanPath}" machine start`, {
107
+ stdio: 'inherit',
108
+ shell: true,
109
+ timeout: 180000 // 3 minutes
110
+ });
111
+ }
112
+
113
+ // Verify backend is actually working
114
+ console.log(color('cyan', ' Verifying backend connection...'));
115
+ try {
116
+ execSync(`"${podmanPath}" info`, execOptions);
117
+ } catch (verifyError) {
118
+ console.log(color('red', ` Backend verification failed: ${verifyError.message}`));
119
+ console.log(color('yellow', ' Please ensure Podman machine is running manually'));
120
+ return false;
121
+ }
122
+
123
+ console.log(color('green', '\nāœ… Podman backend setup completed!\n'));
124
+ return true;
125
+ } catch (setupError) {
126
+ if (setupError.signal === 'SIGTERM') {
127
+ console.log(color('red', '\nāŒ Setup timed out. Please run manually:'));
128
+ } else {
129
+ console.log(color('red', `\nāŒ Setup failed: ${setupError.message}`));
130
+ console.log(color('red', ` Error details: ${setupError.stack}`));
131
+ }
132
+
133
+ const manualCmd = process.platform === 'win32'
134
+ ? `"${podmanPath}" machine init --rootful=false && "${podmanPath}" machine start`
135
+ : `"${podmanPath}" machine init && "${podmanPath}" machine start`;
136
+ console.log(color('cyan', ` Manual setup: ${manualCmd}`));
137
+ return false;
138
+ }
139
+ }
140
+ }
141
+
29
142
  export function checkBackend(podmanPath) {
30
143
  if (process.platform === 'linux') return true;
31
144