sandboxbox 2.3.4 → 2.3.6

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": "2.3.4",
3
+ "version": "2.3.6",
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",
@@ -16,19 +16,40 @@ export function buildCommand(dockerfilePath) {
16
16
 
17
17
  const podmanPath = checkPodman();
18
18
  if (!podmanPath) return false;
19
- if (!setupBackendNonBlocking(podmanPath)) return false;
20
19
 
21
- try {
22
- execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
23
- stdio: 'inherit',
24
- cwd: dirname(dockerfilePath),
25
- shell: process.platform === 'win32'
26
- });
27
- console.log(color('green', '\n✅ Container built successfully!'));
28
- return true;
29
- } catch (error) {
30
- console.log(color('red', `\n❌ Build failed: ${error.message}`));
31
- return false;
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
+ timeout: 30000 // 30 second timeout
37
+ });
38
+ console.log(color('green', '\n✅ Container built successfully!'));
39
+ return true;
40
+ } catch (error) {
41
+ retries++;
42
+ if (retries < maxRetries && error.message.includes('Cannot connect to Podman')) {
43
+ console.log(color('yellow', ` Backend not ready yet (${retries}/${maxRetries}), waiting 15 seconds...`));
44
+ const start = Date.now();
45
+ while (Date.now() - start < 15000) {
46
+ // Wait 15 seconds
47
+ }
48
+ continue;
49
+ }
50
+ console.log(color('red', `\n❌ Build failed: ${error.message}`));
51
+ return false;
52
+ }
32
53
  }
33
54
  }
34
55
 
@@ -45,7 +66,12 @@ export function runCommand(projectDir, cmd = 'bash') {
45
66
 
46
67
  const podmanPath = checkPodman();
47
68
  if (!podmanPath) return false;
48
- if (!setupBackendNonBlocking(podmanPath)) return false;
69
+
70
+ // Start backend setup but don't block on Windows/macOS
71
+ const backendReady = setupBackendNonBlocking(podmanPath);
72
+ if (process.platform === 'linux' && !backendReady) {
73
+ return false; // Only block on Linux for rootless service
74
+ }
49
75
 
50
76
  try {
51
77
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
@@ -78,7 +104,12 @@ export function shellCommand(projectDir) {
78
104
 
79
105
  const podmanPath = checkPodman();
80
106
  if (!podmanPath) return false;
81
- if (!setupBackendNonBlocking(podmanPath)) return false;
107
+
108
+ // Start backend setup but don't block on Windows/macOS
109
+ const backendReady = setupBackendNonBlocking(podmanPath);
110
+ if (process.platform === 'linux' && !backendReady) {
111
+ return false; // Only block on Linux for rootless service
112
+ }
82
113
 
83
114
  try {
84
115
  const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
package/utils/podman.js CHANGED
@@ -27,153 +27,93 @@ export function getPodmanPath() {
27
27
  }
28
28
 
29
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
-
30
+ if (process.platform === 'linux') {
31
+ // Linux can run true rootless Podman
32
+ const execOptions = { encoding: 'utf-8', stdio: 'pipe', shell: true };
44
33
  try {
45
- // Check existing machines with shorter timeout
46
- let machines = [];
34
+ execSync(`"${podmanPath}" info`, execOptions);
35
+ return true;
36
+ } catch (infoError) {
37
+ console.log(color('yellow', '\n🔧 Starting Podman rootless service...'));
38
+ // Try to start service automatically
47
39
  try {
48
- const machineListOutput = execSync(`"${podmanPath}" machine list --format json`, {
49
- ...execOptions,
50
- timeout: 3000 // 3 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 quickly, checking via info command...'));
56
- // Try to detect if machine exists by checking for specific error messages
57
- try {
58
- execSync(`"${podmanPath}" info`, {
59
- ...execOptions,
60
- timeout: 3000
61
- });
62
- console.log(color('green', ' Backend is already working!'));
63
- return true;
64
- } catch (infoError) {
65
- if (infoError.message.includes('127.0.0.1')) {
66
- console.log(color('cyan', ' Machine exists but not running, proceeding...'));
67
- } else {
68
- console.log(color('yellow', ' Assuming no machines exist'));
69
- }
70
- }
40
+ execSync('podman system service --time=0 &', { stdio: 'ignore', shell: true });
41
+ return true;
42
+ } catch (serviceError) {
43
+ console.log(color('cyan', ' Run: podman system service --time=0 &'));
44
+ return false;
71
45
  }
46
+ }
47
+ }
72
48
 
73
- if (machines.length === 0) {
74
- console.log(color('cyan', ' Creating Podman machine...'));
75
- const initCmd = process.platform === 'win32'
76
- ? `"${podmanPath}" machine init --rootful=false`
77
- : `"${podmanPath}" machine init`;
49
+ // Windows: Implement completely silent automated setup
50
+ if (process.platform === 'win32') {
51
+ const execOptions = { encoding: 'utf-8', stdio: 'pipe', shell: true };
78
52
 
79
- try {
80
- execSync(initCmd, {
81
- stdio: ['pipe', 'pipe', 'pipe'],
82
- shell: true,
83
- timeout: 120000 // 2 minutes
84
- });
85
- } catch (initError) {
86
- const errorOutput = initError.stdout?.toString() + initError.stderr?.toString();
87
- if (errorOutput?.includes('already exists') ||
88
- errorOutput?.includes('VM already exists') ||
89
- initError.message.includes('already exists')) {
90
- console.log(color('cyan', ' Machine already exists, proceeding...'));
91
- } else {
92
- console.log(color('red', ` Error output: ${errorOutput}`));
93
- throw initError;
94
- }
95
- }
96
- } else {
97
- console.log(color('cyan', ' Using existing machine'));
53
+ try {
54
+ execSync(`"${podmanPath}" info`, execOptions);
55
+ return true;
56
+ } catch (infoError) {
57
+ if (!infoError.message.includes('Cannot connect to Podman')) {
58
+ return false;
98
59
  }
99
60
 
100
- console.log(color('cyan', ' Checking machine status...'));
101
- try {
102
- const statusOutput = execSync(`"${podmanPath}" machine list`, {
103
- encoding: 'utf-8',
104
- stdio: 'pipe',
105
- shell: true,
106
- timeout: 3000 // 3 seconds timeout
107
- });
61
+ console.log(color('yellow', '\n🔧 Setting up Podman automatically (silent mode)...'));
108
62
 
109
- if (statusOutput.includes('Running')) {
110
- console.log(color('green', ' Machine is already running'));
111
- } else {
112
- console.log(color('cyan', ' Starting Podman machine...'));
113
- startMachineWithRetry();
114
- }
115
- } catch (statusError) {
116
- console.log(color('cyan', ' Could not check status, attempting to start machine...'));
117
- startMachineWithRetry();
118
- }
63
+ // Start machine setup in background without blocking
64
+ setupMachineBackground(podmanPath);
65
+ return true; // Continue even if setup is in progress
66
+ }
67
+ }
119
68
 
120
- // Give background process time to start, then verify once
121
- console.log(color('cyan', ' Giving background process time to start...'));
122
- const start = Date.now();
123
- while (Date.now() - start < 15000) {
124
- // Wait 15 seconds for background start
125
- }
69
+ // macOS: Similar automated approach
70
+ if (process.platform === 'darwin') {
71
+ const execOptions = { encoding: 'utf-8', stdio: 'pipe', shell: true };
126
72
 
127
- // Quick verification attempt
128
- console.log(color('cyan', ' Verifying backend connection...'));
129
- try {
130
- execSync(`"${podmanPath}" info`, {
131
- ...execOptions,
132
- timeout: 5000 // 5 second timeout
133
- });
134
- console.log(color('green', ' Backend is ready!'));
135
- } catch (verifyError) {
136
- console.log(color('yellow', ' Backend still starting. This is normal for first-time setup.'));
137
- console.log(color('cyan', ' If this persists, run manually:'));
138
- const manualCmd = process.platform === 'win32'
139
- ? `"${podmanPath}" machine init --rootful=false && "${podmanPath}" machine start`
140
- : `"${podmanPath}" machine init && "${podmanPath}" machine start`;
141
- console.log(color('cyan', ` ${manualCmd}`));
142
- console.log(color('yellow', ' Then try sandboxbox again.'));
73
+ try {
74
+ execSync(`"${podmanPath}" info`, execOptions);
75
+ return true;
76
+ } catch (infoError) {
77
+ if (!infoError.message.includes('Cannot connect to Podman')) {
143
78
  return false;
144
79
  }
145
80
 
146
- console.log(color('green', '\n Podman backend setup completed!\n'));
81
+ console.log(color('yellow', '\n🔧 Setting up Podman automatically...'));
82
+ setupMachineBackground(podmanPath);
147
83
  return true;
148
-
149
- function startMachineWithRetry() {
150
- // Use completely silent background start
151
- console.log(color('cyan', ' Starting Podman machine silently in background...'));
152
-
153
- const startProcess = spawn(`"${podmanPath}" machine start`, {
154
- stdio: ['pipe', 'pipe', 'pipe'],
155
- shell: true,
156
- detached: true
157
- });
158
-
159
- startProcess.unref(); // Completely detach from parent
160
- console.log(color('yellow', ' Machine start initiated in background (may take 1-2 minutes)'));
161
- }
162
- } catch (setupError) {
163
- if (setupError.signal === 'SIGTERM') {
164
- console.log(color('red', '\n❌ Setup timed out. Please run manually:'));
165
- } else {
166
- console.log(color('red', `\n❌ Setup failed: ${setupError.message}`));
167
- console.log(color('red', ` Error details: ${setupError.stack}`));
168
- }
169
-
170
- const manualCmd = process.platform === 'win32'
171
- ? `"${podmanPath}" machine init --rootful=false && "${podmanPath}" machine start`
172
- : `"${podmanPath}" machine init && "${podmanPath}" machine start`;
173
- console.log(color('cyan', ` Manual setup: ${manualCmd}`));
174
- return false;
175
84
  }
176
85
  }
86
+
87
+ return false;
88
+ }
89
+
90
+ function setupMachineBackground(podmanPath) {
91
+ console.log(color('cyan', ' Starting machine setup in background...'));
92
+
93
+ const initCmd = process.platform === 'win32'
94
+ ? `"${podmanPath}" machine init --rootful=false`
95
+ : `"${podmanPath}" machine init`;
96
+
97
+ const initProcess = spawn(initCmd, {
98
+ stdio: ['pipe', 'pipe', 'pipe'],
99
+ shell: true,
100
+ detached: true
101
+ });
102
+
103
+ initProcess.unref();
104
+
105
+ // Start machine after init completes (with delay)
106
+ setTimeout(() => {
107
+ const startProcess = spawn(`"${podmanPath}" machine start`, {
108
+ stdio: ['pipe', 'pipe', 'pipe'],
109
+ shell: true,
110
+ detached: true
111
+ });
112
+ startProcess.unref();
113
+ }, 30000); // Wait 30 seconds for init to complete
114
+
115
+ console.log(color('yellow', ' Setup initiated in background (may take 2-3 minutes)'));
116
+ console.log(color('cyan', ' Container operations will work when setup completes\n'));
177
117
  }
178
118
 
179
119
  export function checkBackend(podmanPath) {