sandboxbox 2.0.1 → 2.0.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/README.md CHANGED
@@ -6,16 +6,25 @@ Run your projects in isolated containers using Podman. Works on **Windows, macOS
6
6
 
7
7
  ## Installation
8
8
 
9
- No installation required! Use with `npx`:
9
+ No installation required! **Podman binaries auto-download** on first use:
10
10
 
11
11
  ```bash
12
12
  npx sandboxbox build
13
13
  npx sandboxbox run ./my-project
14
14
  ```
15
15
 
16
- ## Quick Start
16
+ ### Auto-Download Feature
17
+
18
+ SandboxBox automatically downloads portable Podman binaries when you run it:
19
+ - ✅ **Windows** - Downloads podman.exe (v4.9.3)
20
+ - ✅ **macOS** - Downloads podman remote client
21
+ - ✅ **Linux** - Downloads static podman binary
22
+
23
+ Just like sqlite or Playwright, no manual installation needed!
17
24
 
18
- ### 1. Install Podman (One-time)
25
+ ### Manual Installation (Optional)
26
+
27
+ If you prefer to install Podman system-wide:
19
28
 
20
29
  **Windows:**
21
30
  ```powershell
@@ -36,7 +45,9 @@ sudo dnf install podman # Fedora
36
45
  sudo apk add podman # Alpine
37
46
  ```
38
47
 
39
- ### 2. Build Container
48
+ ## Quick Start
49
+
50
+ ### 1. Build Container
40
51
 
41
52
  ```bash
42
53
  npx sandboxbox build
@@ -48,7 +59,7 @@ This builds a container with:
48
59
  - Playwright with all browser dependencies
49
60
  - Git, npm, and essential build tools
50
61
 
51
- ### 3. Run Your Project
62
+ ### 2. Run Your Project
52
63
 
53
64
  ```bash
54
65
  # Run with default shell
@@ -64,7 +75,7 @@ npx sandboxbox run ./my-project "claude --help"
64
75
  npx sandboxbox run ./my-project "npx playwright test"
65
76
  ```
66
77
 
67
- ### 4. Interactive Shell
78
+ ### 3. Interactive Shell
68
79
 
69
80
  ```bash
70
81
  npx sandboxbox shell ./my-project
package/bin/.gitkeep ADDED
@@ -0,0 +1 @@
1
+ # Downloaded Podman binaries will go here
package/cli.js CHANGED
@@ -66,32 +66,54 @@ function showHelp() {
66
66
  console.log(color('magenta', '🚀 Fast startup • True isolation • Cross-platform'));
67
67
  }
68
68
 
69
+ function getPodmanPath() {
70
+ // Check for bundled podman first
71
+ const platform = process.platform;
72
+ const arch = process.arch === 'arm64' ? 'arm64' : 'amd64';
73
+ let bundledPodman;
74
+
75
+ if (platform === 'win32') {
76
+ bundledPodman = resolve(__dirname, 'bin', 'podman.exe');
77
+ } else if (platform === 'darwin') {
78
+ bundledPodman = resolve(__dirname, 'bin', 'podman');
79
+ } else {
80
+ bundledPodman = resolve(__dirname, 'bin', `podman-remote-static-linux_${arch}`);
81
+ }
82
+
83
+ if (existsSync(bundledPodman)) {
84
+ return bundledPodman;
85
+ }
86
+ // Fall back to system podman
87
+ return 'podman';
88
+ }
89
+
69
90
  function checkPodman() {
91
+ const podmanPath = getPodmanPath();
92
+ const isBundled = podmanPath.includes('bin');
93
+
70
94
  try {
71
- const version = execSync('podman --version', { encoding: 'utf-8', stdio: 'pipe' }).trim();
72
- console.log(color('green', `✅ ${version}`));
73
- return true;
95
+ const version = execSync(`"${podmanPath}" --version`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
96
+ console.log(color('green', `✅ ${version}${isBundled ? ' (bundled)' : ''}`));
97
+ return podmanPath;
74
98
  } catch (error) {
75
99
  console.log(color('red', '❌ Podman not found'));
76
- console.log(color('yellow', '\n📦 Please install Podman:'));
100
+ console.log(color('yellow', '\n📦 Podman will be auto-downloaded on first install'));
101
+ console.log(color('yellow', ' Or you can install manually:'));
77
102
  console.log('');
78
103
  if (process.platform === 'win32') {
79
104
  console.log(color('cyan', ' Windows:'));
80
- console.log(' 1. Download from https://podman.io/getting-started/installation');
81
- console.log(' 2. Or use: winget install RedHat.Podman');
105
+ console.log(' winget install RedHat.Podman');
82
106
  } else if (process.platform === 'darwin') {
83
107
  console.log(color('cyan', ' macOS:'));
84
108
  console.log(' brew install podman');
85
- console.log(' podman machine init');
86
- console.log(' podman machine start');
109
+ console.log(' podman machine init && podman machine start');
87
110
  } else {
88
111
  console.log(color('cyan', ' Linux:'));
89
112
  console.log(' sudo apt-get install podman # Ubuntu/Debian');
90
113
  console.log(' sudo dnf install podman # Fedora');
91
- console.log(' sudo apk add podman # Alpine');
92
114
  }
93
115
  console.log('');
94
- return false;
116
+ return null;
95
117
  }
96
118
  }
97
119
 
@@ -120,11 +142,12 @@ async function main() {
120
142
  console.log(color('blue', '🏗️ Building container...'));
121
143
  console.log(color('yellow', `Dockerfile: ${dockerfilePath}\n`));
122
144
 
123
- if (!checkPodman()) process.exit(1);
145
+ const buildPodman = checkPodman();
146
+ if (!buildPodman) process.exit(1);
124
147
 
125
148
  try {
126
149
  console.log('');
127
- execSync(`podman build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
150
+ execSync(`"${buildPodman}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
128
151
  stdio: 'inherit',
129
152
  cwd: __dirname
130
153
  });
@@ -157,11 +180,12 @@ async function main() {
157
180
  console.log(color('yellow', `Project: ${projectDir}`));
158
181
  console.log(color('yellow', `Command: ${cmd}\n`));
159
182
 
160
- if (!checkPodman()) process.exit(1);
183
+ const runPodman = checkPodman();
184
+ if (!runPodman) process.exit(1);
161
185
 
162
186
  try {
163
187
  console.log('');
164
- execSync(`podman run --rm -it -v "${projectDir}:/workspace" -w /workspace sandboxbox:latest ${cmd}`, {
188
+ execSync(`"${runPodman}" run --rm -it -v "${projectDir}:/workspace" -w /workspace sandboxbox:latest ${cmd}`, {
165
189
  stdio: 'inherit'
166
190
  });
167
191
  console.log('');
@@ -189,11 +213,12 @@ async function main() {
189
213
  console.log(color('blue', '🐚 Starting interactive shell...'));
190
214
  console.log(color('yellow', `Project: ${shellProjectDir}\n`));
191
215
 
192
- if (!checkPodman()) process.exit(1);
216
+ const shellPodman = checkPodman();
217
+ if (!shellPodman) process.exit(1);
193
218
 
194
219
  try {
195
220
  console.log('');
196
- execSync(`podman run --rm -it -v "${shellProjectDir}:/workspace" -w /workspace sandboxbox:latest /bin/bash`, {
221
+ execSync(`"${shellPodman}" run --rm -it -v "${shellProjectDir}:/workspace" -w /workspace sandboxbox:latest /bin/bash`, {
197
222
  stdio: 'inherit'
198
223
  });
199
224
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "2.0.1",
3
+ "version": "2.0.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",
@@ -8,7 +8,8 @@
8
8
  "sandboxbox": "./cli.js"
9
9
  },
10
10
  "scripts": {
11
- "start": "node cli.js"
11
+ "start": "node cli.js",
12
+ "postinstall": "node scripts/download-podman.js"
12
13
  },
13
14
  "keywords": [
14
15
  "containers",
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Auto-download Podman portable binaries
5
+ * Similar to how sqlite/playwright auto-downloads platform-specific binaries
6
+ */
7
+
8
+ import { createWriteStream, existsSync, mkdirSync, chmodSync, unlinkSync } from 'fs';
9
+ import { get as httpsGet } from 'https';
10
+ import { get as httpGet } from 'http';
11
+ import { join, dirname } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { execSync } from 'child_process';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+ const binDir = join(__dirname, '..', 'bin');
18
+
19
+ // Podman remote client versions
20
+ const PODMAN_VERSION = '4.9.3';
21
+
22
+ // Get architecture
23
+ const ARCH = process.arch === 'arm64' ? 'arm64' : 'amd64';
24
+
25
+ const DOWNLOADS = {
26
+ win32: {
27
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-windows_amd64.zip`,
28
+ binary: 'podman.exe',
29
+ extract: 'unzip'
30
+ },
31
+ darwin: {
32
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-darwin_${ARCH}.tar.gz`,
33
+ binary: 'podman',
34
+ extract: 'tar'
35
+ },
36
+ linux: {
37
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-static-linux_${ARCH}.tar.gz`,
38
+ binary: `podman-remote-static-linux_${ARCH}`,
39
+ extract: 'tar'
40
+ }
41
+ };
42
+
43
+ function download(url, dest) {
44
+ return new Promise((resolve, reject) => {
45
+ const get = url.startsWith('https') ? httpsGet : httpGet;
46
+
47
+ get(url, (response) => {
48
+ if (response.statusCode === 302 || response.statusCode === 301) {
49
+ return download(response.headers.location, dest).then(resolve).catch(reject);
50
+ }
51
+
52
+ if (response.statusCode !== 200) {
53
+ reject(new Error(`Download failed: ${response.statusCode}`));
54
+ return;
55
+ }
56
+
57
+ const file = createWriteStream(dest);
58
+ response.pipe(file);
59
+
60
+ file.on('finish', () => {
61
+ file.close();
62
+ resolve();
63
+ });
64
+
65
+ file.on('error', reject);
66
+ }).on('error', reject);
67
+ });
68
+ }
69
+
70
+ async function main() {
71
+ const platform = process.platform;
72
+
73
+ console.log(`\n📦 Setting up Podman portable binaries for ${platform}...`);
74
+
75
+ if (!DOWNLOADS[platform]) {
76
+ console.log(`⚠️ Platform ${platform} not supported for auto-download`);
77
+ console.log(` Skipping auto-download (will use system Podman if available)`);
78
+ return;
79
+ }
80
+
81
+ // Create bin directory
82
+ if (!existsSync(binDir)) {
83
+ mkdirSync(binDir, { recursive: true });
84
+ }
85
+
86
+ const { url, binary, extract } = DOWNLOADS[platform];
87
+ const binaryPath = join(binDir, binary);
88
+
89
+ // Check if already downloaded
90
+ if (existsSync(binaryPath)) {
91
+ console.log(`✅ Podman already installed at ${binaryPath}`);
92
+ return;
93
+ }
94
+
95
+ console.log(`📥 Downloading Podman remote v${PODMAN_VERSION}...`);
96
+
97
+ const archiveName = url.split('/').pop();
98
+ const archivePath = join(binDir, archiveName);
99
+
100
+ try {
101
+ // Download archive
102
+ console.log(` Downloading from GitHub releases...`);
103
+ await download(url, archivePath);
104
+ console.log(`✅ Downloaded successfully`);
105
+
106
+ // Extract based on platform
107
+ console.log(`📦 Extracting...`);
108
+ if (extract === 'tar') {
109
+ execSync(`tar -xzf "${archivePath}" -C "${binDir}" --strip-components=1`, {
110
+ stdio: 'pipe'
111
+ });
112
+ } else if (extract === 'unzip') {
113
+ execSync(`unzip -q "${archivePath}" -d "${binDir}"`, {
114
+ stdio: 'pipe'
115
+ });
116
+ }
117
+
118
+ // Make executable on Unix
119
+ if (platform !== 'win32' && existsSync(binaryPath)) {
120
+ chmodSync(binaryPath, 0o755);
121
+ }
122
+
123
+ console.log(`✅ Podman remote installed successfully!`);
124
+ console.log(` Binary: ${binaryPath}\n`);
125
+
126
+ // Clean up archive
127
+ if (existsSync(archivePath)) {
128
+ unlinkSync(archivePath);
129
+ }
130
+
131
+ } catch (error) {
132
+ console.error(`⚠️ Auto-download failed: ${error.message}`);
133
+ console.log(`\n💡 No problem! You can install Podman manually:`);
134
+ if (platform === 'win32') {
135
+ console.log(` winget install RedHat.Podman`);
136
+ } else if (platform === 'darwin') {
137
+ console.log(` brew install podman && podman machine init && podman machine start`);
138
+ } else {
139
+ console.log(` sudo apt-get install podman # Ubuntu/Debian`);
140
+ }
141
+ console.log(`\n Or it will use system Podman if installed.\n`);
142
+ // Don't fail the npm install
143
+ }
144
+ }
145
+
146
+ main().catch(() => {
147
+ // Silently fail - will use system Podman
148
+ });