sandboxbox 2.0.0 → 2.0.2

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,53 @@ 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
+ let bundledPodman;
73
+
74
+ if (platform === 'win32') {
75
+ bundledPodman = resolve(__dirname, 'bin', 'podman.exe');
76
+ } else if (platform === 'darwin') {
77
+ bundledPodman = resolve(__dirname, 'bin', 'podman');
78
+ } else {
79
+ bundledPodman = resolve(__dirname, 'bin', 'podman-remote-static-linux_amd64');
80
+ }
81
+
82
+ if (existsSync(bundledPodman)) {
83
+ return bundledPodman;
84
+ }
85
+ // Fall back to system podman
86
+ return 'podman';
87
+ }
88
+
69
89
  function checkPodman() {
90
+ const podmanPath = getPodmanPath();
91
+ const isBundled = podmanPath.includes('bin');
92
+
70
93
  try {
71
- const version = execSync('podman --version', { encoding: 'utf-8', stdio: 'pipe' }).trim();
72
- console.log(color('green', `✅ ${version}`));
73
- return true;
94
+ const version = execSync(`"${podmanPath}" --version`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
95
+ console.log(color('green', `✅ ${version}${isBundled ? ' (bundled)' : ''}`));
96
+ return podmanPath;
74
97
  } catch (error) {
75
98
  console.log(color('red', '❌ Podman not found'));
76
- console.log(color('yellow', '\n📦 Please install Podman:'));
99
+ console.log(color('yellow', '\n📦 Podman will be auto-downloaded on first install'));
100
+ console.log(color('yellow', ' Or you can install manually:'));
77
101
  console.log('');
78
102
  if (process.platform === 'win32') {
79
103
  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');
104
+ console.log(' winget install RedHat.Podman');
82
105
  } else if (process.platform === 'darwin') {
83
106
  console.log(color('cyan', ' macOS:'));
84
107
  console.log(' brew install podman');
85
- console.log(' podman machine init');
86
- console.log(' podman machine start');
108
+ console.log(' podman machine init && podman machine start');
87
109
  } else {
88
110
  console.log(color('cyan', ' Linux:'));
89
111
  console.log(' sudo apt-get install podman # Ubuntu/Debian');
90
112
  console.log(' sudo dnf install podman # Fedora');
91
- console.log(' sudo apk add podman # Alpine');
92
113
  }
93
114
  console.log('');
94
- return false;
115
+ return null;
95
116
  }
96
117
  }
97
118
 
@@ -120,11 +141,12 @@ async function main() {
120
141
  console.log(color('blue', '🏗️ Building container...'));
121
142
  console.log(color('yellow', `Dockerfile: ${dockerfilePath}\n`));
122
143
 
123
- if (!checkPodman()) process.exit(1);
144
+ const buildPodman = checkPodman();
145
+ if (!buildPodman) process.exit(1);
124
146
 
125
147
  try {
126
148
  console.log('');
127
- execSync(`podman build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
149
+ execSync(`"${buildPodman}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
128
150
  stdio: 'inherit',
129
151
  cwd: __dirname
130
152
  });
@@ -157,11 +179,12 @@ async function main() {
157
179
  console.log(color('yellow', `Project: ${projectDir}`));
158
180
  console.log(color('yellow', `Command: ${cmd}\n`));
159
181
 
160
- if (!checkPodman()) process.exit(1);
182
+ const runPodman = checkPodman();
183
+ if (!runPodman) process.exit(1);
161
184
 
162
185
  try {
163
186
  console.log('');
164
- execSync(`podman run --rm -it -v "${projectDir}:/workspace" -w /workspace sandboxbox:latest ${cmd}`, {
187
+ execSync(`"${runPodman}" run --rm -it -v "${projectDir}:/workspace" -w /workspace sandboxbox:latest ${cmd}`, {
165
188
  stdio: 'inherit'
166
189
  });
167
190
  console.log('');
@@ -189,11 +212,12 @@ async function main() {
189
212
  console.log(color('blue', '🐚 Starting interactive shell...'));
190
213
  console.log(color('yellow', `Project: ${shellProjectDir}\n`));
191
214
 
192
- if (!checkPodman()) process.exit(1);
215
+ const shellPodman = checkPodman();
216
+ if (!shellPodman) process.exit(1);
193
217
 
194
218
  try {
195
219
  console.log('');
196
- execSync(`podman run --rm -it -v "${shellProjectDir}:/workspace" -w /workspace sandboxbox:latest /bin/bash`, {
220
+ execSync(`"${shellPodman}" run --rm -it -v "${shellProjectDir}:/workspace" -w /workspace sandboxbox:latest /bin/bash`, {
197
221
  stdio: 'inherit'
198
222
  });
199
223
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sandboxbox",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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,145 @@
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
+ const DOWNLOADS = {
23
+ win32: {
24
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-windows_amd64.zip`,
25
+ binary: 'podman.exe',
26
+ extract: 'unzip'
27
+ },
28
+ darwin: {
29
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-darwin_amd64.tar.gz`,
30
+ binary: 'podman',
31
+ extract: 'tar'
32
+ },
33
+ linux: {
34
+ url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-static-linux_amd64.tar.gz`,
35
+ binary: 'podman-remote-static-linux_amd64',
36
+ extract: 'tar'
37
+ }
38
+ };
39
+
40
+ function download(url, dest) {
41
+ return new Promise((resolve, reject) => {
42
+ const get = url.startsWith('https') ? httpsGet : httpGet;
43
+
44
+ get(url, (response) => {
45
+ if (response.statusCode === 302 || response.statusCode === 301) {
46
+ return download(response.headers.location, dest).then(resolve).catch(reject);
47
+ }
48
+
49
+ if (response.statusCode !== 200) {
50
+ reject(new Error(`Download failed: ${response.statusCode}`));
51
+ return;
52
+ }
53
+
54
+ const file = createWriteStream(dest);
55
+ response.pipe(file);
56
+
57
+ file.on('finish', () => {
58
+ file.close();
59
+ resolve();
60
+ });
61
+
62
+ file.on('error', reject);
63
+ }).on('error', reject);
64
+ });
65
+ }
66
+
67
+ async function main() {
68
+ const platform = process.platform;
69
+
70
+ console.log(`\n📦 Setting up Podman portable binaries for ${platform}...`);
71
+
72
+ if (!DOWNLOADS[platform]) {
73
+ console.log(`⚠️ Platform ${platform} not supported for auto-download`);
74
+ console.log(` Skipping auto-download (will use system Podman if available)`);
75
+ return;
76
+ }
77
+
78
+ // Create bin directory
79
+ if (!existsSync(binDir)) {
80
+ mkdirSync(binDir, { recursive: true });
81
+ }
82
+
83
+ const { url, binary, extract } = DOWNLOADS[platform];
84
+ const binaryPath = join(binDir, binary);
85
+
86
+ // Check if already downloaded
87
+ if (existsSync(binaryPath)) {
88
+ console.log(`✅ Podman already installed at ${binaryPath}`);
89
+ return;
90
+ }
91
+
92
+ console.log(`📥 Downloading Podman remote v${PODMAN_VERSION}...`);
93
+
94
+ const archiveName = url.split('/').pop();
95
+ const archivePath = join(binDir, archiveName);
96
+
97
+ try {
98
+ // Download archive
99
+ console.log(` Downloading from GitHub releases...`);
100
+ await download(url, archivePath);
101
+ console.log(`✅ Downloaded successfully`);
102
+
103
+ // Extract based on platform
104
+ console.log(`📦 Extracting...`);
105
+ if (extract === 'tar') {
106
+ execSync(`tar -xzf "${archivePath}" -C "${binDir}" --strip-components=1`, {
107
+ stdio: 'pipe'
108
+ });
109
+ } else if (extract === 'unzip') {
110
+ execSync(`unzip -q "${archivePath}" -d "${binDir}"`, {
111
+ stdio: 'pipe'
112
+ });
113
+ }
114
+
115
+ // Make executable on Unix
116
+ if (platform !== 'win32' && existsSync(binaryPath)) {
117
+ chmodSync(binaryPath, 0o755);
118
+ }
119
+
120
+ console.log(`✅ Podman remote installed successfully!`);
121
+ console.log(` Binary: ${binaryPath}\n`);
122
+
123
+ // Clean up archive
124
+ if (existsSync(archivePath)) {
125
+ unlinkSync(archivePath);
126
+ }
127
+
128
+ } catch (error) {
129
+ console.error(`⚠️ Auto-download failed: ${error.message}`);
130
+ console.log(`\n💡 No problem! You can install Podman manually:`);
131
+ if (platform === 'win32') {
132
+ console.log(` winget install RedHat.Podman`);
133
+ } else if (platform === 'darwin') {
134
+ console.log(` brew install podman && podman machine init && podman machine start`);
135
+ } else {
136
+ console.log(` sudo apt-get install podman # Ubuntu/Debian`);
137
+ }
138
+ console.log(`\n Or it will use system Podman if installed.\n`);
139
+ // Don't fail the npm install
140
+ }
141
+ }
142
+
143
+ main().catch(() => {
144
+ // Silently fail - will use system Podman
145
+ });