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 +17 -6
- package/bin/.gitkeep +1 -0
- package/cli.js +41 -16
- package/package.json +3 -2
- package/scripts/download-podman.js +148 -0
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!
|
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
|
-
|
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
|
-
###
|
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
|
-
|
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
|
-
###
|
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
|
-
###
|
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(
|
72
|
-
console.log(color('green', `✅ ${version}`));
|
73
|
-
return
|
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📦
|
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('
|
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
|
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
|
-
|
145
|
+
const buildPodman = checkPodman();
|
146
|
+
if (!buildPodman) process.exit(1);
|
124
147
|
|
125
148
|
try {
|
126
149
|
console.log('');
|
127
|
-
execSync(`
|
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
|
-
|
183
|
+
const runPodman = checkPodman();
|
184
|
+
if (!runPodman) process.exit(1);
|
161
185
|
|
162
186
|
try {
|
163
187
|
console.log('');
|
164
|
-
execSync(`
|
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
|
-
|
216
|
+
const shellPodman = checkPodman();
|
217
|
+
if (!shellPodman) process.exit(1);
|
193
218
|
|
194
219
|
try {
|
195
220
|
console.log('');
|
196
|
-
execSync(`
|
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.
|
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
|
+
});
|